{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "40a02dce",
   "metadata": {},
   "source": [
    "# 17-20 · Architektura TicTacToeApp\n",
    "\n",
    "Praktyka do sekcji [„Architektura TicTacToeApp”"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a924160",
   "metadata": {},
   "source": [
    "## Cel\n",
    "\n",
    "Zbieraj graf obiektowy o minimalnym app.root/app.state i upewnij się, że domena i UI są oddzielone."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "deca78a9",
   "metadata": {},
   "source": [
    "## Pro mainloop() w tym laptopie\n",
    "\n",
    "Zamiast `root.mainloop()` używany tutaj `root.update()` + `root.destroy()` jest takie, że aplikacja jest zbudowana dokładnie tak samo, tylko bez niekończącego się oczekiwania na końcu (zobacz wyjaśnienie w rozdziale 16)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f488775",
   "metadata": {},
   "source": [
    "## Sprawa robocza"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c40315a3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:13:05.858528Z",
     "iopub.status.busy": "2026-09-03T10:13:05.858352Z",
     "iopub.status.idle": "2026-09-03T10:13:05.961499Z",
     "shell.execute_reply": "2026-09-03T10:13:05.960983Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "app.root — это Tk: True\n",
      "app.state — это GameState: True\n"
     ]
    }
   ],
   "source": [
    "import tkinter as tk\n",
    "from dataclasses import dataclass, field\n",
    "\n",
    "@dataclass\n",
    "class GameState:\n",
    "    board: list[str] = field(default_factory=lambda: [\"\"] * 9)\n",
    "    current_player: str = \"X\"\n",
    "\n",
    "class TicTacToeApp:\n",
    "    def __init__(self, root):\n",
    "        self.root = root          # app HAS-A root — не наследование от tk.Tk\n",
    "        self.state = GameState()  # app HAS-A state\n",
    "        self.buttons = []\n",
    "\n",
    "root = tk.Tk()\n",
    "app = TicTacToeApp(root)\n",
    "print(\"app.root — это Tk:\", isinstance(app.root, tk.Tk))\n",
    "print(\"app.state — это GameState:\", isinstance(app.state, GameState))\n",
    "root.update()\n",
    "root.destroy()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8552d51c",
   "metadata": {},
   "source": [
    "## Zadanie niezależne od zadania ★★\n",
    "\n",
    "Dodaj metodę build_board(self), aby utworzyć dziewięć tk.Button i upewnij się, że len(self.buttons) == 9 po wywołaniu go."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9db4a0f7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-03T10:13:05.963270Z",
     "iopub.status.busy": "2026-09-03T10:13:05.963131Z",
     "iopub.status.idle": "2026-09-03T10:13:06.052896Z",
     "shell.execute_reply": "2026-09-03T10:13:06.052045Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Клеток в app.buttons: 9\n",
      "app.buttons — виджеты, app.state — состояние. Это разные вещи.\n"
     ]
    }
   ],
   "source": [
    "class TicTacToeApp:\n",
    "    def __init__(self, root):\n",
    "        self.root = root\n",
    "        self.state = GameState()\n",
    "        self.buttons = []\n",
    "\n",
    "    def build_board(self):\n",
    "        for index in range(9):\n",
    "            btn = tk.Button(self.root, text=\"\", width=3, height=1)\n",
    "            btn.grid(row=index // 3, column=index % 3)\n",
    "            self.buttons.append(btn)\n",
    "\n",
    "root = tk.Tk()\n",
    "app = TicTacToeApp(root)\n",
    "app.build_board()\n",
    "root.update()\n",
    "\n",
    "print(\"Клеток в app.buttons:\", len(app.buttons))\n",
    "assert len(app.buttons) == 9\n",
    "\n",
    "# Widgety są tworzone — ale model nadal jest pusty. Oto rozdział:\n",
    "# app.buttons jest to, co widzimy; app.state jest to, co gra faktycznie pamięta.\n",
    "assert app.state.board == [\"\"] * 9\n",
    "print(\"app.buttons — виджеты, app.state — состояние. Это разные вещи.\")\n",
    "root.destroy()"
   ]
  }
 ],
 "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
}
