{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ffff2a04",
   "metadata": {},
   "source": [
    "# 21-04 · Strzelanie\n",
    "\n",
    "Praktyka do rozdziału [„Strzelanie: tworzymy i poruszamy kule”](/pl/chapters/rozdzial-21/21-04-strelba.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1010d87",
   "metadata": {},
   "source": [
    "## Cel\n",
    "\n",
    "Stwórz kulę na dziobie statku i upewnij się, że poleci w górę."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5695f7fe",
   "metadata": {},
   "source": [
    "## Sprawa robocza"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0344c62",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "import pygame\n",
    "\n",
    "SHIRINA, VYSOTA = 480, 720\n",
    "FPS = 60\n",
    "\n",
    "KORABL_SHIRINA, KORABL_VYSOTA = 44, 44\n",
    "KORABL_SKOROST = 260.0   # px/s\n",
    "\n",
    "PULYA_SHIRINA, PULYA_VYSOTA = 6, 18\n",
    "PULYA_SKOROST = 560.0   # px/s\n",
    "\n",
    "VRAG_SHIRINA, VRAG_VYSOTA = 32, 28\n",
    "VRAG_SKOROST = 150.0                # px/s\n",
    "INTERVAL_POYAVLENIYA_VRAGA = 0.75   # секунд между новыми врагами\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",
    "    korabl = pygame.Rect(\n",
    "        SHIRINA // 2 - KORABL_SHIRINA // 2,\n",
    "        VYSOTA - KORABL_VYSOTA - 20,\n",
    "        KORABL_SHIRINA,\n",
    "        KORABL_VYSOTA,\n",
    "    )\n",
    "    return {\n",
    "        \"korabl\": korabl,\n",
    "        \"korabl_x\": float(korabl.x),\n",
    "        \"puli\": [],\n",
    "        \"vragi\": [],\n",
    "        \"schet\": 0,\n",
    "        \"vremya_do_vraga\": INTERVAL_POYAVLENIYA_VRAGA,\n",
    "        \"igra_okonchena\": False,\n",
    "    }\n",
    "\n",
    "\n",
    "def obrabotat_klavishi(state, klavishi, dt):\n",
    "    napravlenie = klavishi[pygame.K_RIGHT] - klavishi[pygame.K_LEFT]\n",
    "    korabl_x = state[\"korabl_x\"] + napravlenie * KORABL_SKOROST * dt\n",
    "    state[\"korabl_x\"] = max(0.0, min(korabl_x, SHIRINA - KORABL_SHIRINA))\n",
    "    state[\"korabl\"].x = round(state[\"korabl_x\"])\n",
    "\n",
    "\n",
    "def vystrelit(state):\n",
    "    korabl = state[\"korabl\"]\n",
    "    pulya_rect = pygame.Rect(\n",
    "        korabl.centerx - PULYA_SHIRINA // 2,\n",
    "        korabl.top,\n",
    "        PULYA_SHIRINA,\n",
    "        PULYA_VYSOTA,\n",
    "    )\n",
    "    state[\"puli\"].append({\"rect\": pulya_rect, \"y\": float(pulya_rect.y)})\n",
    "\n",
    "\n",
    "def sozdat_vraga():\n",
    "    x = random.randint(0, SHIRINA - VRAG_SHIRINA)\n",
    "    return {\"rect\": pygame.Rect(x, -VRAG_VYSOTA, VRAG_SHIRINA, VRAG_VYSOTA), \"y\": float(-VRAG_VYSOTA)}\n",
    "\n",
    "\n",
    "def obnovit_igru(state, dt):\n",
    "    if state[\"igra_okonchena\"]:\n",
    "        return\n",
    "\n",
    "    for pulya in state[\"puli\"]:\n",
    "        pulya[\"y\"] -= PULYA_SKOROST * dt\n",
    "        pulya[\"rect\"].y = round(pulya[\"y\"])\n",
    "    state[\"puli\"] = [p for p in state[\"puli\"] if p[\"rect\"].bottom > 0]\n",
    "\n",
    "    state[\"vremya_do_vraga\"] -= dt\n",
    "    if state[\"vremya_do_vraga\"] <= 0.0:\n",
    "        state[\"vragi\"].append(sozdat_vraga())\n",
    "        state[\"vremya_do_vraga\"] += INTERVAL_POYAVLENIYA_VRAGA\n",
    "\n",
    "    for vrag in state[\"vragi\"]:\n",
    "        vrag[\"y\"] += VRAG_SKOROST * dt\n",
    "        vrag[\"rect\"].y = round(vrag[\"y\"])\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[\"rect\"].colliderect(vrag[\"rect\"]):\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[\"rect\"].bottom >= VYSOTA or vrag[\"rect\"].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[\"rect\"])\n",
    "    for vrag in state[\"vragi\"]:\n",
    "        pygame.draw.rect(screen, KRASNYJ, vrag[\"rect\"])\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": null,
   "id": "0c3843f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "state = novaya_igra()\n",
    "vystrelit(state)\n",
    "\n",
    "print(\"Пуль после выстрела:\", len(state[\"puli\"]))\n",
    "print(\"Позиция пули:\", state[\"puli\"][0][\"rect\"])\n",
    "print(\"Позиция корабля:\", state[\"korabl\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaec78e1",
   "metadata": {},
   "source": [
    "## Sprawdzenie wyniku"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9f400357",
   "metadata": {},
   "outputs": [],
   "source": [
    "pulya = state[\"puli\"][0][\"rect\"]\n",
    "assert pulya.centerx == state[\"korabl\"].centerx\n",
    "assert pulya.top == state[\"korabl\"].top\n",
    "print(\"Верно: пуля появилась ровно у носа корабля.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd439d24",
   "metadata": {},
   "source": [
    "## Eksperyment – kula leci z ekranu i znika"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e50d1309",
   "metadata": {},
   "outputs": [],
   "source": [
    "y_do = state[\"puli\"][0][\"rect\"].y\n",
    "dt = 1 / FPS\n",
    "for kadr in range(200):\n",
    "    obnovit_igru(state, dt)\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": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
