gambit

Silnik szachowy w Scali 3. Generowanie ruchów, ocena pozycji i przeszukiwanie - kompletny mózg do gry w szachy, budowany scala-cli.

gambit
TL;DR

Silnik szachowy w Scali 3. Trzy trudne rzeczy naraz: bezbłędne generowanie ruchów, sensowna ocena pozycji i przeszukiwanie, które w rozsądnym czasie wybierze dobry ruch. Kompletny mózg do gry.

Wprowadzenie

Silnik szachowy tylko z pozoru jest jednym projektem. To trzy podsystemy, z których każdy może zepsuć całość na własny sposób, i dopiero razem dają coś, co gra sensownie. gambit jest o tym, żeby te trzy części zgadzały się ze sobą na tyle dobrze, że wynik zaczyna przypominać grającego przeciwnika, a nie generator losowych ruchów.

Scala 3 daje tu wyrazisty język do modelowania planszy, ruchów i drzewa wariantów. Ale prawdziwe wyzwanie nie jest składniowe, tylko algorytmiczne: bezbłędny generator, uczciwa ocena i przeszukiwanie, które nie liczy w nieskończoność.

Trzy problemy, które muszą się zgadzać

Generator ruchów, który raz na milion pozycji przepuści nielegalny ruch, sprawi, że silnik zagra coś niemożliwego. Ocena, która źle waży figury, poprowadzi go w przegraną z przekonaniem. Przeszukiwanie bez odcięć będzie myśleć godzinami nad ruchem, który ma paść w sekundę.

1
Generowanie ruchów

wszystkie legalne ruchy w każdej pozycji, łącznie z roszadą, biciem w przelocie i promocją.

2
Ocena pozycji

liczba obrazująca, kto stoi lepiej, gdy partia jeszcze trwa.

3
Przeszukiwanie

alfa-beta, które odrzuca gałęzie, których nie opłaca się liczyć.

Żadnego z tych trzech nie da się zbyć. Słaby w którymkolwiek z nich silnik przegra, i to z konkretnego, dającego się wskazać powodu, a nie z ogólnej słabości.

Alfa-beta, czyli sztuka nieliczenia

Naiwne przeszukiwanie sprawdza każdą możliwą kontynuację, a tych są miliardy już na kilka ruchów w głąb. Odcięcie alfa-beta zauważa, że jeśli jedna odpowiedź przeciwnika już obala wariant, reszty jego odpowiedzi nie trzeba liczyć. Przy dobrym porządku ruchów tnie to przestrzeń tak mocno, że silnik sięga głębiej w tym samym czasie.

Węzły do przeszukania na tej samej głębokości (orientacyjnie, tys.)

Naiwne przeszukiwanie
1000
Z odcięciem alfa-beta
60

To nie jest drobna optymalizacja, tylko różnica między silnikiem, który widzi trzy ruchy do przodu, a takim, który widzi sześć. W szachach ta głębia przekłada się wprost na siłę gry.

Perft, czyli dowód poprawności generatora

Poprawność generatora ruchów weryfikuje się tak zwanym perft: liczysz wszystkie pozycje osiągalne na zadaną głębokość i porównujesz z liczbami, które cała społeczność szachowa zna na pamięć. Jeśli twoja liczba się nie zgadza, masz gdzieś buga, i to dokładnie widać, na której głębokości rozjazd się zaczyna.

Perft z pozycji startowej

GłębokośćLiczba pozycji
120
2400
38902
4197281
54865609

Efektem jest kompletny silnik, który bierze pozycję i oddaje ruch: generator wystawia wszystkie legalne kontynuacje, ocena mówi, która prowadzi w lepszą stronę, a alfa-beta przeszukuje na tyle głęboko, na ile pozwala czas. Trzy podsystemy, które z osobna są tylko częściami, razem dają przeciwnika, który potrafi zagrać.

➜
Wskazówka

Perft jest wzorcowym przykładem testu, który naprawdę może paść. Nie sprawdza, że coś działa, tylko porównuje twoją liczbę z liczbą znaną z góry, więc każdy błąd w generatorze natychmiast rozjeżdża wynik.

Więcej projektów

Inne realizacje z tej samej kategorii - zobacz, jak podchodzimy do podobnych wyzwań.

Masz podobny projekt?

Napisz do nas - wycena jest bezpłatna i wraca w godzinę.