This is an old revision of the document!
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 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.
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:
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
.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
.Î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ă):
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.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.initialState(S0), getAvailableBoards(S0, Boards)
, Boards
trebuie legat la lista completă de poziții: [nw, n, ne, w, c, e, sw, s, se]
;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]
;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ă.(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ă.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
.
uttt.pl
vmcheck.
)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.