{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51b48688",
   "metadata": {},
   "source": [
    "# 20-21 · Столкновения и хитбоксы\n",
    "\n",
    "Практика к разделу [«Столкновения и хитбоксы»](../../site/chapters/glava-20/20-21-stolknoveniya.html). AABB-проверка пересечения — чистая функция, как и в тексте раздела."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a300364",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Реализовать и проверить проверку пересечения двух прямоугольников."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed5937e5",
   "metadata": {},
   "source": [
    "## Рабочий пример"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e803748c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:12:17.654021Z",
     "iopub.status.busy": "2026-09-03T10:12:17.653873Z",
     "iopub.status.idle": "2026-09-03T10:12:17.686561Z",
     "shell.execute_reply": "2026-09-03T10:12:17.685942Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Столкновение с близким врагом: True\n",
      "Столкновение с далёким врагом: False\n"
     ]
    }
   ],
   "source": [
    "def pryamougolniki_peresekayutsya(a, b):\n",
    "    ax1, ay1, aw, ah = a\n",
    "    bx1, by1, bw, bh = b\n",
    "    ax2, ay2 = ax1 + aw, ay1 + ah\n",
    "    bx2, by2 = bx1 + bw, by1 + bh\n",
    "    return ax1 < bx2 and ax2 > bx1 and ay1 < by2 and ay2 > by1\n",
    "\n",
    "igrok = (100, 100, 40, 40)\n",
    "vrag_blizko = (120, 120, 40, 40)\n",
    "vrag_daleko = (400, 400, 40, 40)\n",
    "\n",
    "print(\"Столкновение с близким врагом:\", pryamougolniki_peresekayutsya(igrok, vrag_blizko))\n",
    "print(\"Столкновение с далёким врагом:\", pryamougolniki_peresekayutsya(igrok, vrag_daleko))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7aad4631",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5a3b85db",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:12:17.687856Z",
     "iopub.status.busy": "2026-09-03T10:12:17.687685Z",
     "iopub.status.idle": "2026-09-03T10:12:17.691869Z",
     "shell.execute_reply": "2026-09-03T10:12:17.691228Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Верно: пересечение находится правильно, включая пограничный случай соприкосновения краями.\n"
     ]
    }
   ],
   "source": [
    "assert pryamougolniki_peresekayutsya(igrok, vrag_blizko) is True\n",
    "assert pryamougolniki_peresekayutsya(igrok, vrag_daleko) is False\n",
    "# соприкосновение ровно краем — НЕ считается пересечением\n",
    "assert pryamougolniki_peresekayutsya((0, 0, 40, 40), (40, 0, 40, 40)) is False\n",
    "print(\"Верно: пересечение находится правильно, включая пограничный случай соприкосновения краями.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cbf53b2",
   "metadata": {},
   "source": [
    "## Задание ★★ Самостоятельная задача\n",
    "\n",
    "Уменьшите хитбокс игрока на 10 пикселей с каждой стороны (то есть на 20 по ширине и высоте суммарно — как `rect.inflate(-20, -20)` из раздела 20.21) и посмотрите, на что это на самом деле влияет.\n",
    "\n",
    "Проверьте на двух врагах: на `vrag_blizko`, который налезает на игрока глубоко, и на «краевом» враге `vrag_kraem = (135, 135, 40, 40)`, который задевает только угол картинки. Убедитесь, что уменьшение хитбокса меняет ответ именно для краевого врага, а настоящее, глубокое столкновение остаётся столкновением."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b557e3f8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:12:17.693065Z",
     "iopub.status.busy": "2026-09-03T10:12:17.692925Z",
     "iopub.status.idle": "2026-09-03T10:12:17.697025Z",
     "shell.execute_reply": "2026-09-03T10:12:17.696327Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Глубокое перекрытие: до True -> после True\n",
      "Касание углом:       до True -> после False\n",
      "Верно: уменьшение хитбокса убирает попадания по краю, не трогая настоящие столкновения.\n"
     ]
    }
   ],
   "source": [
    "def szhat(rect, na):\n",
    "    x, y, w, h = rect\n",
    "    return (x + na, y + na, w - 2 * na, h - 2 * na)\n",
    "\n",
    "igrok_szhatyj = szhat(igrok, 10)\n",
    "assert igrok_szhatyj == (110, 110, 20, 20)\n",
    "\n",
    "vrag_kraem = (135, 135, 40, 40)   # задевает только угол картинки игрока\n",
    "\n",
    "blizko_do = pryamougolniki_peresekayutsya(igrok, vrag_blizko)\n",
    "blizko_posle = pryamougolniki_peresekayutsya(igrok_szhatyj, vrag_blizko)\n",
    "kraem_do = pryamougolniki_peresekayutsya(igrok, vrag_kraem)\n",
    "kraem_posle = pryamougolniki_peresekayutsya(igrok_szhatyj, vrag_kraem)\n",
    "\n",
    "print(\"Глубокое перекрытие: до\", blizko_do, \"-> после\", blizko_posle)\n",
    "print(\"Касание углом:       до\", kraem_do, \"-> после\", kraem_posle)\n",
    "\n",
    "# Настоящее столкновение остаётся столкновением...\n",
    "assert blizko_do is True\n",
    "assert blizko_posle is True\n",
    "# ...а \"нечестное\" попадание по прозрачному углу картинки исчезает:\n",
    "assert kraem_do is True\n",
    "assert kraem_posle is False\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
}
