{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7044cd72",
   "metadata": {},
   "source": [
    "# 21-25 · Финальная версия игры\n",
    "\n",
    "Практика к разделу [«Собираем финальную версию игры»](../../site/chapters/glava-21/21-25-finalnaya-arhitektura.html). Полный файл — `projects/pygame/space-shooter/space_shooter.py`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cd4fd22",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Запустить настоящий класс Game из финальной игры, прогнать несколько секунд симуляции и убедиться, что счёт, жизни и объекты ведут себя как единое целое."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ce349cd",
   "metadata": {},
   "source": [
    "## Про игровой цикл в этом ноутбуке\n",
    "\n",
    "В обычном `.py`-файле игровой цикл — это `while rabotaet:`, зависящий от событий пользователя (закрытие окна, нажатия клавиш). В автоматически выполняемом ноутбуке некому создавать такие события, поэтому здесь мы прогоняем фиксированное число кадров через `for kadr in range(N):` — логика каждого отдельного кадра (движение, столкновения, отрисовка) при этом точно такая же, как в настоящей игре из `projects/pygame/space-shooter/space_shooter.py`. Скорость по-прежнему задана в пикселях в секунду (px/s), а не в пикселях за кадр — но чтобы результат ноутбука не зависел от того, насколько быстро выполняется код на конкретном компьютере, `dt` здесь берётся не из `clock.tick()`, а фиксируется заранее как `1 / FPS`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7511c46c",
   "metadata": {},
   "source": [
    "## Рабочий пример"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14dcf561",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "import sys\n",
    "\n",
    "sys.path.insert(0, \"../../projects/pygame/space-shooter\")\n",
    "\n",
    "import space_shooter as ss\n",
    "\n",
    "game = ss.Game(rng=random.Random(11))\n",
    "game.start_new_game()\n",
    "\n",
    "for _ in range(180):   # 3 симулированные секунды\n",
    "    game.update(1 / 60)\n",
    "    game.render()\n",
    "\n",
    "print(\"Состояние:\", game.state)\n",
    "print(\"Счёт:\", game.score)\n",
    "print(\"Врагов на поле:\", len(game.enemies))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f15ddbdc",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e0738d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert game.state in (ss.GameStatus.PLAYING, ss.GameStatus.GAME_OVER)\n",
    "assert game.score >= 0\n",
    "assert game.lives >= 0\n",
    "print(\"Верно: финальная игра проходит несколько секунд симуляции без ошибок.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75b55c95",
   "metadata": {},
   "source": [
    "## Задание ★★ Самостоятельная задача\n",
    "\n",
    "Вручную создайте столкновение пули с врагом через `game.resolve_bullet_enemy_collisions()` и убедитесь, что счёт увеличивается ровно на стоимость одного врага."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0c33576",
   "metadata": {},
   "outputs": [],
   "source": [
    "tochka = (game.player.rect.centerx, 300.0)\n",
    "vrag = ss.Enemy(game.assets.images[\"enemy_scout\"], tochka, points=100, speed=0.0)\n",
    "game.enemies.add(vrag)\n",
    "pulya = ss.Bullet(game.assets.images[\"bullet\"], tochka)\n",
    "game.bullets.add(pulya)\n",
    "\n",
    "schet_do = game.score\n",
    "game.score += game.resolve_bullet_enemy_collisions()\n",
    "\n",
    "assert game.score == schet_do + 100\n",
    "print(\"Верно: столкновение пули и врага добавило ровно 100 очков.\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
