{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b23b101c",
   "metadata": {},
   "source": [
    "# 21-04 · Стреляем\n",
    "\n",
    "Практика к разделу [«Стреляем»](../../site/chapters/glava-21/21-04-strelba.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c0372dd",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Создать пулю у носа корабля и убедиться, что она улетает вверх."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20deb681",
   "metadata": {},
   "source": [
    "## Рабочий пример"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f9762091",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:33.834991Z",
     "iopub.status.busy": "2026-08-14T20:59:33.834868Z",
     "iopub.status.idle": "2026-08-14T20:59:33.977018Z",
     "shell.execute_reply": "2026-08-14T20:59:33.976562Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pygame-ce 2.5.8 (SDL 2.32.10, Python 3.14.6)\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "import pygame\n",
    "\n",
    "SHIRINA, VYSOTA = 500, 600\n",
    "FPS = 60\n",
    "\n",
    "KORABL_SHIRINA, KORABL_VYSOTA = 50, 40\n",
    "KORABL_SKOROST = 6\n",
    "\n",
    "PULYA_SHIRINA, PULYA_VYSOTA = 4, 12\n",
    "PULYA_SKOROST = 9\n",
    "\n",
    "VRAG_SHIRINA, VRAG_VYSOTA = 40, 30\n",
    "VRAG_SKOROST = 2\n",
    "INTERVAL_POYAVLENIYA_VRAGA = 45\n",
    "\n",
    "BELYJ = (255, 255, 255)\n",
    "CHERNYJ = (10, 10, 20)\n",
    "ZELYONYJ = (80, 220, 120)\n",
    "KRASNYJ = (230, 60, 60)\n",
    "ZHYOLTYJ = (240, 220, 80)\n",
    "\n",
    "pygame.init()\n",
    "screen = pygame.display.set_mode((SHIRINA, VYSOTA))\n",
    "pygame.display.set_caption(\"Космический шутер\")\n",
    "clock = pygame.time.Clock()\n",
    "shrift = pygame.font.SysFont(None, 32)\n",
    "shrift_bolshoj = pygame.font.SysFont(None, 64)\n",
    "\n",
    "\n",
    "def novaya_igra():\n",
    "    return {\n",
    "        \"korabl\": pygame.Rect(\n",
    "            SHIRINA // 2 - KORABL_SHIRINA // 2,\n",
    "            VYSOTA - KORABL_VYSOTA - 20,\n",
    "            KORABL_SHIRINA,\n",
    "            KORABL_VYSOTA,\n",
    "        ),\n",
    "        \"puli\": [],\n",
    "        \"vragi\": [],\n",
    "        \"schet\": 0,\n",
    "        \"kadrov_do_vraga\": INTERVAL_POYAVLENIYA_VRAGA,\n",
    "        \"igra_okonchena\": False,\n",
    "    }\n",
    "\n",
    "\n",
    "def obrabotat_klavishi(state, klavishi):\n",
    "    korabl = state[\"korabl\"]\n",
    "    if klavishi[pygame.K_LEFT]:\n",
    "        korabl.x -= KORABL_SKOROST\n",
    "    if klavishi[pygame.K_RIGHT]:\n",
    "        korabl.x += KORABL_SKOROST\n",
    "    korabl.x = max(0, min(korabl.x, SHIRINA - KORABL_SHIRINA))\n",
    "\n",
    "\n",
    "def vystrelit(state):\n",
    "    korabl = state[\"korabl\"]\n",
    "    pulya = pygame.Rect(\n",
    "        korabl.centerx - PULYA_SHIRINA // 2,\n",
    "        korabl.top,\n",
    "        PULYA_SHIRINA,\n",
    "        PULYA_VYSOTA,\n",
    "    )\n",
    "    state[\"puli\"].append(pulya)\n",
    "\n",
    "\n",
    "def sozdat_vraga():\n",
    "    x = random.randint(0, SHIRINA - VRAG_SHIRINA)\n",
    "    return pygame.Rect(x, -VRAG_VYSOTA, VRAG_SHIRINA, VRAG_VYSOTA)\n",
    "\n",
    "\n",
    "def obnovit_igru(state):\n",
    "    if state[\"igra_okonchena\"]:\n",
    "        return\n",
    "\n",
    "    for pulya in state[\"puli\"]:\n",
    "        pulya.y -= PULYA_SKOROST\n",
    "    state[\"puli\"] = [p for p in state[\"puli\"] if p.bottom > 0]\n",
    "\n",
    "    state[\"kadrov_do_vraga\"] -= 1\n",
    "    if state[\"kadrov_do_vraga\"] <= 0:\n",
    "        state[\"vragi\"].append(sozdat_vraga())\n",
    "        state[\"kadrov_do_vraga\"] = INTERVAL_POYAVLENIYA_VRAGA\n",
    "\n",
    "    for vrag in state[\"vragi\"]:\n",
    "        vrag.y += VRAG_SKOROST\n",
    "\n",
    "    novye_puli = []\n",
    "    novye_vragi = list(state[\"vragi\"])\n",
    "    for pulya in state[\"puli\"]:\n",
    "        popala = False\n",
    "        for vrag in list(novye_vragi):\n",
    "            if pulya.colliderect(vrag):\n",
    "                novye_vragi.remove(vrag)\n",
    "                state[\"schet\"] += 10\n",
    "                popala = True\n",
    "                break\n",
    "        if not popala:\n",
    "            novye_puli.append(pulya)\n",
    "    state[\"puli\"] = novye_puli\n",
    "    state[\"vragi\"] = novye_vragi\n",
    "\n",
    "    for vrag in state[\"vragi\"]:\n",
    "        if vrag.bottom >= VYSOTA or vrag.colliderect(state[\"korabl\"]):\n",
    "            state[\"igra_okonchena\"] = True\n",
    "            break\n",
    "\n",
    "\n",
    "def narisovat(state):\n",
    "    screen.fill(CHERNYJ)\n",
    "    pygame.draw.rect(screen, ZELYONYJ, state[\"korabl\"])\n",
    "    for pulya in state[\"puli\"]:\n",
    "        pygame.draw.rect(screen, ZHYOLTYJ, pulya)\n",
    "    for vrag in state[\"vragi\"]:\n",
    "        pygame.draw.rect(screen, KRASNYJ, vrag)\n",
    "\n",
    "    tablo = shrift.render(f\"Счёт: {state['schet']}\", True, BELYJ)\n",
    "    screen.blit(tablo, (10, 10))\n",
    "\n",
    "    if state[\"igra_okonchena\"]:\n",
    "        nadpis = shrift_bolshoj.render(\"ИГРА ОКОНЧЕНА\", True, BELYJ)\n",
    "        rect = nadpis.get_rect(center=(SHIRINA // 2, VYSOTA // 2))\n",
    "        screen.blit(nadpis, rect)\n",
    "\n",
    "    pygame.display.flip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6b825062",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:33.978111Z",
     "iopub.status.busy": "2026-08-14T20:59:33.977929Z",
     "iopub.status.idle": "2026-08-14T20:59:33.980671Z",
     "shell.execute_reply": "2026-08-14T20:59:33.980214Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Пуль после выстрела: 1\n",
      "Позиция пули: Rect(248, 540, 4, 12)\n",
      "Позиция корабля: Rect(225, 540, 50, 40)\n"
     ]
    }
   ],
   "source": [
    "state = novaya_igra()\n",
    "vystrelit(state)\n",
    "\n",
    "print(\"Пуль после выстрела:\", len(state[\"puli\"]))\n",
    "print(\"Позиция пули:\", state[\"puli\"][0])\n",
    "print(\"Позиция корабля:\", state[\"korabl\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50eead7d",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0dad6204",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:33.981616Z",
     "iopub.status.busy": "2026-08-14T20:59:33.981503Z",
     "iopub.status.idle": "2026-08-14T20:59:33.983976Z",
     "shell.execute_reply": "2026-08-14T20:59:33.983538Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Верно: пуля появилась ровно у носа корабля.\n"
     ]
    }
   ],
   "source": [
    "pulya = state[\"puli\"][0]\n",
    "assert pulya.centerx == state[\"korabl\"].centerx\n",
    "assert pulya.top == state[\"korabl\"].top\n",
    "print(\"Верно: пуля появилась ровно у носа корабля.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e5bc855",
   "metadata": {},
   "source": [
    "## Эксперимент — пуля улетает за экран и исчезает"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "296b38d2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:33.985013Z",
     "iopub.status.busy": "2026-08-14T20:59:33.984892Z",
     "iopub.status.idle": "2026-08-14T20:59:33.987834Z",
     "shell.execute_reply": "2026-08-14T20:59:33.987172Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Пуль осталось: 0\n",
      "Верно: пуля улетела с 540 за пределы экрана и была удалена из списка.\n"
     ]
    }
   ],
   "source": [
    "y_do = state[\"puli\"][0].y\n",
    "for kadr in range(200):\n",
    "    obnovit_igru(state)\n",
    "\n",
    "print(\"Пуль осталось:\", len(state[\"puli\"]))\n",
    "assert len(state[\"puli\"]) == 0, \"пуля должна улететь за верхний край экрана и исчезнуть\"\n",
    "print(f\"Верно: пуля улетела с {y_do} за пределы экрана и была удалена из списка.\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
