This is an old revision of the document!


Prolog: Ultimate Tic-Tac-Toe

  • Data publicării: 10.05.2022
  • Data ultimei modificări: 10.05.2022
  • Deadline hard: ziua laboratorului 12
  • vmchecker - în curând

Obiective

  • Aplicarea mecanismelor oferite de Prolog pentru implementarea unor funcții clasice, dar și pentru fluxul bidirecțional al controlului.
  • Exploatarea mecanismului de backtracking pe care îl oferă motorul de execuție Prolog.

Organizare

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

  • una pe care o veți rezolva după laboratorul 10, cu deadline dependent de ziua în care aveți laboratorul 11:
    • laborator marți ⇒ deadline 17 mai
    • laborator miercuri ⇒ deadline 18 mai
    • laborator joi ⇒ deadline 19 mai
    • laborator vineri ⇒ deadline 20 mai
    • laborator luni ⇒ deadline 23 mai
  • una pe care o veți rezolva după laboratorul 11, cu deadline la o săptămână după deadline-ul etapei 1m în ziua laboratorului 12.

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 23.05, 30.05).

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ătrului 0 în poziția indicată de săgeata verde, deci jucătorul 0 a mutat în poziția e 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:

  • inițial, primul jucător poate muta (pune un X sau un 0) pe oricare dintre cele 9 table individuale.
  • în mod obișnuit, un jucător este obligat să își realizeze mutarea pe tabla cu aceeași poziție (în cadrul U-board-ului) cu poziția pe care tocmai a mutat oponentul său în cadrul unei table individuale. În exemplul de mai sus, cum jucătorul 0 tocmai a mutat pe poziția w a unei table individuale, jucătorul x va trebui acum să mute într-una din pozițiile libere din tabla w.
  • în cazul în care tabla pe care ar trebui să joace un jucător, atunci când îi vine rândul, a fost deja câștigată (și deci nu se mai pot face mutări pe ea), jucătorul care este la rând va putea muta pe oricare dintre tablele individuale disponibile (care nu au fost deja câștigate). În exemplul de mai sus, dacă jucătorul 0 ar fi mutat în centrul tablei (tabla indiiduală din poziția n a U-board), ar fi câștigat tabla n, dar, cum tabla c este deja finalizată, jucătorul x ar fi putut la următoarea mutare să mute în oricare dintre tablele ne, w, e, sw, s, ceea ce ar fi fost un avantaj pentru x.

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ă):

  • predicate care construiesc reprezentarea unei stări (care va fi acesată folosind predicatele de acces):
    • initialState construiește reprezentarea stării inițiale a jocului.
    • buildState construiește reprezentarea unei stări pe baza configurației tablei de joc și pe baza poziției în care a mutat jucătorul anterior.
  • predicate care accesează reprezentarea unei stări:
    • getBoards, getBoard, getPos/3, getPos/4 obțin informații despre tablele de joc individuale. Tablele individuale sunt văzute ca liste de 9 celule, puse în ordinea pozițiilor dată mai sus. Fiecare celulă poate avea ca valoare atomul (literalul) x, numărul 0, sau atomul vid ''. Pentru starea din exemplul de mai sus, getBoard(State, n, B) trebuie să lege B la lista [x,'','', 0,'',0, '',0,''].
    • getUBoard obține configurația tablei de UTTT, văzută ca o tablă individuală. În plus față de tablele individuale, tabla de UTTT poate avea și celule cu valoarea r, pentru tablele individuale remizate. Pentru starea din exemplul de mai sus, getUBoard(State, UBoard) trebuie să lege UBoard la lista [x,'','', '',x,'', '','',0].
    • getNextPlayer obține jucătorul care urmează la rând (x sau 0). Acesta poate fi determinat numărând celulele cu x și cu 0 din tablele individuale. Primul jucător este x. În exemplul de mai sus, următorul jucător este x pentru că sunt 14 mutări ale lui x și 14 mutări ale lui 0.
    • getAvailableBoards obține tablele individuale (ca poziții în tabla de UTTT) disponibile pentru următoarea mutare.
      • în starea inițială, este întreaga listă de poziții (jucătorul poate muta în orice tablă), deci pentru initialState(S0), getAvailableBoards(S0, Boards), Boards trebuie legat la lista completă de poziții: [nw, n, ne, w, c, e, sw, s, se];
      • de obicei, este o listă conținând o singură poziție, aceeași cu poziția dintr-o tablă individuală unde a mutat jucătorul precedent. În exemplul de mai sus, cum jucătorul 0 tocmai a mutat în poziția w a tablei n, jucătorul x trebuie să mute obligatoriu în tabla w, deci getAvailableBoards(State, Boards) trebuie să lege Boards la lista [w];
      • atunci când jucătorul precedent a mutat într-o poziție care corespunde unei table individuale în care jocul s-a terminat, sunt disponibile pentru următoarea poziție toate tablele care nu sunt încă finalizate (nu au fost câștigate sau remizate). Dacă în exemplul de mai sus x mută în centrul tablei w, pentru următoarea mutare, a lui 0 vor fi disponibile tablele [n, ne, w, e, sw, s].
    • getBoardResult obține rezultatul pentru configurația unei table individuale. Rezultatul poate fi x, 0, r, sau '', acesta din urmă pentru cazul în care jocul pe această tablă individuală continuă.
  • predicate pentru efectuarea unei mutări. Dacă pentru mutare este disponibilă o singură tablă, atunci mutarea este poziția pe această tablă unde se va pune x sau 0; dacă pentru mutare sunt disponibile mai multe table, atunci mutarea este o pereche (UPos, Pos) între poziția tablei individuale (în cadrul U-board) unde se va face mutarea, și poziția din tablă unde se va pune x sau 0.
    • validMove verifică validitatea unei mutări.
    • makeMove determină starea următoare după efectuarea unei mutări.

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

  • dummy_first, care alege întotdeauna prima (în ordinea pozițiilor) mutare disponibilă. În exemplul de mai sus, strategia va întoarce n, prima poziție disponibilă din tabla w unde trebuie să mute x. Dacă din starea din exemplu, x ar muta, conform unei alte strategii, în c, pentru mutarea lui 0 strategia dummy_first ar întoarce (n, n), pentru că n este prima tablă disponibilă, și n este prima poziție disponibilă din acea tablă.
  • dummy_last, care alege întotdeauna ultima (în ordinea pozițiilor) mutare disponibilă. În exemplul de mai sus, strategia va întoarce we, ultima poziție disponibilă din tabla w unde trebuie să mute x. Dacă din starea din exemplu, x ar muta, conform unei alte strategii, în c, pentru mutarea lui 0 strategia last ar întoarce (s, se), pentru că s este ultima tablă disponibilă, și se este ultima poziție disponibilă din acea tablă.

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 introgarea:

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.

Precizări

  • se va lucra numai în fișierul uttt.pl
  • pentru rularea testelor se va apela predicatul vmcheck/0 (apela de la consolă ca vmcheck.)
  • pentru rezultate mai detaliate ale testelor, decomentați linia detailed_mode_disabled :- !, fail. din fișierul checker.pl. Modul detaliat (unde este posibil să primiți câteva puncte în plus cu implementările implicite) nu este cel folosit pe vmchecker, dar în acest mod testerul oferă mai mult detalii despre testele eșuate.

Resurse

Changelog

  • 10.05 - publicarea temei (încă nu sunt disponibile testele pentru bonusul etapei 1)
  • 11.05 - modificare a scheletului - în uttt.pl trebuie inclus files.pl în loc de checker.pl
  • 11.05 - adăugare teste pentru bonusul etapei 1
  • 11.05 - mai multe exemplificări în enunț
pp/22/teme/prolog-ultimate-ttt.1652279288.txt.gz · Last modified: 2022/05/11 17:28 by bot.pp
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0