Fixes
- added edge cases tris - added tostring Tris - changed State to Symbol for better readability
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package net.berack.upo.ai.problem2;
|
package net.berack.upo.ai.problem2;
|
||||||
|
|
||||||
|
import static net.berack.upo.ai.problem2.Tris.Symbol.EMPTY;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
@@ -9,7 +11,7 @@ import java.util.Iterator;
|
|||||||
*
|
*
|
||||||
* @author Berack96
|
* @author Berack96
|
||||||
*/
|
*/
|
||||||
public class Tris implements Iterable<Tris.State> {
|
public class Tris implements Iterable<Tris.Symbol> {
|
||||||
public static final int LENGTH = 3;
|
public static final int LENGTH = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,23 +33,23 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possibili stati delle zone
|
* Possibili simboli delle zone
|
||||||
*/
|
*/
|
||||||
public static enum State {
|
public static enum Symbol {
|
||||||
EMPTY,
|
EMPTY,
|
||||||
VALUE_X,
|
VALUE_X,
|
||||||
VALUE_O
|
VALUE_O
|
||||||
}
|
}
|
||||||
|
|
||||||
private State[] tris;
|
private Symbol[] tris;
|
||||||
private State currentTurn = State.VALUE_X;
|
private Symbol currentTurn = Symbol.VALUE_X;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crea una nuova istanza del gioco con tutti gli spazi vuoti
|
* Crea una nuova istanza del gioco con tutti gli spazi vuoti
|
||||||
*/
|
*/
|
||||||
public Tris() {
|
public Tris() {
|
||||||
this.tris = new State[LENGTH * LENGTH];
|
this.tris = new Symbol[LENGTH * LENGTH];
|
||||||
Arrays.fill(tris, State.EMPTY);
|
Arrays.fill(tris, Symbol.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,39 +58,49 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
* @param coord le coordinate in cui il giocatore vuole giocare la sua mossa
|
* @param coord le coordinate in cui il giocatore vuole giocare la sua mossa
|
||||||
*/
|
*/
|
||||||
public Tris(Tris current, Coordinate coord) {
|
public Tris(Tris current, Coordinate coord) {
|
||||||
Arrays.copyOf(current.tris, current.tris.length);
|
this.tris = Arrays.copyOf(current.tris, current.tris.length);
|
||||||
this.currentTurn = current.currentTurn;
|
this.currentTurn = current.currentTurn;
|
||||||
this.play(coord.x, coord.y);
|
this.play(coord.x, coord.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indica il valore del simbolo che verrà inserito nella successiva mossa giocata,
|
||||||
|
* ovvero il simbolo che verrà giocato alla prossima mossa.
|
||||||
|
*
|
||||||
|
* @return il simbolo da giocare
|
||||||
|
*/
|
||||||
|
public Symbol getNextPlaySymbol() {
|
||||||
|
return this.currentTurn;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permette di prendere il valore della cella specificata dalle coordinate
|
* Permette di prendere il valore della cella specificata dalle coordinate
|
||||||
* @param x la coordinata x da cui ricevere lo stato
|
* @param x la coordinata x da cui ricevere il simbolo
|
||||||
* @param y la coordinata y da cui ricevere lo stato
|
* @param y la coordinata y da cui ricevere il simbolo
|
||||||
* @return il valore della cella
|
* @return il valore della cella
|
||||||
*/
|
*/
|
||||||
public State get(int x, int y) {
|
public Symbol get(int x, int y) {
|
||||||
return this.tris[this.index(x, y)];
|
return this.tris[this.index(x, y)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permette di far avanzare il gioco, facendo giocare il turno al giocatore corrente nella cella specificata dalle coordinate.
|
* Permette di far avanzare il gioco, facendo giocare il turno al giocatore corrente nella cella specificata dalle coordinate.
|
||||||
* Nel caso in cui lo stato scelto dalle coordinate non risulti EMPTY il metodo lancerà una eccezione.
|
* Nel caso in cui il simbolo scelto dalle coordinate non risulti EMPTY il metodo lancerà una eccezione.
|
||||||
* Una volta che si ha un vincitore questo metodo non potrà essere piu chiamato e lancerà una eccezione.
|
* Una volta che si ha un vincitore questo metodo non potrà essere piu chiamato e lancerà una eccezione.
|
||||||
*
|
*
|
||||||
* @param x la coordinata x in cui mettere lo stato
|
* @param x la coordinata x in cui mettere il simbolo
|
||||||
* @param y la coordinata y in cui mettere lo stato
|
* @param y la coordinata y in cui mettere il simbolo
|
||||||
* @throws UnsupportedOperationException nel caso in cui si ha già avuto un vincitore
|
* @throws UnsupportedOperationException nel caso in cui si ha già avuto un vincitore
|
||||||
*/
|
*/
|
||||||
public void play(int x, int y) {
|
public void play(int x, int y) {
|
||||||
if(this.haveWinner() != State.EMPTY) throw new UnsupportedOperationException("The game has already finished!");
|
if(this.haveWinner() != Symbol.EMPTY) throw new UnsupportedOperationException("The game has already finished!");
|
||||||
if(!isPlayAvailable(x, y)) throw new IllegalArgumentException("The state to modify must be Empty!");
|
if(!isPlayAvailable(x, y)) throw new IllegalArgumentException("The state to modify must be Empty!");
|
||||||
|
|
||||||
this.tris[this.index(x, y)] = this.currentTurn;
|
this.tris[this.index(x, y)] = this.currentTurn;
|
||||||
this.currentTurn = switch(this.currentTurn) {
|
this.currentTurn = switch(this.currentTurn) {
|
||||||
case VALUE_X -> State.VALUE_O;
|
case VALUE_X -> Symbol.VALUE_O;
|
||||||
case VALUE_O -> State.VALUE_X;
|
case VALUE_O -> Symbol.VALUE_X;
|
||||||
default -> State.EMPTY;
|
default -> Symbol.EMPTY;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +114,7 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
* @return vero se la mossa può essere giocata, altrimenti falso
|
* @return vero se la mossa può essere giocata, altrimenti falso
|
||||||
*/
|
*/
|
||||||
public boolean isPlayAvailable(int x, int y) {
|
public boolean isPlayAvailable(int x, int y) {
|
||||||
return this.tris[this.index(x, y)] == State.EMPTY;
|
return this.tris[this.index(x, y)] == Symbol.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,9 +126,11 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
* @return un array di coordinate disponibili per giocare.
|
* @return un array di coordinate disponibili per giocare.
|
||||||
*/
|
*/
|
||||||
public Coordinate[] availablePlays() {
|
public Coordinate[] availablePlays() {
|
||||||
|
if(this.haveWinner() != EMPTY) return new Coordinate[0];
|
||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for(var i = 0; i < this.tris.length; i++)
|
for(var i = 0; i < this.tris.length; i++)
|
||||||
if(this.tris[i] == State.EMPTY)
|
if(this.tris[i] == Symbol.EMPTY)
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
var res = new Coordinate[count];
|
var res = new Coordinate[count];
|
||||||
@@ -134,31 +148,31 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
* Indica se si ha un vincitore e restituisce chi ha vinto.
|
* Indica se si ha un vincitore e restituisce chi ha vinto.
|
||||||
* @return EMPTY se non c'è ancora un vincitore, altrimenti restituisci il vincitore
|
* @return EMPTY se non c'è ancora un vincitore, altrimenti restituisci il vincitore
|
||||||
*/
|
*/
|
||||||
public State haveWinner() {
|
public Symbol haveWinner() {
|
||||||
// top left corner -> horizontal and vertical
|
// top left corner -> horizontal and vertical
|
||||||
var state = this.tris[0];
|
var state = this.tris[0];
|
||||||
if(state != State.EMPTY) {
|
if(state != Symbol.EMPTY) {
|
||||||
if(this.tris[1] == state && this.tris[2] == state) return state;
|
if(this.tris[1] == state && this.tris[2] == state) return state;
|
||||||
if(this.tris[3] == state && this.tris[6] == state) return state;
|
if(this.tris[3] == state && this.tris[6] == state) return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bottom right corner -> horizontal and vertical
|
// bottom right corner -> horizontal and vertical
|
||||||
state = this.tris[8];
|
state = this.tris[8];
|
||||||
if(state != State.EMPTY) {
|
if(state != Symbol.EMPTY) {
|
||||||
if(this.tris[7] == state && this.tris[6] == state) return state;
|
if(this.tris[7] == state && this.tris[6] == state) return state;
|
||||||
if(this.tris[5] == state && this.tris[2] == state) return state;
|
if(this.tris[5] == state && this.tris[2] == state) return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// central -> diagonals, horizontal and vertical
|
// central -> diagonals, horizontal and vertical
|
||||||
state = this.tris[4];
|
state = this.tris[4];
|
||||||
if(state != State.EMPTY) {
|
if(state != Symbol.EMPTY) {
|
||||||
if(this.tris[0] == state && this.tris[8] == state) return state;
|
if(this.tris[0] == state && this.tris[8] == state) return state;
|
||||||
if(this.tris[6] == state && this.tris[2] == state) return state;
|
if(this.tris[6] == state && this.tris[2] == state) return state;
|
||||||
if(this.tris[3] == state && this.tris[5] == state) return state;
|
if(this.tris[3] == state && this.tris[5] == state) return state;
|
||||||
if(this.tris[1] == state && this.tris[7] == state) return state;
|
if(this.tris[1] == state && this.tris[7] == state) return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return State.EMPTY;
|
return Symbol.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,11 +190,30 @@ public class Tris implements Iterable<Tris.State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<State> iterator() {
|
public String toString() {
|
||||||
return new Iterator<State>() {
|
var builder = new StringBuilder();
|
||||||
|
for(var y = 0; y < LENGTH; y++) {
|
||||||
|
if(y > 0) builder.append("\n-----\n");
|
||||||
|
|
||||||
|
for(var x = 0; x < LENGTH; x++) {
|
||||||
|
if(x > 0) builder.append('|');
|
||||||
|
builder.append(switch(this.tris[this.index(x, y)]) {
|
||||||
|
case VALUE_O -> 'O';
|
||||||
|
case VALUE_X -> 'X';
|
||||||
|
default -> ' ';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Symbol> iterator() {
|
||||||
|
return new Iterator<Symbol>() {
|
||||||
int current = 0;
|
int current = 0;
|
||||||
@Override public boolean hasNext() { return current < tris.length; }
|
@Override public boolean hasNext() { return current < tris.length; }
|
||||||
@Override public State next() { return tris[current++]; }
|
@Override public Symbol next() { return tris[current++]; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ package net.berack.upo.ai.problem2;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static net.berack.upo.ai.problem2.Tris.State.*;
|
import static net.berack.upo.ai.problem2.Tris.Symbol.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import net.berack.upo.ai.problem2.Tris.State;
|
|
||||||
|
|
||||||
public class TestTris {
|
public class TestTris {
|
||||||
|
|
||||||
@@ -21,13 +21,25 @@ public class TestTris {
|
|||||||
for(var state : tris) assertEquals(EMPTY, state);
|
for(var state : tris) assertEquals(EMPTY, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContructor2() {
|
||||||
|
var tris = new Tris();
|
||||||
|
tris.play(0, 0);
|
||||||
|
|
||||||
|
var move = tris.availablePlays()[0];
|
||||||
|
var second = new Tris(tris, move);
|
||||||
|
|
||||||
|
assertFalse(second.isPlayAvailable(move.x, move.y));
|
||||||
|
assertEquals(VALUE_O, second.get(move.x, move.y));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPlay() {
|
public void testPlay() {
|
||||||
var tris = new Tris();
|
var tris = new Tris();
|
||||||
tris.play(0, 0);
|
tris.play(0, 0);
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var states = new Tris.State[] {VALUE_X, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY};
|
var states = new Tris.Symbol[] {VALUE_X, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY};
|
||||||
for(var state : tris) assertEquals(states[i++], state);
|
for(var state : tris) assertEquals(states[i++], state);
|
||||||
|
|
||||||
tris.play(2, 2);
|
tris.play(2, 2);
|
||||||
@@ -76,6 +88,8 @@ public class TestTris {
|
|||||||
var curr = actions[i];
|
var curr = actions[i];
|
||||||
tris.play(curr.x, curr.y);
|
tris.play(curr.x, curr.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertEquals(0, tris.availablePlays().length, "The game is over, there aren't any moves left");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -114,47 +128,47 @@ public class TestTris {
|
|||||||
|
|
||||||
// horizontal 1 line X
|
// horizontal 1 line X
|
||||||
var tris = new Tris();
|
var tris = new Tris();
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,0);
|
tris.play(1,0);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,1);
|
tris.play(1,1);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(0,0);
|
tris.play(0,0);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,2);
|
tris.play(1,2);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(2,0);
|
tris.play(2,0);
|
||||||
assertTrue(tris.haveWinner() == State.VALUE_X);
|
assertTrue(tris.haveWinner() == VALUE_X);
|
||||||
|
|
||||||
// diagonal \ O
|
// diagonal \ O
|
||||||
tris = new Tris();
|
tris = new Tris();
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(2,1);
|
tris.play(2,1);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,1);
|
tris.play(1,1);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(2,0);
|
tris.play(2,0);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(2,2);
|
tris.play(2,2);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,2);
|
tris.play(1,2);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(0,0);
|
tris.play(0,0);
|
||||||
assertTrue(tris.haveWinner() == State.VALUE_O);
|
assertTrue(tris.haveWinner() == VALUE_O);
|
||||||
|
|
||||||
// vertical 2 column X
|
// vertical 2 column X
|
||||||
tris = new Tris();
|
tris = new Tris();
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,0);
|
tris.play(1,0);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(0,2);
|
tris.play(0,2);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,1);
|
tris.play(1,1);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(0,1);
|
tris.play(0,1);
|
||||||
assertTrue(tris.haveWinner() == State.EMPTY);
|
assertTrue(tris.haveWinner() == EMPTY);
|
||||||
tris.play(1,2);
|
tris.play(1,2);
|
||||||
assertTrue(tris.haveWinner() == State.VALUE_X);
|
assertTrue(tris.haveWinner() == VALUE_X);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user