Like

Napisanie gry w kółko i krzyżyk to efekt wyzwania, które rzucił mój kolega, Robert Celiński. To ambitny chłopak, który pisze swoje hobbystyczne apki ze świetnymi funkcjami i grafiką z koszmaru hipstera dizajnera.

Postarałem się napisać to tak by jak najwięcej rzeczy załatwić przy pomocy standardowych funkcji Google Sheets. Dwa któtkie skrypty odpowiadają tylko za resetowanie planszy i wstawianie ruchów komputera.

Zagrać możesz korzystając z kopii pliku roboczego.

Natomiast sama rozgrywka wygląda tak:

Taktyka gry (nie odważę się nazwać jej sztuczną inteligencją) myśli tylko jeden ruch w przód. Dobry gracz jest w stanie ograć ten program. Jest jeden sposób (przynajmniej ja znam jeden) i nie blokowałem go, byście mogli sami go wypróbować.

Ustawiam planszę

To najprostszy fragment. Przy pomocy formatowania obramowań rysuję planszę.

Ustawiam jednostronne cienkie obramowania by wiadomo było gdzie będziemy działać.

Drugi krok to zawężenie możliwości wpisywania znaków do komórek w których będzie odbywać się gra. Chcemy by arkusz dopuszczał użycie tylko dwóch znaków: x i o. Możemy to osiągnąć korzystając z funkcji w Menu –> Dane —> Sprawdzanie poprawności danych.

Wybieramy interesujący nas obszar – w tym wypadku zakres B4:D6 i jako kryteria ustalamy niestandardową formułę.

=or(B4="x";B4="o")

Zasada działania jest taka: jeśli warunek niestandardowej formuły zostanie spełniony, arkusz dopuści znak do użycia. Gdybyśmy chcieli dopuścić np. tylko x to napisalibyśmy =B4=”x”. a ponieważ funkcja działa w całym zakresie, to jest kopiowana na pozostałe komórki i zmieniany jest adres. Zatem dla komórki B5 niestandardowa formuła wyglądałaby: =B5=”x”. To zaleta adresowania względnego i ogólna zasada działania niestandardowych formuł ustawianych dla całych zakresów (warto to wiedzieć przy formatowaniu warunkowym).

No więc mamy formułę OR (LUB), która daje wartość PRAWDA jeśli spełniony jest choć jeden z warunków opisanych wewnątrz niej.

Ustalam ogólną strategię komputera

Ten fragment zaczerpnąłem z opisu gry w Wikipedii. Mówią tam o tym które pozycje na planszy dają największe możliwości manewru. Np. jeśli postawimy kółko na środkowym polu, możemy wygrać aż na 4 sposoby (dwa sposoby na skos, w poziomie i w pionie).

W przypadku stawiania kółka w rogu, sposoby są dwa (poziom, pion, jeden ze skosów),

W przypadku stawiania kółka na pozstałych polach – mamy dwie możliwości (pion i poziom).

Obrazek z wikipedii ilustrujący liczbę możliwych sposobów na wygraną dla każdego z pól.

Na bazie tych informacji tworzę tabelę, która będzie podpowiadać komputerowi jak ma grać (na razie nie biorąc pod uwagę ruchów przeciwnika). Jeśli środek będzie pusty, to on tam właśnie postawi swój krzyżyk, bo widzi najwyższą wartość. Jeśli środek będzie zajęty, to wstawi krzyżyk na jednym z rogów.

Na razie informacje dla komputera są tylko w tej małej tabelce. Do nich będę za chwilę dodawał inne czynniki i wszystko razem sumował. Ale póki co, suma wszystkich czynników uwzględnianych przez komputer to tylko ta ogólna taktyka.

Nazwałem tą tabelę ogolnainicjatywa. Są tam dane z wikipedii, ale również informacja o tym czy na planszy nic już nie stoi. Bo jeśli stoi to w tej tabeli mamy zero.

Ta formuła ustala, że jeśli lewy górny róg planszy (B4) jest pusty, to ma tu wskoczyć 3, a jak tam coś jest to 0. Takie same ustawienia są dla pozostałych pól. Tylko oczywiście wartości inne.

Tabelka: propozycja dla komputera:

