Compare commits

...

47 Commits
v18 ... v56

Author SHA1 Message Date
Hatvani Tamás
f8c14cc268 Enhance logging for data upload process
Added print statements to log data being uploaded to each tab.
2025-11-12 18:50:01 +01:00
Hatvani Tamás
566d72c2d6 Merge pull request #7 from htamas1210/Workflow
Workflow
2025-11-12 18:45:08 +01:00
ad26d22281 indentation error in python script 2025-11-12 18:44:00 +01:00
352b4e57d7 rewrote the python script
the script now checks for project name on every line read from the log
file and appends it to a list named by the project, if the branch is
master then it appends all lines into one list, then it checks if there
is any data in the list and uploads them to the correct tab that matches
the project name or master
2025-11-12 18:42:17 +01:00
Hatvani Tamás
cdd8b45da8 Merge pull request #6 from htamas1210/Engine/movegen-utils
Engine/movegen utils
2025-11-12 11:08:36 +01:00
Varga Dávid Lajos
6006442f90 added '#[inline(always)]' annotation to hot functions: utils::pop_lsb, utils::pop_msb 2025-11-11 14:23:30 +01:00
Varga Dávid Lajos
eebdfdbee2 implemented utility function: bitboard::utils::notation_from_square_number 2025-11-11 14:21:11 +01:00
Varga Dávid Lajos
a6aba8801e implemented utility function: bitboard::utils::pop_msb 2025-11-11 14:16:33 +01:00
Varga Dávid Lajos
8d1300d7e2 implemented utility function: bitboard::utils::pop_lsb 2025-11-11 14:14:39 +01:00
Varga Dávid Lajos
d0e6ce81ce fixed bad test values in test utils::tests::pop_lsb_test 2025-11-11 14:06:43 +01:00
Varga Dávid Lajos
f3dea86ded fixed typo in test utils::tests::pop_msb_test 2025-11-11 14:05:31 +01:00
Varga Dávid Lajos
092ed19104 added frame and tests for function: bitboard::util::notation_from_square_number 2025-11-11 13:59:53 +01:00
Varga Dávid Lajos
f8894bbdff added frame and tests for function: bitboard::util::pop_msb 2025-11-11 13:51:14 +01:00
Varga Dávid Lajos
258a8a0da9 added frame and tests for function: bitboard::util::pop_lsb 2025-11-11 13:39:26 +01:00
Varga Dávid Lajos
dca6eac3ba added file bitboard/utils.rs 2025-11-11 12:50:53 +01:00
Hatvani Tamás
5ee797e1f8 Add conditional logging for master branch 2025-11-11 12:32:03 +01:00
Hatvani Tamás
eaf3bfa192 Merge pull request #5 from htamas1210/Engine/movegen-lut
Engine/movegen lut
2025-11-11 12:17:48 +01:00
Hatvani Tamás
9c73ca6838 Fix formatting of conditional checks in dispatcher.yml 2025-11-11 11:34:05 +01:00
Hatvani Tamás
0f50f31b13 Update branch checks to be case-sensitive
Refactor branch checks to be case-sensitive for Engine, Server, and UI.
2025-11-11 11:27:42 +01:00
Hatvani Tamás
b6f0b6ee5e Fix conditional syntax in dispatcher.yml 2025-11-11 11:22:21 +01:00
4ae9eea7e2 removed pull_request dispatcher from release workflow 2025-11-11 11:13:45 +01:00
1f368551c1 removed pull_request dispatch from the test files 2025-11-11 11:12:39 +01:00
Hatvani Tamás
4e9f222ddc Allow case-insensitive branch checks in dispatcher.yml 2025-11-11 11:01:38 +01:00
Varga Dávid Lajos
061795a039 implemented the generation of LUT: RAY_TABLE 2025-11-11 10:39:51 +01:00
Varga Dávid Lajos
1af497f063 implemented the generation of LUT: KNIGHT_ATTACK_MAP 2025-11-11 10:33:51 +01:00
Varga Dávid Lajos
b6cdf5b778 implemented the generation of LUT: PAWN_ATTACK_MAP 2025-11-11 10:32:41 +01:00
Varga Dávid Lajos
4eb4bc1348 implemented the generation of LUT: KING_ATTACK_MAPS 2025-11-11 10:25:23 +01:00
Varga Dávid Lajos
c2fe2e968a added masks for use against wrap-around 2025-11-11 10:22:05 +01:00
Varga Dávid Lajos
21f2890b92 added frame and tests for table: RAY_TABLE 2025-11-10 17:17:30 +01:00
Varga Dávid Lajos
5d748b07d2 added frame and tests for table: KNIGHT_ATTACK_MAP 2025-11-10 16:54:14 +01:00
Varga Dávid Lajos
b252f16b2d added frame and tests for table: PAWN_ATTACK_MAP 2025-11-10 15:50:58 +01:00
Varga Dávid Lajos
18f4060df0 added frame and tests for table: KING_ATTACK_MAP 2025-11-10 15:28:15 +01:00
Varga Dávid Lajos
033440fbac added dependency to Cargo.toml: once_cell 2025-11-10 13:18:48 +01:00
Varga Dávid Lajos
bfb2a6db23 added file and module structure for attackmaps.rs 2025-11-10 13:13:03 +01:00
Bence
70d10719e6 Dokumentumok feltöltése a master branch-re 2025-11-10 12:21:27 +01:00
Hatvani Tamás
85f7c6e690 Remove checkout step from upload_data workflow
Removed checkout step from upload job in workflow.
2025-11-09 15:05:10 +01:00
Hatvani Tamás
2ac2f17d1a Fix subprocess by adding decode to file path in upload_data.yml 2025-11-09 15:01:42 +01:00
Hatvani Tamás
7cd4bb6b09 Refactor subprocess call and print statement
Updated subprocess command to use list format and modified print statement for clarity.
2025-11-09 14:55:23 +01:00
1bf4d875af Workflow: added subprocess run to have the correct path for the test log file 2025-11-09 14:48:04 +01:00
Hatvani Tamás
8a01bba644 Update pip install command to break system packages 2025-11-09 14:34:18 +01:00
Hatvani Tamás
cf2edab297 Remove Python setup from upload_data workflow
Removed Python setup step for Google Sheets API.
2025-11-09 14:31:18 +01:00
Hatvani Tamás
5443002c5f Workflow: removed runs-on: self-hosted from test upload job 2025-11-09 14:26:28 +01:00
e19a5aac9a Workflow: Re added test-upload job, added workflow_call trigger 2025-11-09 14:24:24 +01:00
Hatvani Tamás
3515703a90 Modify zip commands in release workflow to include files in the folder with the wildcard
Update zip commands to include all files in the directories.
2025-11-09 13:12:27 +01:00
Hatvani Tamás
a6966e055d Compress build folders for upload
Added compression step for build folders before release.
2025-11-09 13:05:23 +01:00
Bence
1788cce42d Kövspec áttekintés, vágyálom rendszer hozzáadása 2025-11-04 12:01:25 +01:00
Hatvani Tamás
49f4ffc380 Document system requirements for chess application
Added system requirements and specifications for a two-player chess application, detailing functional, client-side, server-side, non-functional requirements, and components.
2025-11-03 18:41:29 +01:00
13 changed files with 564 additions and 40 deletions

