Skip to content

Commit

Permalink
WIP: First attempt to generalize the puzzle dimensions and available …
Browse files Browse the repository at this point in the history
…moves
  • Loading branch information
greathouse committed May 24, 2012
1 parent 23672bd commit bfab4ba
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 95 deletions.
2 changes: 2 additions & 0 deletions spock/BoardStateTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ class BoardStateTests extends spock.lang.Specification {
cost == 0
}

@Unroll
def "simple test"() {
setup:
ApplicationState.instance.initialize(3, 3, "1 2 3 4 5 6 7 8 0")
def boardState = new BoardState(i, null)
when:
def cost = boardState.calculateCost()
Expand Down
59 changes: 59 additions & 0 deletions src/com/greenmoonsoftware/eightpuzzle/ApplicationState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.greenmoonsoftware.eightpuzzle;

import java.util.HashMap;
import java.util.Map;

public final class ApplicationState {
public static final ApplicationState instance = new ApplicationState();
private int dimensionX;
private int dimensionY;
private String solutionState;
private Map<String, Coordinates> solutionMap = new HashMap<String, Coordinates>();
private ApplicationState() {}

public final void initialize(int dimX, int dimY, String solution) {
dimensionX = dimX;
dimensionY = dimY;
solutionState = solution;
String[] solutionValues = solutionState.split(" ");
if (solutionValues.length != dimX*dimY) {
throw new IllegalArgumentException("Solution values ["+solutionValues.length+"] do not match dimension size ["+dimX*dimY+"]");
}
int spotCounter = 0;
for (int x=0; x<dimensionX; x++) {
for (int y=0; y<dimensionY; y++) {
solutionMap.put(solutionValues[spotCounter], new Coordinates(x,y));
spotCounter++;
}
}
}

public int getDimensionX() {
return dimensionX;
}

public int getDimensionY() {
return dimensionY;
}

public int getXForValue(String val) {
return solutionMap.get(val).x;
}

public int getYForValue(String val) {
return solutionMap.get(val).y;
}

public String getSolutionState() {
return solutionState;
}

private static class Coordinates {
final int x;
final int y;
private Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
}
}
178 changes: 87 additions & 91 deletions src/com/greenmoonsoftware/eightpuzzle/BoardState.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,35 @@

public class BoardState {
private String state;
private char[][] chars;
private Spot[][] chars;
private BoardState parentState;
private Set<BoardState> nextStates;
private int cost = -1;
private int openSpotX;
private int openSpotY;

public BoardState(String state, BoardState parent) {
String[] stateValues = state.split(" ");
this.state = state;
this.parentState = parent;
char[][] c = {
{state.charAt(0), state.charAt(1), state.charAt(2)},
{state.charAt(3), state.charAt(4), state.charAt(5)},
{state.charAt(6), state.charAt(7), state.charAt(8)}
};

ApplicationState appState = ApplicationState.instance;
int dimX = appState.getDimensionX();
int dimY = appState.getDimensionY();

Spot[][] c = new Spot[dimX][dimY];
int spotCounter = 0;
for (int x=0; x<dimX; x++) {
for (int y=0; y<dimY; y++) {
String value = stateValues[spotCounter];
c[x][y] = new Spot(value, x, y, appState.getXForValue(value), appState.getYForValue(value));
if (value.equals("0")) {
openSpotX = x;
openSpotY = y;
}
spotCounter++;
}
}
chars = c;
calculateCost();
}
Expand Down Expand Up @@ -50,65 +66,73 @@ public int hashCode() {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
return sb.append(state.substring(0, 3)).append("\n")
.append(state.substring(3,6)).append("\n")
.append(state.substring(6))
.toString();
for (int x=0; x<ApplicationState.instance.getDimensionX(); x++) {
for (int y=0; y<ApplicationState.instance.getDimensionY(); y++) {
sb.append(String.format("%3s",chars[x][y].getCharacter())).append(" ");
}
sb.append("\n");
}
return sb.toString();
}

public Set<BoardState> getNextAvailableStates() {
if (nextStates != null) {
return nextStates;
}
ApplicationState appState = ApplicationState.instance;
nextStates = new HashSet<BoardState>();
int blankIndex = state.indexOf('0');
switch(blankIndex) {
case 0:
addState(1, blankIndex);
addState(3, blankIndex);
break;
case 1:
addState(0,blankIndex);
addState(2,blankIndex);
addState(4,blankIndex);
break;
case 2:
addState(1,blankIndex);
addState(5,blankIndex);
break;
case 3:
addState(0,blankIndex);
addState(4,blankIndex);
addState(6,blankIndex);
break;
case 4:
addState(1,blankIndex);
addState(3,blankIndex);
addState(5,blankIndex);
addState(7,blankIndex);
break;
case 5:
addState(2,blankIndex);
addState(4,blankIndex);
addState(8,blankIndex);
break;
case 6:
addState(3,blankIndex);
addState(7,blankIndex);
break;
case 7:
addState(6,blankIndex);
addState(4,blankIndex);
addState(8,blankIndex);
break;
case 8:
addState(5,blankIndex);
addState(7,blankIndex);
break;
int top = openSpotX - 1;
int bottom = openSpotX + 1;
int left = openSpotY - 1;
int right = openSpotY + 1;

if (bottom < appState.getDimensionX()) {
nextStates.add(moveOpenSpot(openSpotX, openSpotY, bottom, openSpotY));
}
if (right < appState.getDimensionY()) {
nextStates.add(moveOpenSpot(openSpotX, openSpotY, openSpotX, right));
}
if (left >= 0) {
nextStates.add(moveOpenSpot(openSpotX, openSpotY, openSpotX, left));
}
if (top >= 0) {
nextStates.add(moveOpenSpot(openSpotX, openSpotY, top, openSpotY));
}

return nextStates;
}

private String encode(Spot[][] spots) {
StringBuilder sb = new StringBuilder();
for (Spot[] x : spots) {
for (Spot y : x) {
sb.append(y.getCharacter()).append(" ");
}
}
return sb.toString().trim();
}

private Spot[][] copySpots() {
//TODO: Maybe look at using System.arraycopy for micro-optimazation
Spot[][] copy = new Spot[ApplicationState.instance.getDimensionX()][ApplicationState.instance.getDimensionY()];
for (int x=0; x<ApplicationState.instance.getDimensionX(); x++) {
for (int y=0; y<ApplicationState.instance.getDimensionY(); y++) {
copy[x][y] = chars[x][y];
}
}
return copy;
}

private BoardState moveOpenSpot(int fromX, int fromY, int toX, int toY) {
Spot[][] copy = copySpots();
Spot openSpot = chars[fromX][fromY];
copy[toX][toY] = openSpot.newMove(toX, toY);

Spot replaceSpot = chars[toX][toY];
copy[fromX][fromY] = replaceSpot.newMove(fromX, fromY);
return new BoardState(encode(copy), this);
}

private void addState(int from, int to) {
StringBuilder sb = new StringBuilder(state);
sb.setCharAt(to, state.charAt(from));
Expand All @@ -120,46 +144,18 @@ public int calculateCost() {
if (cost >= 0) {
return cost;
}
ApplicationState appState = ApplicationState.instance;
int cost = 0;
for (int x=0; x<3; x++) {
for (int y=0; y<3; y++) {
char c = chars[x][y];
switch(c) {
case '1': //position 1:1
cost += Math.abs(x+1-1);
cost += Math.abs(y+1-1);
break;
case '2':
cost += Math.abs(x+1-1);
cost += Math.abs(y+1-2);
break;
case '3':
cost += Math.abs(x+1-1);
cost += Math.abs(y+1-3);
break;
case '4':
cost += Math.abs(x+1-2);
cost += Math.abs(y+1-1);
break;
case '5':
cost += Math.abs(x+1-2);
cost += Math.abs(y+1-2);
break;
case '6':
cost += Math.abs(x+1-2);
cost += Math.abs(y+1-3);
break;
case '7':
cost += Math.abs(x+1-3);
cost += Math.abs(y+1-1);
break;
case '8':
cost += Math.abs(x+1-3);
cost += Math.abs(y+1-2);
break;
}
for (int x=0; x<appState.getDimensionX(); x++) {
for (int y=0; y<appState.getDimensionY(); y++) {
cost += chars[x][y].calculateCost();
}
}
return cost;
}

public static void main(String[] args) {
ApplicationState.instance.initialize(3,3,"1 2 3 4 5 6 7 8 0");
System.out.println(new BoardState("1 2 3 4 5 0 7 8 6", null).calculateCost());
}
}
8 changes: 4 additions & 4 deletions src/com/greenmoonsoftware/eightpuzzle/Solver.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

public class Solver {
private final BoardState initialState;
private final String solutionState = "123456780";
private int movesForSolution = 0;

private PriorityQueue<BoardState> openStates = new PriorityQueue<BoardState>(1000, new Comparator<BoardState>() {
Expand All @@ -23,7 +22,8 @@ public int compare(BoardState o1, BoardState o2) {

private int calculations = 0;

public Solver(String initialState) {
public Solver(int dimX, int dimY, String initialState, String solutionState) {
ApplicationState.instance.initialize(3,3,solutionState);
this.initialState = new BoardState(initialState, null);
}

Expand All @@ -36,7 +36,7 @@ public Result solve() {
calculations++;
currentState = openStates.poll();
closedStates.add(currentState);
if (solutionState.equals(currentState.getState())) {
if (ApplicationState.instance.getSolutionState().equals(currentState.getState())) {
System.out.println("Found solution");
foundSolution = true;
break;
Expand All @@ -59,7 +59,7 @@ private void calculateMoves(BoardState state) {
}

public static void main(String[] args) {
Result r = new Solver("012345678").solve();
Result r = new Solver(3,3,"0 1 2 3 4 5 6 7 8", "1 2 3 4 5 6 7 8 0").solve();
if (!r.isFoundSolution()) {
System.out.println("No solution!");
return;
Expand Down
56 changes: 56 additions & 0 deletions src/com/greenmoonsoftware/eightpuzzle/Spot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.greenmoonsoftware.eightpuzzle;

public class Spot {
private int currentX;
private int currentY;
private int solutionX;
private int solutionY;
private int cost = -1;

private String character;

public Spot(String character, int currentX, int currentY, int solutionX, int solutionY) {
this.character = character;
this.currentX = currentX;
this.currentY = currentY;
this.solutionX = solutionX;
this.solutionY = solutionY;
}

public Spot newMove(int newX, int newY) {
return new Spot(this.character, newX, newY, solutionX, solutionY);
}

public int getCurrentX() {
return currentX;
}

public int getCurrentY() {
return currentY;
}

public int getSolutionX() {
return solutionX;
}

public int getSolutionY() {
return solutionY;
}

public String getCharacter() {
return character;
}

public int calculateCost() {
if (cost >= 0) {
return cost;
}
if (character.equals("0")) {
cost = 0;
return cost;
}
cost = Math.abs(currentX - solutionX);
cost += Math.abs(currentY - solutionY);
return cost;
}
}
Loading

0 comments on commit bfab4ba

Please sign in to comment.