Została zrobiona po to, by komputer sprawdzał, które pola są już zajęte oraz żeby decydować w które pole zostanie wstawiony krzyżyk. Tu również roztrzygany jest problem “co zrobić, gdy kilka zagrań jest równie dobrych”. Jeśli w tabeli sumastrategii (to ta w której bierzemy pod uwagę zarówno ogólną taktykę jak i reakcje na ruchy przeciwnika i dążenie do wygranej) pojawi się kilka równorzędnych rozwiązań, komputer wstawi liczby losowe z zakresu 1 do 1 000 000 do tabeli propozycjedlakompa i w następnym kroku wybierze większą z nich i wrzuci sobie do tabeli cozrobikomputer.

Wszystkie te tabelki mogą być widoczne w czasie gry i są podpisane, więc jak będziecie grali – zerknijcie co się w nich pojawia.

W wierszu formuły jest objaśniony mechanizm działania formuły. Jest ona skopiowana dla wszystkich pól.

Komputer obmyśla swój ruch niezależnie od tego czy jest aktualnie jego kolej czy nie. Na powyższym obrazku jest pusta plansza i kolej na kółka (czyli na człowieka), ale komputer już wie, że najchętniej by postawił krzyżyk na środku. (ma najwyższą liczbę w sumie strategii) i pomysł na ruch widzimy w tabeli cozrobikomputer.

Uczę komputer walczyć o wygraną

Chcemy by komputer dążył do wygranej. Czyli do ułożenia trzech krzyżyków w jednej linii. Poproszę go zatem, żeby sprawdzał dla każdego pola, ile ma już krzyżyków w każdej z osi i wypisał to w tabeli wlasnezakonczenie.

Komputer analizuje dla każdej komórki wszystkie osie na których mógłby stawiać krzyżyki i sprawdza ile krzyżyków już tam jest.

Walka o wygraną będzie najważniejszym czynnikiem. Ważniejszym niż obrona, bo często możemy obronić się przed atakiem, samemu kończąc grę zwycięstwem. Tak przynajmniej przyjąłem w tej taktyce. Jak to zostanie zrealizowane, zobaczycie w dalszej części.

Uczę komputer się bronić

Komputer musi też “patrzeć” co robi przeciwnik, czyli człowiek operujący kółkami. Służy do tego tabela ochronaprzedprzeciwnikiem. W niej dla każdego pola podliczane są ilości kółek w poszczególnych osiach. Im więcej tych kółek tym ważniejsze by stawiać tam swoje krzyżyki i blokować przeciwnika.

Komputer podejmuje decyzję

To co zrobi komputer to suma trzech czynników – ogólnej możliwości inicjatywy (chętniej będzie stawiał krzyżyk na środku planszy niż w połowie boku), dążenia do wygranej i obrony przed przeciwnikiem (czyli człowiekiem).

=P10+P14*P14*P14+P18*P18

Najważniejsze jest jednak dążenie do wygranej. Stąd wartość z tabeli poszukiwania własnego rozwiązania jest podniesiona do sześcianu (P14*P14*P14), na drugim miejscu jest obrona – dlatego w algorytmie podnieśliśmy ją do kwadratu (P18*P18), a na ostatnim miejscu jest ogólna inicjatywa (po prostu dodana).

W ten prosty algorytm to serce taktyki komputera. W początkowej fazie gry nie ma na planszy wielu kółek i przyżyków, stąd wartości obrony i ataku są niskie (podniesienie 1 do sześcianu ciągle daje 1). Natomiast, gdy jest więcej krzyżyków i kółek, obrona i atak stają się kluczowe. 2 do sześcianu to 8, więc na pewno będzie istotniejsze niż współczynnik ogólnej inicjatywy, który maksymalnie wynosi 4.

Dla każdego pola tabeli sumastrategii jest robione podsumowanie wartości z tabel powyżej wedle zadanego algorytmu.

Jak już wspominałem wcześniej – jeśli suma strategii dla kilku pól będzie taka sama, komputer rozstrzygnie ten problem za pomocą losowania liczby z zakresu 1 – 1 000 000.

Pozwalam komputerowi postawić krzyżyk i rozegrać turę

W tabeli podpisanej “Co zrobi komputer” widzimy, że on zawsze jest gotowy do ruchu i wiemy co zrobi w kolejnej kolejce. Natomiast Arkusze Google nie są w stanie czekać na rozkaz, żeby skopiować te plany na plansze gry. Dlatego muszę posiłkować się tutaj skryptem.

Ale zanim on ruszy, musi wiedzieć czyja jest teraz kolej. Dlatego podliczam ile jest na planszy kółek i krzyżyków, oraz zapisuję kto zaczął partię.

Do podliczania używam formuły COUNTIF

