Table of Contents

Prolog: Ultimate Tic-Tac-Toe

Obiective

Organizare

Tema este împărțită în 2 etape:

Așa cum se poate observa, ziua deadline-ului variază în funcție de semigrupa în care sunteți repartizați. Restanțierii care refac tema și nu refac laboratorul beneficiază de ultimul deadline (deci vor avea deadline-uri în zilele de 25.05, 01.06).

Rezolvările tuturor etapelor pot fi trimise până în ziua laboratorului 12 (deadline hard pentru toate etapele). Orice exercițiu trimis după un deadline soft se punctează cu jumătate din punctaj. Cu alte cuvinte, nota finală pe etapă se calculează conform formulei: n = (n1 + n2) / 2 (n1 = nota obținută înainte de deadline; n2 = nota obținută după deadline). Când toate submisiile sunt înainte de deadline, nota pe ultima submisie este și nota finală (întrucât n1 = n2).

În fiecare etapă, veți folosi ce ați învățat în săptămâna anterioară pentru a dezvolta aplicația.

Descriere generală

Veți implementa în Prolog anumite componente dintr-un joc de Ultimate Tic-Tac-Toe. Vom utiliza aceste reguli. Puteți juca jocul aici. Vom numi jocul de Ultimate Tic Tac Toe “UTTT” și un joc obișnuit de Tic-Tac-Toe (X și 0) “TTT”. Vom face referire mai jos la următorul exemplu:

Vom identifica tablele individuale, ca și pozițiile din tablele individuale, prin “pozițiile” (în ordinea parcurgerii de la stânga la dreapta și de sus în jos): NW, N, NE, W, C, E, SW, S, SE:

nw | n | ne
---+---+---
w  | c |  e
---+---+---
sw | s | se

Tabla de UTTT (o vom mai numi și “U-board”) este formată 9 table obișnuite de TTT (numite în cod “board”). O mutare a unui jucător va fi o mutare obișnuită de TTT pe una dintre tablele disponibile pentru mutări (despre asta mai târziu). În exemplul de mai sus, ultima mutare a fost o jucătorului 0 în poziția indicată de săgeata verde, deci jucătorul 0 a mutat în poziția w din tabla individuală care este în poziția n din tabla e UTTT.

Tablele individuale de TTT și tabla de UTTT se câștigă după regulile obișnuite din X și 0. Câștigarea unei table individuale de TTT reprezintă o mutare (un X sau 0) pe tabla mare de UTTT. În exemplul de mai sus, tablele din pozițiile nw, c și se din U-board au fost deja câștigate, de jucătorul x, x și 0, respectiv. Jucătorul 0 este, de asemenea, aproape de a câștiga tablele n și ne.

Când un jucător câștigă pe tabla mare de UTTT, este câștigător al jocului și jocul se termină. În exemplul de mai sus, dacă jucătorul 0 câștigă și tablele ne și e atunci câștigă jocul (dacă nu îl câștigă x înainte).

Tabla sau tablele individuale de TTT pe care poate muta un jucător se decid(e) astfel:

Etapa 1

În această etapă vom implementa câteva predicate care lucrează cu liste. Trebuie implementate construcția și accesul la o stare a jocului și efectuare unei mutări în joc. Pentru bonus, se vor implementa două strategii foarte simple.

Reprezentarea concretă a unei stări este la alegerea fiecăruia. Puteți folosi liste, perechi, sau structuri (compounds).

Vom avea 3 grupuri de predicate (ordinea recomandată de implementare a predicatelor este cea din fișierul sursă):

Pentru BONUS în această etapă se vor implementa două strategii foarte simple:

NOTĂ: pentru majoritatea testelor de la predicatele de acces este necesar ca buildState să fie implementat. Pentru restul (mai puține), este necesar ca initialState să fie implementat. Ideal este să implementați predicatele de acces la reprezentarea stării în același timp cu părțile corespunzătoare din predicatele care construiesc reprezentarea stării.

Hints

Pe parcursul implementării temei, veți găsi foarte utile predicatele nth0/3 și nth0/4.

Pentru a afișa o stare a jocului, folosiți predicatul printBoards/1, iar pentru a afișa o tablă individuală folosiți predicatul printBoard/1. De exemplu, pentru a vizualiza starea inițială (odată ce ați implementat construcția sa), puteți folosi interogarea:

initialState(S), printBoards(S).

Iar pentru a afișa o stare utilizată în teste, puteți folosi, de exemplu (odată ce ați implementat predicatul buildState:

uttt(2, S), printBoards(S).

Testele sunt disponibile în fișierul checker.pl, iar jocurile și listele de mutări folosite în teste sunt disponibile în fișierul input.pl.

Pentru testele de validMove care ies din timp, vedeți acest thread.

Etapa 2

În această etapă vom evalua calitatea mutărilor după un algoritm dat și vom implementa două strategii care folosesc aceste măsuri de calitate.

Calitatea mutărilor într-o tablă individuală

Într-o tablă individuală, vom avea următoarele priorități pentru mutări (mutarea cu cea mai mare calitate va fi cea de prioritate minimă):

Predicatul movePriority/4 evaluează prioritatea unei mutări, pentru un jucător, pentru o tablă individuală.

Predicatul bestIndividualMoves/3 ordonează mutările disponibile într-o tablă individuală în funcție de prioritatea lor pentru jucătorul curent. Ordinea apriori a mutărilor este cea din lista positions. Două mutări cu prioritate egală își păstrează ordinea apriori. Folosiți pentru sortare sortMoves/2.

În exemplu, pentru tabla w, cele mai bune mutări sunt, în ordine, [c, ne, sw, se, n, w, e, s], pentru că:

Strategia narrowGreedy va întoarce o mutare bazată pe următorul algoritm: dacă este o singură tablă disponibilă, se va alege mutarea cu cea mai mică prioritate (sau prima mutare cu cea mai mică prioritate); dacă sunt mai multe table disponibile, se alege tabla cu cea mai mică prioritate, și în ea mutarea cu cea mai ică prioritate.

Calitatea mutărilor în jocul UTTT

Pentru a evalua mutările la nivelul întregului joc de UTTT, vom considera următoarea ordonare a priorităților:

Predicatul bestMoves/2 ordonează mutările disponibile în ordinea prezentată mai sus. Pentru stările în care sunt mai multe table disponibile pentru jucătorul curent, mutările vor fi luate apriori (înainte de sortarea după priorități) după ordonearea tablelor individuale conform cu bestIndividualMoves/3.

NOTĂ: când calculați cele mai bune mutări, în moemntul evaluării unei anumite mutări M, atunci când facem referire la ce va face mai departe un alt jucător (sau același jucător), faceți evaluarea pe starea în care mutarea M s-ar fi aplicat deja.

În exemplu, pentru x, cele mai bune mutări (după această strategie) sunt, în ordine, [sw, w, e, s, n, ne, c, se], pentru că:

Strategia greedy va întoarce cea mai bună mutare (prima) din mutările întoarse de bestMoves/2.

Bonus etapa 2

Pentru puncte bonus, implementați cât mai elegant, folosind findall, forall, și având reguli diferite doar atunci când este neapărat nevoie (e.g. pentru calculul priorităților pe diferite cazuri).

Hints

Precizări

Resurse

Changelog