Skocz do zawartości

Grająca Szachownica


okjak

Pomocna odpowiedź

Podoba Ci się ten projekt? Zostaw pozytywny komentarz i daj znać autorowi, że zbudował coś fajnego!

Masz uwagi? Napisz kulturalnie co warto zmienić. Doceń pracę autora nad konstrukcją oraz opisem.

@okjak witam na forum i dziękuję za opis ciekawego projektu! Miło widzieć, że nauka z naszych kursów przynosi takie efekty 🚀

  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

O nie! Kolega mnie uprzedził 😄 od dłuższego czasu myślę nad szachownicą, ale okazało się za drogie.

Cytat

Zaimplementowałem mini-max z przycinaniem alpha-beta i iteracyjnym pogłębianiem.

Wow, interesujące! A mógłbyś napisać nieco więcej na ten temat? Też zastanawiałem się nad algorytmem i doszedłem do API szachownicy, ale Twój pomysł jest dużo bardziej ambitny.

Cytat

Skoro już umiałem testować pojedyncze pole, nadszedł czas na zaprojektowanie płytek do samej szachownicy. Tu użyłem ekspandera wyprowadzeń MCP23017 oraz demultiplexera MC74HC154.

Też ciekawy pomysł, choć czy nie dało się uniknąćekspandera?

Miałem kilka planów odnośnie szachownicy. Wymyśliłem najpierw że pola będą z PCB (64 PCB na pola i 64 na pionki... cena kosmiczna). Na polach i pionkach miały być koncentryczne pola stykowe łączone przy pomocy sprężynowych test pinów i dociskane magnesami. Każdy pionek miał mieć w sobie komórkę adresowaną np z 1-wire albo I2C, a najlepiej małego attina żeby dało się przy starcie gry przypisywać rolę i stronę dla pionka. Ale właśnie jak określić specyficzny kształt (typ pionka)... Tu można by zrobić pionki al'a warcaby (albo japońskie shogi) z małym wyświetlaczem.

To prostszy pomysł, niestety bez inteligentnych pionków z mikrokontrolerem tylko magnesik i hallotrony. Kupiłem dla testu kilka wersji hallotronów z otwartym kolektorem i złożyłem w multipleksowaną matrycę i efekt był świetny - 16 połączeń i tyle 🙂 czysta plansza nie wzbudzająca podejrzeń i cena też dość niska.

Ten pierwszy pomysł był wodotryskiem ale zabijała go cena PCB, koszt jednej sztuki to byłby ponad 1000zł. Drugi był już realny ale wtedy przyszła korona i nie dało się robić PCB 😞 Projekt na razie zawiesiłem ale ciesze się że jest ktoś kto tak łatwo się nie poddał 🙂 Super sprawa, powodzenia w dalszych projektach! 🙂 

Edytowano przez Gieneq
  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

Hej, dziękuję bardzo 🙂