View File

@@ -26,13 +26,13 @@ jobs:
SERVER=false
UI=false
if [[ "$BRANCH" == *"Engine"* ]]; then
if [[ "$BRANCH" == *"Engine"* ]] ; then
ENGINE=true
fi
if [[ "$BRANCH" == *"Server"* ]]; then
if [[ "$BRANCH" == *"Server"* ]] ; then
SERVER=true
fi
if [[ "$BRANCH" == *"UI"* ]]; then
if [[ "$BRANCH" == *"UI"* ]] ; then
UI=true
fi
@@ -68,13 +68,10 @@ jobs:
test-data-upload:
needs: [engine, server, ui]
runs-on: self-hosted
if: always()
#uses: ./.github/workflows/upload_data.yml
steps:
- name: Temp turn off
run: |
echo "Uploading data to table"
uses: ./.github/workflows/upload_data.yml
secrets: inherit
release:
needs: test-data-upload

View File

@@ -1,7 +1,6 @@
name: Engine Tests
on:
pull_request:
workflow_dispatch:
workflow_call:

View File

@@ -1,7 +1,6 @@
name: Release build
on:
pull_request:
workflow_dispatch:
workflow_call:
@@ -82,16 +81,24 @@ jobs:
cargo build --release --target x86_64-pc-windows-gnu
pwd
cp target/x86_64-pc-windows-gnu/release/ui.exe $(git rev-parse --show-toplevel)/release/windows/ui.exe
- name: Compress build folders for upload
run: |
cd $(git rev-parse --show-toplevel)
pwd
cd release/
zip linux.zip linux/*
zip windows.zip windows/*
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: "v${{ github.run_number }}"
name: "Release v${{ github.run_number }}"
generate_release_notes: true
files: |
release/linux/
release/windows/
release/linux.zip
release/windows.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,7 +1,6 @@
name: Server Tests
on:
pull_request:
workflow_dispatch:
workflow_call:

View File

@@ -30,6 +30,9 @@ echo "$PROJECT_NAME" > "$LOG_FILE"
awk '/^running [0-9]+ test[s]?$/,/^$/' full_test_output.log >> "$LOG_FILE"
# --- APPEND TO GLOBAL LOG (in repo root) ---
if [[ $(git rev-parse --abbrev-ref HEAD) == "master" ]]; then
echo "master" >> $FINAL_LOG
fi
cat "$LOG_FILE" >> "$FINAL_LOG"
# --- SUMMARY ---

View File

@@ -1,7 +1,6 @@
name: UI Tests
on:
pull_request:
workflow_dispatch:
workflow_call:

View File

@@ -2,22 +2,15 @@ name: Upload Test Results to Google Sheets
on:
workflow_dispatch:
workflow_call:
jobs:
upload:
runs-on: self-hosted
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Python (for Google Sheets API)
uses: actions/setup-python@v5
with:
python-version: '3.x'
steps:
- name: Install dependencies
run: |
pip install gspread google-auth
pip install gspread google-auth --break-system-packages
- name: Upload test_data.log to Google Sheets
env:
@@ -27,31 +20,77 @@ jobs:
echo "$GOOGLE_SERVICE_ACCOUNT_JSON" > service_account.json
python <<'PYCODE'
import gspread, json, time
import gspread, json, subprocess
# credentials
creds = json.load(open("service_account.json"))
gc = gspread.service_account_from_dict(creds)
sh = gc.open_by_key("${{ secrets.SPREADSHEET_ID }}")
v = subprocess.run(['git','rev-parse','--show-toplevel'], capture_output=True).stdout.decode().strip()
print(f"{v}/test_data.log")
with open("test_data.log", "r") as f:
def writeRowsToSpreadsheet(data_list, worksheet):
existing_rows = len(worksheet.get_all_values())
start_row = existing_rows + 3
rows_to_append = [row.split() for row in data_list]
print("rows to append")
print(f"{rows_to_append}")
for i, row in enumerate(rows_to_append):
worksheet.insert_row(row, start_row + i)
with open(f"{v}/test_data.log", "r") as f:
lines = [line.strip() for line in f if line.strip()]
isMaster = False
project = lines[0].lower()
worksheet = sh.worksheet(project)
if project == "master":
isMaster = True
engine_data = []
server_data = []
ui_data = []
master_data = []
# project name
data = lines[1:]
for entry in lines:
if not isMaster and entry == "engine":
project = "engine"
elif not isMaster and entry == "server":
project = "server"
elif not isMaster and entry == "ui":
project = "ui"
#blank rows
existing_rows = len(worksheet.get_all_values())
start_row = existing_rows + 3
if project == "engine" and entry != "engine":
engine_data.append(entry)
elif project == "server" and entry != "server":
server_data.append(entry)
elif project == "ui" and entry != "ui":
ui_data.append(entry)
elif project == "master" and entry != "master":
master_data.append(entry)
# Split data into columns (by spaces)
rows_to_append = [row.split() for row in data]
print("PRINTING FILTERED DATA\n\n")
print(f"engine\n{engine_data}")
print(f"server\n{server_data}")
print(f"ui\n{ui_data}")
print(f"master\n{master_data}")
print("\n\n\n")
for i, row in enumerate(rows_to_append):
worksheet.insert_row(row, start_row + i)
if isMaster and len(master_data) != 0:
print("uploading to master tab")
worksheet = sh.worksheet("master")
writeRowsToSpreadsheet(master_data, worksheet)
exit(0)
if len(engine_data) != 0:
print("uploading to engine tab")
writeRowsToSpreadsheet(engine_data, sh.worksheet("engine"))
if len(server_data) != 0:
print("uploading to server tab")
writeRowsToSpreadsheet(server_data, sh.worksheet("server"))
if len(ui_data) != 0:
print("uploading to ui tab")
writeRowsToSpreadsheet(ui_data, sh.worksheet("ui"))
print(f"Uploaded {len(rows_to_append)} rows to '{project}' tab.")
PYCODE

125
Docs/kovspec.md Normal file
View File

@@ -0,0 +1,125 @@
# Követelmény-specifikáció
## 1. Áttekintés
A jelen dokumentum célja, hogy bemutassa a CastlingCreations megrendelésére készülő Knightly nevű alkalmazás alapvető céljait, funkcionális és nem funkcionális követelményeinek áttekintését, valamint a fejlesztés kontextusát.
A Knightly egy modern, digitális sakkalkalmazás, amely kezdetben helyi hálózaton (LAN) keresztül teszi lehetővé két játékos számára, hogy valós időben mérkőzzenek meg egymással. A rendszer egy grafikus felhasználói felületen keresztül biztosítja a játék indítását, a szerver futtatását és a másik félhez történő csatlakozást.
A projekt hosszú távú célja, hogy a Knightly egy online platformmá fejlődjön, amely hasonló módon működik, mint a népszerű sakkportálok (pl. chess.com): a felhasználók fiókot hozhatnak létre, bejelentkezhetnek, és egy központi szerveren keresztül kereshetnek, illetve indíthatnak mérkőzéseket.
A fejlesztés első szakasza azonban a LAN-alapú verzió megvalósítására koncentrál, amely a sakkjátszma logikai alapjainak, a játékállapot kezelésének és a hálózati kommunikáció modelljének megvalósítását célozza. A későbbi online verzió ezekre az alapokra építkezve bővíthető tovább.
## 2. Vágyálom rendszer
A vágyálom rendszer célja egy teljes körű, modern online sakkplatform létrehozása, amely nemcsak a klasszikus sakkjáték digitális megvalósítását kínálja, hanem egy közösségi, versenyképes és kényelmes felhasználói élményt is biztosít. A hosszú távú cél, hogy a rendszer működése és felépítése a nagyobb nemzetközi sakkoldalakhoz (például a chess.com-hoz vagy a lichess.org-hoz) hasonló legyen, de saját, könnyen kezelhető és letisztult felülettel.
A felhasználók a rendszerben saját profilt hozhatnak létre, amellyel be tudnak jelentkezni, és részt vehetnek online mérkőzéseken más játékosok ellen. A rendszer automatikusan párosítaná őket ellenfelekkel, de lehetőséget adna arra is, hogy barátokat hívjanak meg privát meccsekre. A lejátszott mérkőzések mentésre kerülnének, így a játékosok bármikor visszanézhetnék vagy elemezhetnék azokat.
A játék mellett a felhasználók statisztikákat is láthatnának magukról, például nyerési arányt, aktuális értékszámot (rating), leggyakoribb megnyitásokat, illetve fejlődési tendenciát az idő során. A rendszer ezen felül egy egyszerű chat funkciót is tartalmazna, hogy a játékosok kommunikálhassanak egymással a játszmák közben vagy akár azokon kívül is.
A vágyálom rendszer alapját egy központi szerver képezné, amely kezeli a felhasználói fiókokat, a bejelentkezéseket, a matchmaking folyamatot, valamint a játékok futását és szinkronizálását. A szerver a kliensalkalmazásokkal valós idejű adatkapcsolatot tartana fenn, így a játék során minden lépés azonnal megjelenne a másik játékosnál is.
A platform célja a megbízható és folyamatos működés, akár nagyobb terhelés mellett is. A rendszer fejlesztése során kiemelt szempont lenne a biztonság (adatvédelem, csalás elleni védelem), a stabil hálózati kommunikáció, valamint a bővíthetőség például ranglisták, versenyek vagy mobilalkalmazás későbbi integrálásának lehetősége.
Összességében a vágyálom rendszer egy minden szempontból teljes értékű, közösségorientált sakkalkalmazás lenne, amely a mostani, helyi hálózaton működő változatból fejlődne tovább egy interneten keresztül bárhonnan elérhető platformmá.
## 4. Rendszer követelmények
A rendszer célja egy kétjátékos sakkalkalmazás megvalósítása, amely alapvetően hálózati kapcsolat (LAN vagy internet) segítségével biztosítja a valós idejű játékot. A rendszer kliensszerver architektúrán alapul, ahol az egyes komponensek jól elkülönülten, meghatározott feladatokat látnak el.
### 4.1 Kötelező funkcionális követelmények
A rendszernek az alábbi alapvető funkciókat mindenképpen biztosítania kell:
- Két játékos közötti sakkjátszma lebonyolítása, a hivatalos sakk szabályai alapján.
- A játékosok felváltva tehetnek lépéseket, a lépések érvényességét a kliens oldali logika ellenőrzi.
- A rendszer valós időben szinkronizálja a két kliens állapotát (mindkét fél ugyanazt a táblát látja).
- A játék vége (sakkmatt, patt, idő lejárta) automatikusan felismerésre kerül.
- A szerver egyidejűleg több játékot is képes kezelni (külön szobákban vagy sessionökben).
- A játékosok elindíthatnak új meccset, illetve befejezett játék után visszatérhetnek a főmenübe.
- A rendszer minden játékban egyedi azonosítót (Game ID) használ a játékállapot nyomon követéséhez.
- A kliens értesítéseket kap az ellenfél lépéseiről és a játék állapotváltozásairól.
### 4.2 Kliens oldali követelmények
A kliens felelős a játékos felhasználói élményéért, a grafikus megjelenítésért és a játéklogika helyi működéséért.
A kliensnek tudnia kell:
- A sakk tábla és a figurák megjelenítése, lépések kezelése (egérkattintás vagy billentyűparancsok).
- Lépések érvényesítése és elküldése a szervernek.
- A szervertől érkező események (ellenfél lépése, állapotváltozás) feldolgozása és megjelenítése.
- Hibák és megszakadt kapcsolat kezelése (újracsatlakozási lehetőség).
- Saját IP vagy szerver cím megadása LAN esetén.
- Alapvető menürendszer (csatlakozás, szerverindítás, új játék, kilépés).
- A hálózati kommunikáció egységes formátumban történjen (pl. JSON alapú üzenetek).
### 4.3 Szerver oldali követelmények
A szerver feladata a kliensek közti kommunikáció kezelése, az állapotok szinkronizálása és a játék logikai integritásának megőrzése.
A szervernek tudnia kell:
- Kapcsolatok fogadása és kezelése több kliens esetén is.
- Új játék (session) létrehozása és azonosító kiosztása.
- Üzenetek továbbítása a kliensek között (pl. lépés, visszajelzés, játék vége).
- A játékállapot naprakészen tartása és küldése mindkét félnek.
- Kapcsolat megszakadása esetén az érintett játék szüneteltetése vagy lezárása.
- Üzenetformátumok ellenőrzése és hibás adatok elutasítása.
- Kliensazonosítás és egyszerű hitelesítés (pl. játékosnév alapján).
- A kommunikáció biztonságos kezelése (üzenetduplikáció, szinkronizációs hibák elkerülése).
### 4.4 A komponensek közti kommunikáció (szerződések)
A rendszer komponensei egy meghatározott üzenetprotokollon keresztül kommunikálnak egymással.
A kommunikáció kétirányú, valós idejű, és az alábbi szerződések szerint zajlik:
A kliens minden lépést csak akkor hajt végre a felhasználói felületen, ha a szervertől visszaigazolást kapott az érvényességéről.
A szerver az üzeneteket sorosítva, FIFO-elv szerint dolgozza fel, és broadcastolja a változásokat az adott játékhoz tartozó összes kliensnek.
### 4.5 Nem funkcionális követelmények
- Megbízhatóság: a rendszernek stabilan kell működnie hálózati késleltetés és csomagvesztés esetén is.
- Teljesítmény: a szerver legalább 10 párhuzamos játékot képes kezelni érezhető lassulás nélkül.
- Biztonság: a kliens csak a szerver által engedélyezett parancsokat hajthatja végre.
- Bővíthetőség: a rendszer felépítése moduláris legyen, hogy később könnyen kiterjeszthető legyen (pl. online matchmaking).
- Platformfüggetlenség: a kliens és a szerver futtatható legyen Windows, Linux és esetleg webes környezetben.
- Karbantarthatóság: kódmodulok (logika, hálózat, UI) elkülönítése, jól dokumentált interfészekkel.
### 4.6 Minimális technikai elvárások
- Programozási nyelv: Rust, C#, Python vagy más, hálózati alkalmazásokra alkalmas nyelv.
- Kommunikációs protokoll: TCP vagy WebSocket alapú kapcsolat.
- Adatcsere formátum: JSON.
- Grafikus felület: desktop GUI (pl. egérvezérlés, drag & drop lépés).
- Követelmény kliensoldalon: legalább 4 GB RAM, modern operációs rendszer.
- Követelmény szerveroldalon: 1 CPU mag, 512 MB RAM, állandó hálózati kapcsolat.
## 5. Követelménylista
### Szerver
| Név | Verzió | Leírás |
| --- | ------ | ------ |
| **WebSocket** | 1.0 | A szerver és a kliens között folyamatos kétirányú kommunikációt biztosít. A kapcsolat létrejötte után a szerver valós időben képes fogadni és továbbítani az eseményeket (pl. lépés végrehajtása, állapotfrissítés). Hiba esetén a kapcsolat automatikusan újraépül. |
| **Kapcsolatok csoportosítása** | 1.0 | A szerver figyeli az elérhető, szabad klienseket, majd két szabad kapcsolatot automatikusan összerendel egy meccsbe. A csoportosítás után a játékosok azonos „room”-ba kerülnek, és a szerver biztosítja az egymás közötti adatkommunikációt. |
| **Kommunikáció az engine-nel** | 1.0 | A szerver a játékosoktól érkező lépéseket és játékinformációkat továbbítja az engine-nek feldolgozásra. Az engine válasza után a szerver visszaküldi az eredményt a klienseknek (pl. érvényes lépés, matt, patt). A kommunikáció aszinkron módon zajlik, válaszidő-ellenőrzéssel. |
| **Kommunikáció a UI-al** | 1.0 | A szerver WebSocket-en keresztül adatokat továbbít a felhasználói felület és az engine között. A UI által kért műveletek (pl. új meccs létrehozása, állapotlekérés) feldolgozását a szerver közvetíti.|
### Engine
| Név | Verzió | Leírás |
| --- | ------ | ------ |
| **Bitboard** | 1.0 | A játék táblaállapotát bitműveletekkel reprezentálja a hatékonyság érdekében. Minden bábu típus és szín külön bitmask-on kerül tárolásra, lehetővé téve a gyors lekérdezéseket és lépésellenőrzéseket. |
| **Lépésgenerálás LUT** | 1.0 | Előre kiszámított lookup táblák segítségével gyorsítja a lépésgenerálást és szabályellenőrzést. Ez csökkenti a számítási időt, és optimalizálja az engine teljesítményét. |
| **Lépésgenerálás** | 1.0 | A különböző bábutípusok (gyalog, bástya, futó, stb.) lépési logikáját valósítja meg. A függvények ellenőrzik a lépés érvényességét, figyelembe véve az aktuális állást, sakkhelyzetet és speciális szabályokat (pl. sáncolás, en passant). |
| **Util függvények** | 1.0 | Segédfüggvények az engine belső működéséhez, például raycast műveletek, bitműveleti maszkok kezelése, valamint logikai ellenőrzések a lépések és ütések számításához. |
### UI
| Név | Verzió | Leírás |
| --- | ------ | ------ |
| **Belépés** | 1.0 | A felhasználó a kezdőképernyőn keresztül adhatja meg a nevét lokális játékhoz, vagy hitelesítheti magát online játékmód esetén. Hibás adatok esetén a rendszer figyelmeztetést küld. |
| **Főmenü** | 1.0 | Az alkalmazás központi navigációs felülete, ahol a felhasználó meccset kereshet, új játékot indíthat lokálisan, vagy beállításokat módosíthat. A menü megjeleníti az aktuális státuszt (online/offline). |
| **Játék** | 1.0 | A játékfelület megjeleníti a táblát, bábukat, lépéseket, és az aktuális játékállást. Támogatja mind az online, mind a lokális módot. A felület kezeli az interakciókat (lépéskattintás, visszavonás, végeredmény kijelzés). |
| **Kommunikáció a szerverrel** | 1.0 | A kliens a szerveren keresztül kommunikál az engine-nel. A UI felel az üzenetek küldéséért (lépés, új játék, visszajelzés), valamint a szervertől kapott események vizuális megjelenítéséért. |
### GitHub Actions (CI/CD)
| Név | Leírás |
| --- | ------ |
| Folyamatos tesztelés | A projekt minden commit után automatikusan tesztelődik. A pipeline lefuttatja a teszteket, és értesítést küld hibás build esetén. |
| Folyamatos integráció | Az új funkciók beolvadásakor a rendszer automatikusan integrálja a változtatásokat, új buildet hoz létre, és frissíti a fejlesztői környezetet. |
| Tesztadatok | A tesztadatok legyenek elérhetőek egy táblázatban, dátummal ellátva. (Google Sheets) |

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
once_cell = "1.19"

2
engine/src/bitboard.rs Normal file
View File

@@ -0,0 +1,2 @@
mod attackmaps;
mod utils;

View File

@@ -0,0 +1,252 @@
use once_cell::sync::Lazy;
const A_FILE: u64 = 0x0101_0101_0101_0101;
const H_FILE: u64 = 0x8080_8080_8080_8080;
const AB_FILE: u64 = 0x0303_0303_0303_0303;
const GH_FILE: u64 = 0xC0C0_C0C0_C0C0_C0C0;
/*
EXPLANATIONS:
> square_index: 8 * rank number + file number (a-h = 0-7)
> side: white = 0, black = 1
> direction_index: 0..8 = [E, NE, N, NW, W, SW, S, SE]
*/
// KING_ATTACK_MAP[<square_index>]
pub static KING_ATTACK_MAP: Lazy<[u64; 64]> = Lazy::new(|| {
let mut table: [u64; 64] = [0u64; 64];
for sq in 0..64 {
let king: u64 = 1 << sq;
let left_attacks: u64 = king << 7 | king >> 1 | king >> 9;
let right_attacks: u64 = king << 1 | king << 9 | king >> 7;
table[sq] = (left_attacks & !H_FILE) | (right_attacks & !A_FILE) | king << 8 | king >> 8;
}
return table;
});
// PAWN_ATTACK_MAP[<square_index>][<side>]
pub static PAWN_ATTACK_MAP: Lazy<[[u64; 2]; 64]> = Lazy::new(|| {
let mut table: [[u64; 2]; 64] = [[0u64; 2]; 64];
for sq in 0..64 {
let pawn: u64 = 1 << sq;
table[sq][0] |= (pawn << 9) & !A_FILE;
table[sq][0] |= (pawn << 7) & !H_FILE;
}
for sq in 0..64 {
let pawn: u64 = 1 << sq;
table[sq][1] |= (pawn >> 9) & !H_FILE;
table[sq][1] |= (pawn >> 7) & !A_FILE;
}
return table;
});
// KNIGHT_ATTACK_MAP[<square_index>]
pub static KNIGHT_ATTACK_MAP: Lazy<[u64; 64]> = Lazy::new(|| {
let mut table: [u64; 64] = [0u64; 64];
for sq in 0..64 {
let knight: u64 = 1 << sq;
let far_left_attacks: u64 = knight << 6 | knight >> 10;
let near_left_attacks: u64 = knight << 15 | knight >> 17;
let far_right_attacks: u64 = knight << 10 | knight >> 6;
let near_right_attacks: u64 = knight << 17 | knight >> 15;
table[sq] = (far_left_attacks & !GH_FILE) | (far_right_attacks & !AB_FILE) | (near_left_attacks & !H_FILE) | (near_right_attacks & !A_FILE);
}
return table;
});
// RAY_TABLE[<square_index>][<direction_index>]
pub static RAY_TABLE: Lazy<[[u64; 8]; 64]> = Lazy::new(|| {
let mut table: [[u64; 8]; 64] = [[0u64; 8]; 64];
let dirs: [i8; 8] = [1, 9, 8, 7, -1, -9, -8, -7];
for sq in 0..64 {
for d in 0..8 {
let mut ray: u64 = 0u64;
let origin: u64 = 1 << sq;
let mut new_target: u64 = if dirs[d] > 0 {origin << dirs[d]} else {origin >> -dirs[d]};
if [0, 1, 7].contains(&d) {
new_target &= !A_FILE;
}
else if [3, 4, 5].contains(&d) {
new_target &= !H_FILE;
}
while new_target != 0 {
ray |= new_target;
new_target = if dirs[d] > 0 {new_target << dirs[d]} else {new_target >> -dirs[d]};
if [0, 1, 7].contains(&d) {
new_target &= !A_FILE;
}
else if [3, 4, 5].contains(&d) {
new_target &= !H_FILE;
}
}
table[sq][d] = ray;
}
}
return table;
});
// <----- TESTS ----->
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_king_attack_map() {
// test setup for corners [SW, SE, NW, NE]
let corner_indexes: [usize; 4] = [0, 7, 56, 63];
let corner_attack_maps: [u64; 4] = [
(1u64 << 1) | (1u64 << 8) | (1u64 << 9),
(1u64 << 6) | (1u64 << 14) | (1u64 << 15),
(1u64 << 48) | (1u64 << 49) | (1u64 << 57),
(1u64 << 54) | (1u64 << 55) | (1u64 << 62)
];
// tests for corners
for index in 0..4 {
assert_eq!(KING_ATTACK_MAP[corner_indexes[index]], corner_attack_maps[index]);
}
// test setup for sides [S, E, W, N]
let side_indexes: [usize; 4] = [3, 31, 32, 60];
let side_attack_maps: [u64; 4] = [
(1 << 2) | (1 << 4) | (1 << 10) | (1 << 11) | (1 << 12),
(1 << 22) | (1 << 23) | (1 << 30) | (1 << 38) | (1 << 39),
(1 << 24) | (1 << 25) | (1 << 33) | (1 << 40) | (1 << 41),
(1 << 51) | (1 << 52) | (1 << 53) | (1 << 59) | (1 << 61)
];
// tests for sides
for index in 0..4 {
assert_eq!(KING_ATTACK_MAP[side_indexes[index]], side_attack_maps[index]);
}
// test setup for center
let center_index: usize = 27;
let center_attack_map: u64 = (1 << 18) | (1 << 19) | (1 << 20) | (1 << 26) | (1 << 28) | (1 << 34) | (1 << 35) | (1 << 36);
// test for center
assert_eq!(KING_ATTACK_MAP[center_index], center_attack_map);
}
#[test]
fn test_pawn_attack_map() {
// test setup for white sides
let white_side_indexes: [usize; 2] = [24, 31];
let white_side_attack_maps: [u64; 2] = [
(1 << 33),
(1 << 38)
];
// tests for white sides
for index in 0..2 {
assert_eq!(PAWN_ATTACK_MAP[white_side_indexes[index]][0], white_side_attack_maps[index])
}
// test setup for black sides
let black_side_indexes: [usize; 2] = [32, 39];
let black_side_attack_maps: [u64; 2] = [
(1 << 25),
(1 << 30)
];
// tests for black sides
for index in 0..2 {
assert_eq!(PAWN_ATTACK_MAP[black_side_indexes[index]][1], black_side_attack_maps[index])
}
// test setup for white center
let white_center_indexes: [usize; 2] = [11, 12];
let white_center_attack_maps: [u64; 2] = [
(1 << 18) | (1 << 20),
(1 << 19) | (1 << 21)
];
// tests for white center
for index in 0..2 {
assert_eq!(PAWN_ATTACK_MAP[white_center_indexes[index]][0], white_center_attack_maps[index])
}
// test setup for black center
let black_center_indexes: [usize; 2] = [51, 52];
let black_center_attack_maps: [u64; 2] = [
(1 << 42) | (1 << 44),
(1 << 43) | (1 << 45)
];
// tests for black center
for index in 0..2 {
assert_eq!(PAWN_ATTACK_MAP[black_center_indexes[index]][1], black_center_attack_maps[index])
}
}
#[test]
fn test_knight_attack_map() {
// test setup for corners [SW, SE, NW, NE]
let corner_indexes: [usize; 4] = [0, 7, 56, 63];
let corner_attack_maps: [u64; 4] = [
(1 << 17) | (1 << 10),
(1 << 13) | (1 << 22),
(1 << 41) | (1 << 50),
(1 << 46) | (1 << 53)
];
// tests for corners
for index in 0..4 {
assert_eq!(KNIGHT_ATTACK_MAP[corner_indexes[index]], corner_attack_maps[index]);
}
// test setup for sides [S, E, W, N]
let side_indexes: [usize; 4] = [3, 31, 32, 60];
let side_attack_maps: [u64; 4] = [
(1 << 9) | (1 << 13) | (1 << 18) | (1 << 20),
(1 << 14) | (1 << 21) | (1 << 37) | (1 << 46),
(1 << 17) | (1 << 26) | (1 << 42) | (1 << 49),
(1 << 43) | (1 << 45) | (1 << 50) | (1 << 54)
];
// tests for sides
for index in 0..4 {
assert_eq!(KNIGHT_ATTACK_MAP[side_indexes[index]], side_attack_maps[index]);
}
// test setup for center
let center_index: usize = 27;
let center_attack_map: u64 = (1 << 10) | (1 << 12) | (1 << 17) | (1 << 21) | (1 << 33) | (1 << 37) | (1 << 42) | (1 << 44);
// test for center
assert_eq!(KNIGHT_ATTACK_MAP[center_index], center_attack_map);
}
#[test]
fn test_ray_table() {
// test setup for all directions from center
let starting_square_index: usize = 27;
let ray_masks: [u64; 8] = [
(1 << 28) | (1 << 29) | (1 << 30) | (1 << 31),
(1 << 36) | (1 << 45) | (1 << 54) | (1 << 63),
(1 << 35) | (1 << 43) | (1 << 51) | (1 << 59),
(1 << 34) | (1 << 41) | (1 << 48),
(1 << 26) | (1 << 25) | (1 << 24),
(1 << 18) | (1 << 9) | (1 << 0),
(1 << 19) | (1 << 11) | (1 << 3),
(1 << 20) | (1 << 13) | (1 << 6)
];
// tests for all directions from starting_square
for direction in 0..8 {
assert_eq!(RAY_TABLE[starting_square_index][direction], ray_masks[direction]);
}
}
}

