{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1ff02ddc",
   "metadata": {},
   "source": [
    "# 20-05 · Прыгающий мяч\n",
    "\n",
    "Практика к разделу [«Мини-проект: прыгающий мяч»](../../site/chapters/glava-20/20-05-mini-proekt-myach-itogi.html). Тот же код, что и в `projects/pygame/bouncing-ball/bouncing_ball_basic.py` — это первый, учебный прототип. Пересобранную Pro-версию (`bouncing_ball.py`) мы разберём в разделе 20.33."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "979b3b3e",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Собрать и протестировать мяч, отскакивающий от стен."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3ceb478",
   "metadata": {},
   "source": [
    "## Про игровой цикл в этом ноутбуке\n",
    "\n",
    "В обычном `.py`-файле игровой цикл — это `while rabotaet:`, где `rabotaet` становится `False`, когда пользователь закрывает окно. В автоматически выполняемом ноутбуке некому закрыть окно, поэтому здесь мы прогоняем фиксированное число кадров через `for _ in range(N):` — логика каждого отдельного кадра (движение, отрисовка, `clock.tick()`) при этом точно такая же, как в настоящей игре."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8732de5c",
   "metadata": {},
   "source": [
    "## Полный мини-проект"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1055e5cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:11:59.872464Z",
     "iopub.status.busy": "2026-09-03T10:11:59.872314Z",
     "iopub.status.idle": "2026-09-03T10:12:03.276936Z",
     "shell.execute_reply": "2026-09-03T10:12:03.274707Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pygame-ce 2.5.8 (SDL 2.32.10, Python 3.14.6)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Кадров прогнано: 200\n",
      "Отскоков от стен: 3\n",
      "Финальная позиция: (68, 68)\n"
     ]
    }
   ],
   "source": [
    "import pygame\n",
    "\n",
    "SHIRINA, VYSOTA = 600, 400\n",
    "RADIUS = 20\n",
    "\n",
    "pygame.init()\n",
    "screen = pygame.display.set_mode((SHIRINA, VYSOTA))\n",
    "clock = pygame.time.Clock()\n",
    "\n",
    "x, y = SHIRINA // 2, VYSOTA // 2\n",
    "dx, dy = 4, 3\n",
    "\n",
    "otskokov = 0\n",
    "\n",
    "for kadr in range(200):\n",
    "    for event in pygame.event.get():\n",
    "        pass\n",
    "\n",
    "    x += dx\n",
    "    y += dy\n",
    "\n",
    "    if x - RADIUS < 0 or x + RADIUS > SHIRINA:\n",
    "        dx = -dx\n",
    "        otskokov += 1\n",
    "    if y - RADIUS < 0 or y + RADIUS > VYSOTA:\n",
    "        dy = -dy\n",
    "        otskokov += 1\n",
    "\n",
    "    screen.fill((20, 20, 40))\n",
    "    pygame.draw.circle(screen, (255, 100, 100), (int(x), int(y)), RADIUS)\n",
    "    pygame.display.flip()\n",
    "    clock.tick(60)\n",
    "\n",
    "print(\"Кадров прогнано: 200\")\n",
    "print(\"Отскоков от стен:\", otskokov)\n",
    "print(\"Финальная позиция:\", (x, y))\n",
    "pygame.quit()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d51a6794",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4ce3e53e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:12:03.279573Z",
     "iopub.status.busy": "2026-09-03T10:12:03.279232Z",
     "iopub.status.idle": "2026-09-03T10:12:03.282890Z",
     "shell.execute_reply": "2026-09-03T10:12:03.282442Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Верно: мяч отскочил от стен 3 раз(а) за 200 кадров.\n"
     ]
    }
   ],
   "source": [
    "assert otskokov > 0\n",
    "print(f\"Верно: мяч отскочил от стен {otskokov} раз(а) за 200 кадров.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68a1f45b",
   "metadata": {},
   "source": [
    "## Задание ★★ Самостоятельная задача\n",
    "\n",
    "Проверьте, что мяч всегда остаётся в границах экрана на каждом кадре."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0c3acbe1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:12:03.284684Z",
     "iopub.status.busy": "2026-09-03T10:12:03.284494Z",
     "iopub.status.idle": "2026-09-03T10:12:03.421566Z",
     "shell.execute_reply": "2026-09-03T10:12:03.420238Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Мяч когда-либо вышел за границы: False\n",
      "Верно: мяч всегда оставался в пределах экрана.\n"
     ]
    }
   ],
   "source": [
    "pygame.init()\n",
    "screen = pygame.display.set_mode((SHIRINA, VYSOTA))\n",
    "clock = pygame.time.Clock()\n",
    "\n",
    "x, y = SHIRINA // 2, VYSOTA // 2\n",
    "dx, dy = 7, 5  # быстрее — легче случайно \"пробить\" стену при ошибке в коде\n",
    "\n",
    "vyshel_za_granicy = False\n",
    "for kadr in range(300):\n",
    "    x += dx\n",
    "    y += dy\n",
    "    if x - RADIUS < 0 or x + RADIUS > SHIRINA:\n",
    "        dx = -dx\n",
    "        x = max(RADIUS, min(x, SHIRINA - RADIUS))\n",
    "    if y - RADIUS < 0 or y + RADIUS > VYSOTA:\n",
    "        dy = -dy\n",
    "        y = max(RADIUS, min(y, VYSOTA - RADIUS))\n",
    "\n",
    "    if not (RADIUS <= x <= SHIRINA - RADIUS and RADIUS <= y <= VYSOTA - RADIUS):\n",
    "        vyshel_za_granicy = True\n",
    "\n",
    "print(\"Мяч когда-либо вышел за границы:\", vyshel_za_granicy)\n",
    "assert vyshel_za_granicy is False\n",
    "print(\"Верно: мяч всегда оставался в пределах экрана.\")\n",
    "pygame.quit()"
   ]
  }
 ],
 "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
}
