Двогравцева шахова гра, написана на C# та WPF (.NET Framework), з повною реалізацією правил шахів, графічним інтерфейсом та таймером відліку часу для кожного гравця.
- Клонуй репозиторій:
git clone https://github.com/Bohdan-Melnychuk/Final_project_KPZ.git
- Відкрий
kursach wpf .framework.slnу Visual Studio 2022 - Встанови
kursach wpf .frameworkяк стартовий проєкт - Натисни F5 для збірки та запуску
Вимоги: Visual Studio 2022, .NET Framework 4.7.2
- Стартовий екран — запуск гри або вихід з діалогом підтвердження (start.xaml.cs)
- Шахова дошка — відмальована на WPF Canvas з кольоровими клітинками та декоративною рамкою (Board.cs, рядки 187–215)
- Маркери ходів — точки для доступних ходів, іконки атаки для захоплення ворожих фігур, червона підсвітка при шаху королю (Board.cs, рядки 256–295)
- Діалог перетворення пішака — інтерактивне вікно вибору фігури: Ферзь, Тура, Слон або Кінь (ChoosingFigure.xaml.cs, рядки 29–31)
- Екран результату — відображає переможця або нічию з кнопкою повернення в меню (Page1.xaml.cs)
- Усі 6 типів фігур з правильними правилами ходів: Король, Ферзь, Тура, Слон, Кінь, Пішак
- Виявлення шаху — усі ходи що залишають Короля під шахом заблоковані (Figure.cs, рядки 93–95)
- Виявлення мату та пату (Board.cs, рядки 473–476)
- Рокіровка — коротка та довга (King.cs, рядки 155–188)
- Перетворення пішака — асинхронний діалог при досягненні останньої горизонталі (Pawn.cs, рядки 126–147)
- Умови нічиї — недостатній матеріал (≤3 фігури) та правило 50 ходів (Board.cs, рядки 539–554)
- Кожен гравець має 5-хвилинний таймер зворотнього відліку (Board.cs, рядки 40–87)
- Таймери перемикаються автоматично після кожного ходу (Board.cs, рядки 127–140)
- Закінчення часу викликає миттєву перемогу суперника (Board.cs, рядки 95–125)
kursach wpf .framework/
├── Figure.cs # Абстрактний базовий клас для всіх шахових фігур
├── Board.cs # Дошка, логіка ходів, таймер, стан гри
├── Cell.cs # Представлення клітинки дошки
├── King.cs # Рух короля, рокіровка, уникнення шаху
├── Queen.cs # Рух ферзя та захисна логіка
├── Rook.cs # Рух тури
├── Bishop.cs # Рух слона
├── Knight.cs # Рух коня
├── Pawn.cs # Рух пішака, перетворення
├── MainWindow.xaml(.cs) # Головне вікно гри
├── start.xaml(.cs) # Стартовий екран
├── Page1.xaml(.cs) # Екран результату гри
├── ChoosingFigure.xaml.cs # Діалог перетворення пішака
└── Image/ # PNG іконки для всіх шахових фігур
1. Template Method — Figure.cs
Абстрактний клас Figure визначає скелет алгоритму валідації ходу у методі CanMoveTo() (рядки 81–97):
- Перевірка меж дошки
- Перевірка зіткнення зі своєю фігурою
- Виклик абстрактного
CanReach()(рядок 99) — перевизначається у кожному підкласі - Симуляція ходу через
WouldKingBeInCheck()(рядки 104–117) — перевіряє чи Король залишився б під шахом
Абстрактні методи що зобов'язані реалізувати всі підкласи оголошені на рядках 28–30. Підкласи перевизначають лише потрібні кроки, не змінюючи загальний алгоритм.
Кожна фігура реалізує власну стратегію руху у MoveFigure(), перемикаючись між двома алгоритмами залежно від стану гри:
StandardMoves()(Queen.cs, рядки 68–79) — звичайні ходи коли Король не під шахомDefensiveMoves()(Queen.cs, рядки 38–66) — захисні ходи коли Король під шахом
Перемикання між стратегіями відбувається через перевірку IsKingInCheck() (Queen.cs, рядок 30). Board викликає MoveFigure() однаково для всіх фігур (Board.cs, рядок 475), не знаючи конкретної стратегії кожної фігури.
3. Composite — Board.cs
Board керує всією колекцією Figure та взаємодіє з ними однаково через абстрактний клас:
GetAllFigures()(рядки 426–454) — повертає плаский список усіх фігур для однорідної ітераціїRefreshAllAttackCell()(рядки 363–375) — викликаєFillArrayAttacksCell()на кожній фігурі без знання їхніх конкретних типівIsSquareUnderAttack()(рядки 378–390) — викликаєIsAttacking()на всіх фігурах поліморфно
Кожен клас має одну причину для зміни:
Figure— лише дані фігури та валідація ходу (Figure.cs, рядки 17–184)Board— лише стан дошки, рендеринг та ігровий процес (Board.cs)ChoosingFigure— лише UI перетворення пішака (ChoosingFigure.xaml.cs, рядки 29–93)Page1— лише відображення результату гри (Page1.xaml.cs)
Figure відкритий для розширення, але закритий для модифікації. Абстрактні методи IsAttacking(), MoveFigure(), FillArrayAttacksCell() (рядки 28–30) дозволяють додати нову фігуру лише через створення нового підкласу — без жодних змін у Board або існуючих фігурах.
Всі підкласи Figure можна використовувати там де очікується Figure. Board зберігає всі фігури у масиві ArrFigure[,] (рядок 13) типу Figure[,] та отримує їх через GetAllFigures() (рядок 426) — працюючи з ними поліморфно через IsAttacking() та MoveFigure() без перевірки конкретного типу.
Спільна логіка сканування напрямків винесена у базовий клас і не дублюється у жодній фігурі:
ScanDiagonal()(рядки 161–172) — спільний для Ферзя і СлонаScanStraight()(рядки 175–184) — спільний для Ферзя і ТуриCheckCell()(рядки 49–79) — централізована перевірка меж та зіткнень для всіх фігур
WouldKingBeInCheck() (рядки 104–117) симулює хід на дошці, одразу перевіряє чи Король опиниться під шахом і скасовує симуляцію. Якщо так — хід негайно відхиляється у CanMoveTo() (рядок 93) ще до відображення маркера гравцю.
Ігрова логіка повністю відокремлена від UI. Figure та підкласи містять лише правила шахів. Board відповідає за стан гри та рендеринг маркерів на Canvas. MainWindow, Page1, start — лише UI без жодної ігрової логіки. Таймер інкапсульований у Board через InitializeTimers() (рядки 40–87).
Довга логіка ходів розбита на приватні методи у кожній фігурі:
StandardMoves()таDefensiveMoves()(Queen.cs, рядки 38–79)- Спільне сканування винесено у
ScanDiagonal()таScanStraight()(Figure.cs, рядки 161–184)
Спільні поля (Color, X, Y, ImageFigure, CellsUnderAttack) та методи (CheckCell, WouldKingBeInCheck, IsBlockedPath, ScanDiagonal, ScanStraight) винесені в абстрактний клас Figure (рядки 17–184), усуваючи дублювання між усіма 6 типами фігур.
Замість великого switch на тип фігури — кожен підклас перевизначає IsAttacking(), MoveFigure() та FillArrayAttacksCell(). Board викликає ці методи поліморфно через RefreshAllAttackCell() (рядки 363–375) та IsSquareUnderAttack() (рядки 378–390).
Розмір дошки 8 та розмір клітинки 80 зберігаються як іменовані поля boardSize та tileSize (Board.cs, рядки 24–25), що дозволяє змінити розмір дошки в одному місці без пошуку магічних чисел по всьому коду.
Запити без побічних ефектів відокремлені від модифікуючих методів:
- Запити:
IsKingInCheck()(рядки 420–423),GetAllFigures()(рядки 426–454),GetKing()(рядки 458–470),IsSquareUnderAttack()(рядки 378–390) - Модифікатори:
MoveFigure()(рядки 298–360),RefreshAllAttackCell()(рядки 363–375)
Методи сканування ScanDiagonal() та ScanStraight() (рядки 161–184) приймають (startX, startY, stepX, stepY) — узгоджену групу параметрів що представляє вектор напрямку. Це замінює розкидану арифметику координат у кожній фігурі єдиним стандартним викликом.