O samym algorytmie mogę napisać bardzo dużo, więc odpowiem chętnie jak będziesz miał bardziej szczegółowe pytania. Sercem algorytmu jest min-max, często używany w grach dwuosobowych o sumie zerowej (lepiej dla Ciebie to gorzej dla przeciwnika i vice versa). Sam min-max jest algorytmem typu brute force, przeszukuje drzewo ruchów na każdym poziomie szukając ruchu który da najlepszy rezultat dla gracza który go wykonuje. Zakładamy że każdy gracz wykonuje ruch najlepszy dla siebie, zatem Twój najlepszy ruch to taki dla którego sytuacja na szachownicy po najlepszym ruchu Twojego przeciwnika jest dla Ciebie najlepsza. Oczywiście nie możemy analizować w ten sposób drzewa gry zbyt głęboko bo średni współczynnik rozgałęziania dla szachów to 35, dlatego na ustalonym z góry poziomie głębokości sytuację na szachownicy oceniamy jakąś prostą (mniej lub bardziej) heurystyką. W przypadku mojego algorytmu to proste zliczanie punktów bierek, przy czym bierki oceniane są nie tylko pod względem swojej wartości, ale również pozycji (np. biały pion stojący na linii 7-mej ma większą wartość niż stojący na linii drugiej, bo zaraz możemy go wymienić na hetmana. Z ciekawostek w mojej implementacji aby uniknąć kosztownych wywołań funkcji i powrotów oraz w celu oszczędzania wielkości stosu rekurencja została "rozwinięta". Trzymam tablicę obiektów reprezentujących stan przeszukiwania na danym poziomie i iteruję się po drzewie w ramach jednej pętli while.

Jak widać w min-max mamy rekurencję i to o dużym poziomie rozgałęziania, stąd goły min-max ma zastosowanie raczej czysto akademickie. Przycinanie alpha-beta jest usprawnieniem min-max pozwalającym obcinać gałęzie przeszukiwania w sytuacji gdy ruch który już znaleźliśmy jest lepszy niż najlepszy jaki moglibyśmy z danej gałęzi uzyskać. Czy jak można się spodziewać jego wydajność (ilość obciętych gałęzi przeszukiwania) zależy od tego jak szybko odnalezione zostanie "dobre" (niekoniecznie najlepsze) rozwiązanie, dlatego można go wspomagać różnymi heurystykami. Więcej o alfa-beta znajdziesz choćby na wikipedii: https://pl.wikipedia.org/wiki/Algorytm_alfa-beta.

Jedną z heurystyk dających szansę na znalezienie w miarę szybko dobrego ruchu iteracyjne pogłębianie, które w skrócie polega na tym że najpierw przeszukujemy drzewo w poszukiwaniu najlepszego ruchu do niewielkiego poziomu (u mnie 3: ruch szachownicy, ruch przeciwnika, kolejny ruch szachownicy). W wielu przypadkach będzie to już całkiem niegłupi ruch który przy głębszym przeszukaniu może ale nie musi okazać się najlepszym. Następnie przeszukujemy drzewo do pełnej już (założonej) głębokości rozpoczynając od tego znalezionego w pierwszej iteracji ruchu - to daje szansę na obcięcie znacznie większej ilości gałęzi niż gdybyśmy zaczynali od losowego czy pierwszego możliwego ruchu. W mojej implementacji zastosowałem dodatkowo podrzucanie najlepszych ruchów również na niższych poziomach drzewa tzn: jeśli w pierwszym przeszukaniu do głębokości 3 najlepszy ruch szachownicy to X, dla którego najlepszym Twoim ruchem jest Y, a kolejnym najlepszym ruchem szachownicy Z, to w drugiej iteracji zaczynam przeszukiwać drzewo podrzucając w pierwszej kolejności te 3 ruchy na odpowiednich poziomach drzewa. Dodatkowo zmniejszam głębokość przeszukiwania jeśli nie udało mi się go skończyć w rozsądnym czasie (przyjąłem 90s), dzięki czemu nie czekamy długo na odpowiedź szachownicy w trudnych do analizy sytuacjach - troszkę to też "uczłowiecza" algorytm bo w trudnych sytuacjach jest większa szansa na zrobienie troszkę głupszego ruchu z jej strony

Kolejnym usprawnieniem, które testowałem ale ostatecznie NIE użyłem jest iteracyjne wewnętrzne pogłębianie. Jak będziesz chciał to powiem o tym coś więcej, póki co tylko tyle że przy jego użyciu często ocenia się wielokrotnie tą same sytuacje, więc aby w pełni wykorzystać jego zalety przydałaby się tzw. tablica transpozycji (hash mapa [pozycja]->[wartość]) której w żadnych rozsądnych rozmiarach nie da się zaimplementować dysponując 2000B w Arduino Uno/Mini.

Z innych ciekawostek nie mogłem sobie pozwolić na tworzenie tablicy legalnych ruchów, a to również z przyczyn pamięciowych. Ruch może być reprezentowany przez nie mniej niż 3B, a istnieją sytuacje w których ilość możliwych ruchów dochodzi do 218, co dawałoby 654B na samą tylko tablicę ruchów na danym poziomie. Musiałem więc zaimplementować iterator po legalnych ruchach co samo w sobie jest pewnie na kilka akapitów, więc niepytany tu się zatrzymam.

 

A co do elektroniki 🙂

Wpadłem również na pomysł z halotronami ale już po tym jak kupiłem 70 (kilka na zapas) SG-2BC, a że nie były tanie to już nie zmieniałem koncepcji 🙂 Moja szachownica również nie rozpoznaje figur jakie na niej stoją. Zakłada że ustawiasz szachy poprawnie, a następnie śledzi ich ruchy. I wściekle miga diodą sygalizującą Twój ruch jeśli wykonasz ruch niepoprawny 🙂 Jeśli chodzi o I2C to jest to u mnie jedyna forma komunikacji mikrokontrolera  z oprzyrządowaniem szachownicy i cieszę się że go użyłem. To bardzo sprytny protokół, ponieważ urządzenia slave nie mogą wystawiać stanu wysokiego, mogą go tylko ściągać do GND, dzięki czemu możliwa jest komunikacja komponentów pracujących na różnych napięciach. A dzięki temu np. wymieniłem ostatnio swoje pro mini (robię teraz na nim zabawkę dla dziecka, którą może się później pochwalę) na Teensy 4.0 które pracuje na 3V podczas gdy całe oprzyrządowanie szachownicy mam policzone dla 5V. Przepięcie się na Teensy było przepięciem 2 kabelków i dodanie dwóch pull-up rezystorów dla obu linii I2C - reszta pracuje jak dotychczas:

IMG-8785.thumb.jpg.1365e4cb48ce5139ddbf0e4eaf1a0db2.jpg

W Teensy mam również możliwość sterowania zegarem procesora (do 600MHz!), więc ustawiam go domyślnie na 12MHz jak Arduino, a przestawiam na 600MHz wyłącznie na czas "myślenia" nad ruchem przez szachownicę (zjada wtedy 100mA). Swoje płytki robiłem już w korona-czasie, Satland pracuje! 🙂

Dzięki za miłe słowa i pozdrawiam!

Okjak
 

 

 

Edytowano przez okjak
  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Masz na myśli aby szachownica myślała nad swoim ruchem zanim ruch wykona przeciwnik? Nie myślałem o tym, ale teraz jak o tym wspomniałeś widzę kilka zalet i wad:
Zalety:
- możliwa szybka odpowiedź na ruch jeśli zdążyłem już przeanalizować odpowiedzi na ruch który użytkownik wykonał (ale z Teensy 4.0 pomimo zwiększenia głębokości przeszukiwań o dwa poziomy, średni czas myślenia szachownicy nad ruchem to kilka sekund),

Wady:

- marnowałbym trochę prąd z baterii rozpatrując odpowiedzi na wszystkie możliwe ruchy przeciwnika, zamiast jedynie na ten który rzeczywiście zrobi,

- komplikacja programu: obecnie myślenie nad ruchem to wywołanie synchronicznej funkcji chess_engine::getTheBestMove() - do czasu jej zakończenia nie mógłbym skanować szachownicy, a wykrywanie ruchu użytkownika to intensywne skanowanie szachownicy (np. w przypadku bicia różnica stanu binarnego to jedynie zniknięcie bierki z jednego pola, aby wykryć że było to bicie muszę zauważyć że w międzyczasie zniknęła też na chwilę bierka z pola na którym była bita figura). Aby to rozwiązać musiałbym przenieść wykrywanie ruchu użytkownika do obsłgi przerwania,

- zwięszone zużycie pamięci: ruch to jak wspominałem 3B, gdybym chciał zapamiętać do późniejszego wykorzystania odpowiedzi na wszystkie możliwe ruchy przeciwnika, musiałbym odłożyć sobie na stosie na to 654B (alokowanie dynamiczne i re-alokowanie w Arduino to proszenie się o problemy).

 

Biorąc pod uwagę powyższe raczej nie będę szedł w tą stronę, tym bardziej że jedyny problem który by to rozwiązało (szybkość odpowiedzi) nie istnieje już na Teensy.

 

Pozdrawiam, Okjak

Link do komentarza
Share on other sites

Według mnie warto przejść na przerwania. 

Warto też zastanowić się nad STM32H7. Będziesz miał wtedy dużo więcej pamięci do dyspozycji.

Odnośnie myślenia przy ruchu przeciwnika, to przy zasilaniu bateryjnym może być problem.

Ale jeśli będą to akumulatory i wystarczą na partię, to na pewno warto.

Można wtedy spróbować zejść o poziom głębiej z analizą. 

A Ty jak grasz w szachy to myślisz przy ruchu przeciwnika? 

Generalnie gra w szachy to gra na czas. 

Gratuluję pomysłu i konsekwencji w realizaji. Naprawdę super projekt.

Zarówno sprzętowo jak i programowo. 

Link do komentarza
Share on other sites

(edytowany)

STM32H7 jest porównywalny z Teensy 4, ten sam Cortex M7, 1MB RAM.

Rzeczywiście mógłbym teraz wykorzystać więcej RAMu, który daje mi Teensy. Na pewno będę projekt rozwijał jak tylko zacznę z nim wygrywać - póki co udało mi się to zaledwie 2 razy.

Duża część uroku tej szachownicy to dla mnie jej tradycyjna forma, więc wszelkie kable wychodzące po bokach odpadają. Używam obecnie akumulatorka 9V, masz na myśli inny który zmieściłby mi się w szachownicy? Może dwie 18650?

Oczywiście że myślę nad ruchem podczas tury przeciwnika, ale z drugiej strony ciężko porównać mnie do komputera, analityczne procesy przeprowadzam na pewno wolniej, ale jestem z pewnością nieco bardziej kreatywny 😉 Choć to co wyprawiają w szachach ostatnio sieci neuronowe jest też niesamowite.

Szczerze mówiąc nie traktuję szachów jako grę na czas, przynajmniej nie w podstawowej formie. W czasach licealnych grałem w klubie szachowym turnieje blitza ale nigdy nie była to moja ulubiona forma gry w szachy.

Wielkie dzięki za sugestie i miłe słowa 🙂

Edytowano przez okjak
  • Lubię! 1
Link do komentarza
Share on other sites

Zapis do EEPROM może się przydać też podczas gry z drugą osobą, jakby trzeba było nagle przerwać. Trzeba tylko znaleźć sposób zaprezentowania, gdzie trzeba ustawić bierki, jeśli były złożone. Najprościej (bez żadnych modyfikacji hardware'u) byłoby albo odczytywać zawartość pamięci za pomocą komputera (i ew. napisać działający na nim program do pokazywania zapisu w formie graficznej), albo ustalić jakąś kolejność, w której poszczególne bierki będą ustawiane na szachownicy - dioda będzie pokazywać, na którym polu postawić jedną z nich, po wykryciu postawienia przez czujnik będzie się zaświecać następna.

  • Lubię! 1
Link do komentarza
Share on other sites

Wydaje mi się że do gry z drugą osobą łatwiej może być zrobić zdjęcie komórką, szachownica nie musi znać położenia pionków, nie musi być nawet włączona 🙂

Jeśli chodzi o elektromagnesy - czemu szachownica miałaby przyciągać pionki? Czy może masz na myśli mechanizm który ruszałby pionkami za pomocą elektromagnesów?

Link do komentarza
Share on other sites

Dzięki Yubi 🙂

Wiedzę elektroniczną zdobywałem w trakcie projektowania i prototypowania, kursy Forbota dały mi na prawdę fajny start. Potem już noty katalogowe, tani oscyloskop i debugowanie problemu po problemie, aż się skończyły problemy 😉 Programowaniem zajmuję się od dzieciaka i zawodowo od 16 lat, skończyłem też studia informatyczne z (między innymi) specjalnością AI, więc sam engine szachów zajął mi mniej więcej czas oczekiwania na realizację zaprojektowanych płytek - dzięki temu też mi się tak nie dłużyło 😛

 

 

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.