=countif(B4:D6;"x")

daje mi informację ile komórek z zakresu B4:D6 jest krzyżykami

Skrypt ma ustawiony tzw. trigger – uruchamia się za każdym razem gdy cokolwiek jest modyfikowane w arkuszu, ale żeby nie rozrabiał, to pobiera również informację czy w danym momencie jest jego kolej czy nie. Ustalane jest to na podstawie tego ile jest na planszy kółek i krzyżyków, oraz tego kto zaczynał. Poniżej jest cały ten skrypt. Można go podejrzeć wchodząc przez Menu –> Narzędzia –> Edytor skryptów

function komputergra() {  // tu definiuję nazwę funkcji i otwieram klamrę funkcji. Ta funkcja jest ustawiona by aktywować się przy każdej modyfikacji arkusza.
  var arkusz = SpreadsheetApp.getActive(); // Definiuję zmienną arkusz, 
                                           // która jest aktywnym arkuszem w aplikacji Arkusze Google
  var ktonastepny = arkusz.getRange('W18').getValue();     // sprawdzam kto zrobił ostatni ruch
  var tabela = arkusz.getRange('L4:N6').getValues();  //Pobieram wartości 
                                                           //komórek L4:N6 (czyli tabeli w której zapisany jest proponowany przez komputer ruch.)
                                                           //opisuję je jako zmienna 'tabela'.
  if (ktonastepny == "x") {                   // ustawiam warunek, że jeśli jest kolej komputera to może on podmienić tabelę. 
  arkusz.getRange('B4:D6').setValues(tabela); // Wpisuję pobrane wartości do komórek B4:D6 
                                                 // (czyli do tabeli aktualnej)
    }
    
} // Zamykam klamrę funkcji.

Sprawdzam czy ktoś wygrał

Warto też wiedzieć, czy przypadkiem gra się już nie zakończyła. W tym celu zbudowałem tabelę, która cały czas “ogląda” wszystkie osie planszy i sprawdza czy nie ma tam trzech takich samych znaków.

Dla każdej osi zbieram te informacje za pomocą funkcji join (opisanej szerzej we wcześniejszym spisie). Formuła spisuje napotkane znaki.

Następnie sprawdzam czy nie ma tam frazy xxx lub ooo i daję znać jeśli się trafią.

Funkcja SWITCH sprawdza dla zakresu U4:U11 czy nie ma tam szukanych ciągów.

Istotne jest też by sprawdzić czy wszystkie pola są pełne. W tym celu za pomocą funkcji COUNTIF liczę puste pola na planszy.

Jeśli ich liczba będzie równa 0, a nigdzie nie znajdą się 3 krzyżyki lub 3 kółka pod rząd, oznaczać to będzie remis.

Czyszczę planszę by zacząć nową grę

Do wyczyszczenia planszy niestety również potrzebny jest skrypt. Wklejam go poniżej wraz z objaśnieniem.

function nowagra() {   // tu definiuję nazwę funkcji i otwieram klamrę funkcji.
  var arkusz = SpreadsheetApp.getActive();// Definiuję zmienną arkusz, 
                                           // która jest aktywnym arkuszem w aplikacji Arkusze Google
  var kompzacznie = arkusz.getRange('b14').getValue(); // sprawdzam jak ustawiona jest kolejność (kto zaczyna)
    arkusz.getRange('B4:D6').clearContent(); // Czyszczę zawartość planszy
  
  if ( kompzacznie == true ) { // jeśli ustalone jest, że zaczyna komputer (x)
    arkusz.getRange('W14').setValue("x"); // to wpisuję to w arkusz, żeby później ustalać czyja kolej.
    komputergra();
  } else {
    arkusz.getRange('W14').setValue("o");} // jeśli ustalone jest, że zaczyna człowiek (o), to też to wpisuję.
 }

Autoryzacja skryptów i zabezpieczenia Google, czyli jak to odpalić u siebie

Niestety Google jest dość upierdliwe jeśli chodzi o skrypty i żeby uruchomić po raz pierwszy na swoim komputerze jakikolwiek skrypt, należy przejść przez serię złowrogich okienek wzbudzających sporo niepokoju. Ma to na celu zapewnienie bezpieczeństwa, bo skrypty mogą sporo namieszać na naszym dysku google, jeśli się im nie przyjrzymy. Dlatego też opisuję przy każdej linijce co dokładnie robi mój skrypt. No i unikam ich na tym blogu jak tylko mogę.

Przebieg autoryzacji:

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.