System backendu dla inteligentnej szachownicy opartej na Raspberry Pi z silnikiem szachowym AI. Backend zarządza komunikacją między aplikacją webową, fizyczną szachownicą i silnikiem szachowym poprzez protokół MQTT oraz dostarcza REST API i powiadomienia real-time przez Mercure.
- 🚀 Funkcjonalności
- 🏗️ Architektura systemu
- 📋 Wymagania
- 🛠️ Instalacja
- 🎮 Użytkowanie
- 📊 Monitorowanie
- 🐛 Debugowanie
- 📡 Dokumentacja komunikacji MQTT
- 🔄 Przepływ walidacji ruchu
- 🎯 Walidacja i synchronizacja
- 📨 Mercure Real-time Messages
- 🔐 Mercure Konfiguracja
- 🐳 Docker - Szybki start
- 📝 Status implementacji
- ♟️ Przykład pełnej partii
- 🏰 Zaawansowane ruchy szachowe
- 🌐 REST API - Endpointy dla wykonywania ruchów, resetowania gry, możliwych ruchów i sprawdzania stanu zdrowia
- 📡 MQTT Broker - Komunikacja z Raspberry Pi i silnikiem szachowym z pełną walidacją
- ⚡ Real-time Mercure - Powiadomienia na żywo przez Server-Sent Events z bezpośrednią HTTP komunikacją
- 🎯 Zarządzanie stanem gry - Śledzenie ruchów, pozycji i historii partii z walidacją przez silnik
- � Specjalne ruchy szachowe - Pełne wsparcie dla roszady, promocji pionka, szachu i mata
- ��� Health Check - Monitorowanie stanu wszystkich komponentów systemu
- 📝 Logowanie - Szczegółowe logi komunikacji i błędów
- 🔄 Synchronizacja - Dwukierunkowa komunikacja między UI a fizyczną planszą z walidacją ruchów
- ♟️ Możliwe ruchy - Real-time podpowiedzi ruchów z silnika szachowego
- 🔐 JWT autoryzacja - Bezpieczna komunikacja z Mercure Hub
┌─────────────┐ REST API ┌─────────────┐ MQTT ┌──────────────┐
│ Web App │◄──────────────►│ Backend │◄────────────►│ Raspberry Pi │
└─────────────┘ └─────────────┘ └──────────────┘
▲ │ ▲
│ Mercure (HTTP+JWT) │ MQTT │
└───────────────────────────────┘ │
│ │
▼ │
┌─────────────┐ │
│Chess Engine │ │
│ AI │◄────────────────────────┘
└─────────────┘ MQTT
- PHP 8.2+ z rozszerzeniami: mbstring, xml, ctype, json
- Composer 2.0+
- Symfony 7.3+ z bundlami: Mercure, MQTT, HTTP Client
- MQTT Broker (np. Mosquitto)
- Mercure Hub dla Server-Sent Events na porcie 3000
- SQLite/MySQL/PostgreSQL (opcjonalne)
git clone https://github.com/KN-Algo/Symfony-Chess-Backend.git
cd Symfony-Chess-Backend
composer install
Utwórz i edytuj plik .env
:
# MQTT Configuration
MQTT_BROKER=127.0.0.1
MQTT_PORT=1883
MQTT_CLIENT_ID=szachmat_backend
# Mercure Configuration (z JWT autoryzacją)
MERCURE_URL=http://127.0.0.1:3000/.well-known/mercure
MERCURE_PUBLIC_URL=http://127.0.0.1:3000/.well-known/mercure
MERCURE_JWT_SECRET=TWÓJ_TOKEN_JWT
# Database (opcjonalne)
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# W katalogu mercure
$env:MERCURE_PUBLISHER_JWT_KEY='TWÓJ_TOKEN_JWT'
$env:MERCURE_SUBSCRIBER_JWT_KEY='TWÓJ_TOKEN_JWT'
.\mercure.exe run --config dev.Caddyfile
symfony server:start --no-tls
php bin/console app:mqtt-listen
POST /move
- Wykonaj ruch (walidowany przez silnik, obsługuje specjalne ruchy)POST /restart
- Zresetuj gręPOST /possible-moves
- Żądaj możliwych ruchów dla pozycjiGET /test-mercure
- Test endpointu MercureGET /state
- Pobierz stan gryGET /health
- Sprawdź stan systemu
Standardowy ruch:
curl -X POST http://localhost:8000/move \
-H "Content-Type: application/json" \
-d '{"from": "e2", "to": "e4"}'
Roszada krótka:
curl -X POST http://localhost:8000/move \
-H "Content-Type: application/json" \
-d '{
"from": "e1",
"to": "g1",
"special_move": "castling_kingside"
}'
Promocja pionka:
curl -X POST http://localhost:8000/move \
-H "Content-Type: application/json" \
-d '{
"from": "e7",
"to": "e8",
"special_move": "promotion",
"promotion_piece": "queen",
"available_pieces": ["queen", "rook", "bishop", "knight"]
}'
curl -X POST http://localhost:8000/possible-moves \
-H "Content-Type: application/json" \
-d '{"position": "e2"}'
Odpowiedź zostanie przesłana przez Mercure w czasie rzeczywistym:
{
"type": "possible_moves",
"position": "e2",
"moves": ["e3", "e4"]
}
const eventSource = new EventSource(
"http://localhost:3000/.well-known/mercure?topic=http://127.0.0.1:8000/chess/updates"
);
eventSource.onmessage = function (event) {
const data = JSON.parse(event.data);
console.log("Otrzymano:", data);
};
php bin/console app:mqtt-listen
- Uruchom listener MQTTphp bin/console cache:clear
- Wyczyść cachephp bin/console debug:router
- Pokaż dostępne trasyphp bin/console debug:container mercure
- Sprawdź konfigurację Mercure
System dostarcza endpoint /health
który zwraca status wszystkich komponentów:
Warning
Poniższe dane są przykładowe i mogą się różnić w zależności od stanu systemu.
{
"status": "healthy",
"timestamp": "...",
"components": {
"mqtt": { "status": "healthy", "response_time": 12.5 },
"mercure": { "status": "healthy", "response_time": 45.2 },
"raspberry_pi": { "status": "warning", "response_time": null },
"chess_engine": { "status": "healthy", "response_time": 89.1 }
}
}
System używa bezpośredniej HTTP komunikacji z Mercure Hub z JWT autoryzacją:
- Logi zapisywane w
public/mercure-debug.log
- Test endpoint:
GET /test-mercure
- Sprawdź JWT token:
php generate_jwt.php
- MQTT Listener loguje wszystkie wiadomości
- Subscribe na
move/+
dla wszystkich move topików - Szczegółowe logi w konsoli i pliku
Komponent | Subskrybuje (MQTT topic) | Publikuje (MQTT topic) |
---|---|---|
Web App | • Mercure WebSocket z chess/updates | • move/web – ruch wysłany przez UI• move/possible_moves/request – żądanie możliwych ruchów |
Silnik szachowy | • move/engine – żądanie walidacji ruchu• engine/possible_moves/request – żądanie możliwych ruchów• control/restart/external – sygnał resetu gry |
• move/ai – ruch AI• status/engine – thinking /ready /error /analyzing • engine/possible_moves/response – odpowiedź z możliwymi ruchami• engine/move/confirmed – potwierdzenie legalnego ruchu z FEN• engine/move/rejected – odrzucenie nielegalnego ruchu |
Raspberry Pi | • move/raspi – polecenie fizycznego ruchu• move/raspi/rejected – polecenie cofnięcia ruchu• control/restart/external – sygnał resetu gry |
• move/player – wykryty ruch gracza na planszy• status/raspi – ready /moving /error /busy |
Backend | • move/player – ruch fizyczny od RPi• move/web – ruch z UI• move/ai – ruch od silnika• move/possible_moves/request – żądanie od UI• engine/possible_moves/response – odpowiedź od silnika• engine/move/confirmed – potwierdzenie od silnika• engine/move/rejected – odrzucenie od silnika• status/raspi – status RPi• status/engine – status silnika• control/restart – reset gry |
• move/engine – żądanie walidacji do silnika• move/raspi – polecenie ruchu do RPi• move/raspi/rejected – polecenie cofnięcia do RPi• engine/possible_moves/request – żądanie do silnika• state/update – pełny stan gry• log/update – aktualizacja logów• control/restart/external – reset gry do RPi i silnika |
Web App → move/web → Backend → move/engine (walidacja + physical: false) → Silnik →
engine/move/confirmed → Backend → move/raspi (do RPi) + Mercure (do UI)
RPi → move/player → Backend → move/engine (walidacja + physical: true) → Silnik →
engine/move/confirmed → Backend → Mercure (do UI) [RPi nic nie robi - pionek już jest na miejscu]
RPi → move/player → Backend → move/engine (walidacja + physical: true) → Silnik →
engine/move/rejected → Backend → move/raspi/rejected (cofnij ruch) + Mercure (do UI)
Silnik → move/ai {from, to, fen, next_player} → Backend → move/raspi (do RPi) + Mercure (do UI)
Web App → POST /possible-moves → Backend → move/possible_moves/request →
Backend → engine/possible_moves/request → Silnik → engine/possible_moves/response →
Backend → Mercure (do UI z type: possible_moves)
Web App (REST API) → Backend [control/restart listener] → control/restart/external → RPi + Silnik
Backend → state/update + log/update + Mercure
Poniżej znajdziesz przykładowe treści wiadomości przesyłanych na każdym z głównych topiców MQTT w systemie. Każdy topic ma przykład wiadomości z minimalnie wymaganymi polami:
Standardowy ruch:
{
"from": "e2",
"to": "e4",
"physical": false
}
Roszada krótka:
{
"from": "e1",
"to": "g1",
"special_move": "castling_kingside",
"physical": false
}
Promocja pionka:
{
"from": "e7",
"to": "e8",
"special_move": "promotion",
"promotion_piece": "queen",
"available_pieces": ["queen", "rook", "bishop", "knight"],
"physical": false
}
{
"from": "g1",
"to": "f3",
"physical": true
}
{
"from": "e2",
"to": "e4",
"current_fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"type": "move_validation",
"physical": false
}
Standardowy ruch AI:
{
"from": "e7",
"to": "e5",
"fen": "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2",
"next_player": "white"
}
Roszada długa AI:
{
"from": "e8",
"to": "c8",
"fen": "r3kbnr/ppppqppp/2n5/2b1p3/2B1P3/5N2/PPPP1PPP/RNBQ1RK1 w kq - 4 4",
"next_player": "white",
"special_move": "castling_queenside",
"additional_moves": [{ "from": "a8", "to": "d8", "piece": "rook" }],
"notation": "0-0-0"
}
Promocja z szachem:
{
"from": "e7",
"to": "e8",
"fen": "rnbqkbnQ/pppp1ppp/8/8/8/8/PPPP1PPP/RNB1KBNR b KQq - 0 4",
"next_player": "black",
"special_move": "promotion",
"promotion_piece": "queen",
"notation": "e8=Q+",
"gives_check": true
}
Standardowy ruch:
{
"from": "e2",
"to": "e4",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
}
Roszada krótka:
{
"from": "e1",
"to": "g1",
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKB1R w Qkq - 1 1",
"type": "castling",
"subtype": "kingside",
"moves": [
{
"from": "e1",
"to": "g1",
"piece": "king",
"order": 1
},
{
"from": "h1",
"to": "f1",
"piece": "rook",
"order": 2
}
],
"notation": "0-0"
}
Promocja pionka:
{
"from": "e7",
"to": "e8",
"fen": "rnbqkbnQ/pppp1ppp/8/8/8/8/PPPP1PPP/RNB1KBNR b KQq - 0 4",
"type": "promotion",
"piece_removed": "pawn",
"piece_placed": "queen",
"color": "white",
"notation": "e8=Q+",
"gives_check": true,
"instructions": {
"step1": "Usuń białego pionka z e7",
"step2": "Umieść białego hetmana na e8",
"step3": "Figura daje szach przeciwnemu królowi"
}
}
{
"from": "e2",
"to": "e5",
"reason": "Illegal move: pawn cannot move two squares from e2 to e5",
"action": "revert_move",
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
{
"position": "e2"
}
Standardowy ruch potwierdzony:
{
"from": "e2",
"to": "e4",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"next_player": "black",
"physical": false
}
Roszada potwierdzona:
{
"from": "e1",
"to": "g1",
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKB1R w Qkq - 1 1",
"next_player": "black",
"physical": false,
"special_move": "castling_kingside",
"additional_moves": [{ "from": "h1", "to": "f1", "piece": "rook" }],
"notation": "0-0"
}
Promocja z szachem potwierdzona:
{
"from": "e7",
"to": "e8",
"fen": "rnbqkbnQ/pppp1ppp/8/8/8/8/PPPP1PPP/RNB1KBNR b KQq - 0 4",
"next_player": "black",
"physical": false,
"special_move": "promotion",
"promotion_piece": "queen",
"notation": "e8=Q+",
"gives_check": true
}
Mat:
{
"from": "d1",
"to": "h5",
"fen": "rnb1kbnr/pppp1ppp/8/7Q/4Pp2/8/PPPP2PP/RNB1KBNR b KQkq - 1 3",
"next_player": "black",
"physical": false,
"notation": "Qh5#",
"gives_check": true,
"game_status": "checkmate",
"winner": "white"
}
{
"from": "e2",
"to": "e5",
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"physical": true,
"reason": "Illegal move: pawn cannot move two squares from e2 to e5"
}
{
"position": "e2",
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
{
"position": "e2",
"moves": ["e3", "e4"]
}
{
"status": "ready"
}
{
"status": "thinking"
}
{
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
💡 Uwaga: Backend nasłuchuje na
control/restart
ale publikujecontrol/restart/external
aby uniknąć nieskończonej pętli resetowania.
{
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}
{
"fen": "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2",
"moves": [
{
"from": "e2",
"to": "e4",
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"player": "white",
"timestamp": 1692454800
},
{
"from": "e7",
"to": "e5",
"fen": "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2",
"player": "black",
"timestamp": 1692454815,
"notation": "e5"
}
],
"turn": "white",
"pending_moves": [],
"game_status": "playing",
"winner": null,
"game_ended": false,
"in_check": false,
"check_player": null
}
{
"moves": ["e2e4", "e7e5"]
}
Important
- WSZYSTKIE ruchy (fizyczne i webowe) są walidowane przez silnik
- Silnik jest źródłem prawdy o legalności ruchów i FEN
- Flaga
physical
określa źródło ruchu i reakcję na walidację
Typ ruchu | Walidacja | Akcja po confirmed | Akcja po rejected |
---|---|---|---|
Webowy | ✅ | Wyślij move/raspi |
Powiadom UI o błędzie |
Fizyczny | ✅ | Nic (pionek już tam jest) | Wyślij move/raspi/rejected |
{
"type": "possible_moves",
"position": "e2",
"moves": ["e3", "e4"]
}
{
"type": "move_pending",
"move": {"from": "e2", "to": "e4"},
"physical": false,
"state": {...}
}
{
"type": "move_confirmed",
"move": {"from": "e2", "to": "e4"},
"physical": false,
"state": {...}
}
{
"type": "move_rejected",
"move": {"from": "e2", "to": "e5"},
"reason": "Illegal move: pawn cannot move two squares from e2 to e5",
"physical": false,
"state": {...}
}
{
"type": "ai_move_executed",
"move": {"from": "g8", "to": "f6"},
"state": {...}
}
{
"type": "raspi_status",
"data": {...},
"timestamp": "17:30:15"
}
{
"type": "engine_status",
"data": {...},
"timestamp": "17:30:20"
}
{
"type": "game_reset",
"state": {
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"moves": [],
"turn": "white"
}
}
- Backend używa HTTP Client zamiast Symfony Hub
- JWT token generowany w locie z claims:
{"mercure": {"publish": ["*"]}}
- Publiczne updates bez autoryzacji subskrypcji
- Topic:
http://127.0.0.1:8000/chess/updates
(cors) {
@cors_preflight method OPTIONS
header {
Access-Control-Allow-Origin "{header.origin}"
Vary Origin
Access-Control-Expose-Headers "Authorization"
Access-Control-Allow-Credentials "true"
}
handle @cors_preflight {
header {
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
Access-Control-Max-Age "3600"
}
respond "" 204
}
}
:80 {
import cors {header.origin}
}
:8000 {
import cors {header.origin}
}
http://localhost:3000 {
encode zstd gzip
mercure {
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY}
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY}
cors_origins *
publish_origins *
demo
anonymous
subscriptions
}
redir / /.well-known/mercure/ui/
respond /healthz 200
}
$env:MERCURE_PUBLISHER_JWT_KEY='TWÓJ_TOKEN_JWT'
$env:MERCURE_SUBSCRIBER_JWT_KEY='TWÓJ_TOKEN_JWT'
.\mercure.exe run --config dev.Caddyfile
Jeśli chcesz szybko uruchomić cały system bez lokalnej instalacji PHP i zależności, możesz użyć Docker:
- Docker Desktop lub Docker Engine
- Docker Compose v2+
-
Sklonuj repozytorium:
git clone https://github.com/KN-Algo/Symfony-Chess-Backend.git cd Symfony-Chess-Backend
-
Skonfiguruj zmienne środowiskowe:
# Windows copy .env.example .env # Linux/Mac cp .env.example .env
Opcjonalnie edytuj
.env
aby dostosować:RASPBERRY_PI_URL
- adres URL Twojego Raspberry PiCHESS_ENGINE_URL
- adres URL silnika szachowegoMERCURE_JWT_SECRET
- zmień na własny secret key
-
Uruchom kontenery:
# Windows docker-compose up --build -d # Linux/Mac docker compose up --build -d
-
Sprawdź status:
docker-compose ps
Po uruchomieniu dostępne będą następujące usługi:
Usługa | URL | Opis |
---|---|---|
Backend API | http://localhost:8000 | Główne API Symfony |
Health Dashboard | http://localhost:8000/health | Dashboard monitoringu systemu |
API Health | http://localhost:8000/api/health | JSON endpoint stanu systemu |
MQTT Broker | localhost:1883 | Mosquitto MQTT (port 1883) |
Mercure Hub | http://localhost:3000 | Hub dla real-time komunikacji |
System jest przygotowany na podłączenie zewnętrznych komponentów. Aby je skonfigurować:
-
Edytuj plik
.env
(utworzony z.env.example
):# Raspberry Pi Configuration RASPBERRY_PI_URL=http://192.168.1.100:8080 # Chess Engine Configuration CHESS_ENGINE_URL=http://192.168.1.101:5000
-
Przebuduj kontenery po zmianie konfiguracji:
docker-compose up --build -d
💡 Wskazówka: System będzie działał nawet bez zewnętrznych komponentów - w panelu zdrowia zobaczysz ich status jako "Niedostępny" z odpowiednimi instrukcjami konfiguracji.
# Zatrzymanie wszystkich kontenerów
docker-compose down
# Rebuild i restart
docker-compose up --build -d
# Podgląd logów
docker-compose logs -f
# Logi konkretnej usługi
docker-compose logs -f symfony-backend
# Wejście do kontenera backend
docker-compose exec symfony-backend bash
# Czyszczenie wszystkiego (UWAGA: usuwa również dane!)
docker-compose down -v --rmi all
# Status kontenerów
docker-compose ps
# Sprawdzenie zasobów
docker stats
# Sprawdzenie sieci Docker
docker network ls
docker network inspect symfony-chess-backend_chess-network
Projekt używa następujących kontenerów:
- symfony-backend - główna aplikacja Symfony (PHP 8.4)
- symfony-mqtt-listener - nasłuchiwanie MQTT w tle
- mqtt-broker - Mosquitto MQTT broker v2
- mercure-hub - Dunglas Mercure dla WebSocket
Wszystkie kontenery są połączone w sieci chess-network
co umożliwia im wzajemną komunikację przez nazwy kontenerów.
📝 Uwaga: Więcej szczegółów dotyczących konfiguracji zewnętrznych komponentów znajdziesz w pliku EXTERNAL_COMPONENTS.md
.
✅ Zaimplementowane funkcjonalności:
- ✅ REST API dla ruchów i stanu gry
- ✅ MQTT komunikacja między komponentami
- ✅ Mercure real-time powiadomienia
- ✅ Walidacja ruchów przez silnik szachowy
- ✅ Zarządzanie stanem gry i historii
- ✅ Health check wszystkich komponentów
- ✅ Synchronizacja fizycznej planszy z UI
- ✅ Roszada krótka i długa - pełna obsługa dla obu stron
- ✅ Promocja pionka - z wyborem figury i walidacją dostępności
- ✅ Szach i mat - detekcja i powiadomienia w czasie rzeczywistym
- ✅ Koniec gry - obsługa mata, pata i remisu
- ✅ Notacja szachowa - standardowa notacja algebraiczna
- ✅ Szczegółowe instrukcje - dla Raspberry Pi do wykonania złożonych ruchów
- ✅ Wszystkie ruchy przechodzą przez walidację silnika
- ✅ Obsługa ruchów fizycznych i z UI
- ✅ Specjalne payloady dla roszady i promocji
- ✅ Dodatkowe ruchy (np. wieża przy roszadzie)
- ✅ Status gry i końcowe powiadomienia
- ✅ Śledzenie specjalnych ruchów w historii
- ✅ Metadane ruchów (notacja, szach, typ ruchu)
- ✅ Status końca gry (checkmate, stalemate, draw)
- ✅ Informacje o szachu i graczu w szachu
- ✅ Pełna synchronizacja między komponentami
🔄 W trakcie rozwoju:
- 🔄 Integracja z rzeczywistym silnikiem szachowym
- 🔄 Konfiguracja Raspberry Pi do fizycznych ruchów
- 🔄 Zaawansowane AI przeciwnika
📋 Planowane funkcjonalności:
- 📋 Zapisywanie partii do bazy danych
- 📋 Analiza partii post-game
- 📋 Multiplayer online
- 📋 Turnieje i ranking graczy
Szczegółowy przykład komunikacji podczas pełnej partii szachowej (mat szewczyka w 4 ruchach) z wszystkimi komunikatami MQTT, HTTP i Mercure znajdziesz w dokumencie:
📋 SAMPLE_GAME_COMMUNICATION.md - Krok po kroku: mat szewczyka z pełną komunikacją systemu
Dokument zawiera:
- 🎯 Każdy ruch z szczegółową komunikacją
- 📡 Wszystkie payloady MQTT w poprawnym formacie
- ⚡ Wiadomości Mercure real-time
- 🌐 Żądania HTTP z odpowiedziami
- 📊 Statystyki i podsumowanie komunikacji
Kompleksowy przykład komunikacji podczas długiej partii demonstrującej specjalne ruchy szachowe: roszadę i promocję pionka:
🏰 ADVANCED_GAME_COMMUNICATION.md - Partia z roszadą i promocją pionka (28 ruchów)
Dokument zawiera:
- 🏰 Roszada krótka - dla obu stron z pełną komunikacją MQTT
- ♛ Promocja pionka - na hetmana z szachem oraz jego zbicie
- ⚔️ Liczne zbicia - demonstracja handling różnych figur
- 📡 Specjalne payloady - dla złożonych ruchów szachowych
- 🎯 28 ruchów - pełna partia z zaawansowanymi mechanikami
- 📊 Statystyki materiału - śledzenie wszystkich zbić i promocji