Puzzle8 fix

- fixed bug causing the solve function to use all the memory and then crash
- added new test cases
- removed unused counter in AStar
This commit is contained in:
2024-02-22 19:55:57 +01:00
parent e5f953526e
commit 17e7c0a9fa
3 changed files with 48 additions and 30 deletions

View File

@@ -22,9 +22,6 @@ public class AStar<State, Action> {
private BiFunction<State, State, Integer> heuristic; private BiFunction<State, State, Integer> heuristic;
private TriFunction<State, State, Action, Integer> cost; private TriFunction<State, State, Action, Integer> cost;
private int lastStateVisitedCount = 0;
private int lastStateTotalCount = 0;
/** /**
* Crea una istanza dell'algoritmo A* con una funzione di azioni possibili da compiere dato uno stato * Crea una istanza dell'algoritmo A* con una funzione di azioni possibili da compiere dato uno stato
* e una funzione di transizione che dato uno stato permette di raggiungerne un'altro tramite un'azione. * e una funzione di transizione che dato uno stato permette di raggiungerne un'altro tramite un'azione.
@@ -95,16 +92,11 @@ public class AStar<State, Action> {
Objects.requireNonNull(initial); Objects.requireNonNull(initial);
Objects.requireNonNull(goal); Objects.requireNonNull(goal);
this.lastStateVisitedCount = 0;
this.lastStateTotalCount = 1;
NodeState found = null; NodeState found = null;
var list = new PriorityQueue<NodeState>(); var list = new PriorityQueue<NodeState>();
list.add(new NodeState(null, initial, null, 0, 0)); list.add(new NodeState(null, initial, null, 0, 0));
while(list.size() > 0) { while(list.size() > 0) {
this.lastStateVisitedCount += 1;
var current = list.poll(); var current = list.poll();
if(current.state.equals(goal)) { if(current.state.equals(goal)) {
found = current; found = current;
@@ -112,8 +104,6 @@ public class AStar<State, Action> {
} }
for(var action : this.actions.apply(current.state)) try { for(var action : this.actions.apply(current.state)) try {
this.lastStateTotalCount += 1;
var next = this.transition.apply(current.state, action); var next = this.transition.apply(current.state, action);
var cost = this.cost.apply(current.state, next, action); var cost = this.cost.apply(current.state, next, action);
var dist = this.heuristic.apply(next, goal); var dist = this.heuristic.apply(next, goal);
@@ -137,16 +127,6 @@ public class AStar<State, Action> {
return path; return path;
} }
public int lastStateVisitedCount() {
return this.lastStateVisitedCount;
}
public int lastStateTotalCount() {
return this.lastStateTotalCount;
}
/** /**
* Classe privata per mantenere i dati all'interno della PriorityQueue. * Classe privata per mantenere i dati all'interno della PriorityQueue.
* Non è stata messa statica dato che ho bisogno dello stato e dell'azione. * Non è stata messa statica dato che ho bisogno dello stato e dell'azione.
@@ -168,7 +148,7 @@ public class AStar<State, Action> {
@Override @Override
public int compareTo(NodeState other) { public int compareTo(NodeState other) {
return this.total - other.total; return Integer.compare(this.total, other.total);
} }
} }

View File

@@ -25,9 +25,9 @@ public class Puzzle8 implements Iterable<Integer> {
for(var i = 0; i < n; i++) index[p1.puzzle[i]] = i; for(var i = 0; i < n; i++) index[p1.puzzle[i]] = i;
for(var i = 0; i < n; i++) { for(var i = 0; i < n; i++) {
var curr = p2.puzzle[i]; if(i == p2.blank) continue;
if(curr == 0) continue;
var curr = p2.puzzle[i];
var i2 = index[curr]; var i2 = index[curr];
sum += Math.abs((i / LENGTH) - (i2 / LENGTH)) + Math.abs((i % LENGTH) - (i2 % LENGTH)); sum += Math.abs((i / LENGTH) - (i2 / LENGTH)) + Math.abs((i % LENGTH) - (i2 % LENGTH));
} }
@@ -117,7 +117,28 @@ public class Puzzle8 implements Iterable<Integer> {
*/ */
public int get(int x, int y) { public int get(int x, int y) {
if(x >= LENGTH) throw new IndexOutOfBoundsException(); if(x >= LENGTH) throw new IndexOutOfBoundsException();
return puzzle[y * LENGTH + x]; return get(y * LENGTH + x);
}
/**
* Permette di ricevere in input il valore della coordinata passata in input.
* Il valore in input ha formato flat2D, ovvero è un numero che rappresenta una coordinata 2D in uno spazio finito.
* La formula generale per calcolare il numero è la seguente: x + (MAX_X + 1) * y.
* Quindi nel nostro caso si può calcolare la formula con: x + Puzzle8.LENGTH * y.
* NOTA: non usare questo metodo se non si ha capito che cosa significhi. Piuttosto utilizzare {@link #get(int, int)}
* @param flat2D la rappresentazione monodimensionale della coordinata 2D
* @return il valore in quel punto del puzzle.
*/
public int get(int flat2D) {
return puzzle[flat2D];
}
/**
* Permette di avere la posizione della tessera vuota in formato flat2D utilizzabile nel metodo {@link #get(int)}
* @return la posizione dello 0 o tessera vuota
*/
public int getBlankPosition() {
return this.blank;
} }
/** /**
@@ -201,13 +222,14 @@ public class Puzzle8 implements Iterable<Integer> {
var rand = new Random(); var rand = new Random();
for(var i = this.puzzle.length - 1; i > 0; i--) { for(var i = this.puzzle.length - 1; i > 0; i--) {
var j = rand.nextInt(i + 1); var j = rand.nextInt(i + 1);
var temp = this.puzzle[i]; var temp = this.puzzle[i];
this.puzzle[i] = this.puzzle[j]; this.puzzle[i] = this.puzzle[j];
this.puzzle[j] = temp; this.puzzle[j] = temp;
if(this.puzzle[i] == 0) this.blank = i;
} }
for(var i = 0; i < this.puzzle.length; i++)
if(this.puzzle[i] == 0)
this.blank = i;
} }
/** /**

View File

@@ -19,7 +19,7 @@ public class TestPuzzle {
var puzzle = new Puzzle8(goalArray); var puzzle = new Puzzle8(goalArray);
for(var i = 0; i < goalArray.length; i++) for(var i = 0; i < goalArray.length; i++)
assertEquals(goalArray[i], puzzle.get(i%3, i/3), "Error in initialization"); assertEquals(goalArray[i], puzzle.get(i), "Error in initialization");
var puzzle2 = new Puzzle8(goalArray); var puzzle2 = new Puzzle8(goalArray);
assertEquals(puzzle, puzzle2, "Error in equality"); assertEquals(puzzle, puzzle2, "Error in equality");
@@ -71,8 +71,7 @@ public class TestPuzzle {
var puzzle = new Puzzle8(goalArray); var puzzle = new Puzzle8(goalArray);
var moves = new Puzzle8.Move[] {RIGHT, DOWN, LEFT, UP, DOWN, RIGHT, UP, LEFT}; var moves = new Puzzle8.Move[] {RIGHT, DOWN, LEFT, UP, DOWN, RIGHT, UP, LEFT};
for(var move : moves) for(var move : moves) puzzle = new Puzzle8(puzzle, move);
puzzle = new Puzzle8(puzzle, move);
assertEquals(new Puzzle8(goalArray), puzzle, "After useless moves the puzzle must be the same as before!"); assertEquals(new Puzzle8(goalArray), puzzle, "After useless moves the puzzle must be the same as before!");
} }
@@ -158,4 +157,21 @@ public class TestPuzzle {
for(var move : actions) puzzle.move(move); for(var move : actions) puzzle.move(move);
assertEquals(puzzle, goal); assertEquals(puzzle, goal);
} }
@Test
public void testShuffleBlankPosition() {
var puzzle = new Puzzle8(Puzzle8.DEFAULT_GOAL);
var tot = Puzzle8.LENGTH * Puzzle8.LENGTH;
for(var i = 0; i < 1000; i++) {
puzzle.shuffle();
var blank = 10;
for(var j = 0; j < tot; j++)
if(puzzle.get(j) == 0)
blank = j;
assertEquals(blank, puzzle.getBlankPosition());
}
}
} }