{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a097ff8e",
   "metadata": {},
   "source": [
    "# 21-06 · Уничтожаем врагов и корабль\n",
    "\n",
    "Практика к разделам [«Уничтожаем врагов»](../../site/chapters/glava-21/21-06-unichtozhenie.html) и [«Перерисовываем врагов. Игра окончена!»](../../site/chapters/glava-21/21-07-game-over.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1938aa6",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Проверить, что попадание пули уничтожает врага и начисляет очки, а враг, долетевший до низа экрана, завершает игру."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4808676",
   "metadata": {},
   "source": [
    "## Рабочий пример — попадание пули по врагу"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "16c0cd97",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:34.718976Z",
     "iopub.status.busy": "2026-08-14T20:59:34.718847Z",
     "iopub.status.idle": "2026-08-14T20:59:34.863906Z",
     "shell.execute_reply": "2026-08-14T20:59:34.863389Z"
    }
   },
   "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": "41074f58",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:34.865050Z",
     "iopub.status.busy": "2026-08-14T20:59:34.864873Z",
     "iopub.status.idle": "2026-08-14T20:59:34.867904Z",
     "shell.execute_reply": "2026-08-14T20:59:34.867518Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Счёт: 10\n",
      "Врагов осталось: 0\n"
     ]
    }
   ],
   "source": [
    "state = novaya_igra()\n",
    "\n",
    "# ставим врага прямо перед носом корабля и стреляем\n",
    "vrag = pygame.Rect(state[\"korabl\"].centerx - 20, state[\"korabl\"].top - 30, VRAG_SHIRINA, VRAG_VYSOTA)\n",
    "state[\"vragi\"] = [vrag]\n",
    "vystrelit(state)\n",
    "\n",
    "for kadr in range(10):\n",
    "    obnovit_igru(state)\n",
    "    if state[\"schet\"] > 0:\n",
    "        break\n",
    "\n",
    "print(\"Счёт:\", state[\"schet\"])\n",
    "print(\"Врагов осталось:\", len(state[\"vragi\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7807b6f9",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "89061aab",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:34.868915Z",
     "iopub.status.busy": "2026-08-14T20:59:34.868802Z",
     "iopub.status.idle": "2026-08-14T20:59:34.871338Z",
     "shell.execute_reply": "2026-08-14T20:59:34.870862Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Верно: враг уничтожен пулей, счёт увеличен на 10.\n"
     ]
    }
   ],
   "source": [
    "assert state[\"schet\"] == 10, \"попадание должно добавить 10 очков\"\n",
    "assert len(state[\"vragi\"]) == 0, \"уничтоженный враг должен исчезнуть из списка\"\n",
    "print(\"Верно: враг уничтожен пулей, счёт увеличен на 10.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4c136f1",
   "metadata": {},
   "source": [
    "## Эксперимент — враг долетает до низа экрана"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6c6f0470",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:34.872239Z",
     "iopub.status.busy": "2026-08-14T20:59:34.872137Z",
     "iopub.status.idle": "2026-08-14T20:59:34.877672Z",
     "shell.execute_reply": "2026-08-14T20:59:34.877140Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Игра окончена: True\n",
      "Верно: враг, долетевший до низа экрана, завершает игру.\n"
     ]
    }
   ],
   "source": [
    "state2 = novaya_igra()\n",
    "state2[\"vragi\"] = [pygame.Rect(100, VYSOTA - VRAG_VYSOTA - 1, VRAG_SHIRINA, VRAG_VYSOTA)]\n",
    "\n",
    "obnovit_igru(state2)\n",
    "narisovat(state2)\n",
    "\n",
    "print(\"Игра окончена:\", state2[\"igra_okonchena\"])\n",
    "assert state2[\"igra_okonchena\"] is True\n",
    "print(\"Верно: враг, долетевший до низа экрана, завершает игру.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c0b9e97",
   "metadata": {},
   "source": [
    "## Эксперимент — обновление игры останавливается после конца игры"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0d9714ef",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-14T20:59:34.878714Z",
     "iopub.status.busy": "2026-08-14T20:59:34.878596Z",
     "iopub.status.idle": "2026-08-14T20:59:34.881435Z",
     "shell.execute_reply": "2026-08-14T20:59:34.880947Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Верно: после игра окончена состояние больше не меняется.\n"
     ]
    }
   ],
   "source": [
    "schet_do = state2[\"schet\"]\n",
    "vragov_do = len(state2[\"vragi\"])\n",
    "\n",
    "for kadr in range(20):\n",
    "    obnovit_igru(state2)\n",
    "\n",
    "assert state2[\"schet\"] == schet_do\n",
    "assert len(state2[\"vragi\"]) == vragov_do\n",
    "print(\"Верно: после игра окончена состояние больше не меняется.\")"
   ]
  }
 ],
 "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
}