View File

@@ -0,0 +1,99 @@
#[inline(always)]
pub fn pop_lsb(value: &mut u64) -> usize {
let idx = value.trailing_zeros() as usize;
*value &= !(1 << idx);
return idx;
}
#[inline(always)]
pub fn pop_msb(value: &mut u64) -> usize {
let idx = 63 - value.leading_zeros() as usize;
*value &= !(1 << idx);
return idx;
}
const RANK_NUMBERS: [char; 8] = ['1', '2', '3', '4', '5', '6', '7', '8'];
const FILE_LETTERS: [char; 8] = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
pub fn notation_from_square_number(sq: u8) -> String {
let row = sq / 8;
let col = sq % 8;
let mut notation = String::new();
let row_not = RANK_NUMBERS[row as usize];
let col_not = FILE_LETTERS[col as usize];
notation.push(col_not);
notation.push(row_not);
return notation;
}
// <----- TESTS ----->
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pop_lsb_test() {
// test setup
let test_values: [u64; 6] = [
0x8000_0000_0000_0000,
0x4E91_CF05_713E_451B,
0xD588_2D58_6962_34B0,
0x9581_3335_DCAB_1DD4,
0xBEAC_DBE0_903A_AC00,
0x01E8_C895_A6F0_0000
];
let expected_values: [usize; 6] = [63, 0, 4, 2, 10, 20];
// tests
for index in 0..6 {
let mut test_value = test_values[index];
assert_eq!(pop_lsb(&mut test_value), expected_values[index])
}
}
#[test]
fn pop_msb_test() {
// test setup
let test_values: [u64; 6] = [
0x86D6_8EB0_96A8_8D1C,
0x0000_0000_0000_0001,
0x3809_24AF_A7AE_8129,
0x0277_DA36_3B31_86D9,
0x0000_C1C3_201C_0DB1,
0x0000_0203_0DE4_E944
];
let expected_values: [usize; 6] = [63, 0, 61, 57, 47, 41];
// tests
for index in 0..6 {
let mut test_value = test_values[index];
assert_eq!(pop_msb(&mut test_value), expected_values[index])
}
}
#[test]
fn notation_from_square_number_test() {
// test setup
let square_indices: [u8; 8] = [1, 12, 22, 27, 32, 47, 53, 58];
let notations: [String; 8] = [
String::from("b1"),
String::from("e2"),
String::from("g3"),
String::from("d4"),
String::from("a5"),
String::from("h6"),
String::from("f7"),
String::from("c8")
];
// tests
for index in 0..8 {
let notation = notation_from_square_number(square_indices[index].clone());
assert_eq!(notation, notations[index]);
}
}
}

View File

@@ -1,3 +1,5 @@
mod bitboard;
fn main() {
println!("Hello, world!");
}