package search; /** * Coins Puzzle Implementation. * CS373 Assignment #1 * @author: Joey Kiernan and Nathaniel Lim * @version: 1.2 * @date: 2/22/10 */ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.StringTokenizer; public class CoinPuzzle implements ProblemGraph { //Constants to denote blank number, action types, boardsize public static final int BOARD_SIZE = 4; public static final int BLANK = 0; public static final int MOVE_PENNY = 1; public static final int MOVE_NICKLE = 2; public static final int MOVE_QUARTER = 3; private CoinState goalNode; public CoinPuzzle(){ setGoalNode(new CoinState("(1 2 3 B)")); } public class CoinState implements State { private int[] board = new int[BOARD_SIZE]; private int blankX; public CoinState(String configurationString){ StringTokenizer tokens = new StringTokenizer(configurationString, "() "); if(tokens.countTokens() != BOARD_SIZE){ throw new RuntimeException("The string: " + configurationString + " is not a valid Coin Puzzle state string."); } for (int i = 0; i < BOARD_SIZE; i++){ String str = tokens.nextToken(); if (str.equals("B")) { board[i] = BLANK; blankX = i; } else { int val = Integer.decode(str); if (val > BOARD_SIZE-1 || val < 0) throw new RuntimeException("The string: " + configurationString + " is not a valid CoinPuzzle state string. The value " + val + " is not a valid entry."); board[i] = val; } } } /** * Uses the board integer array to produce a String representation * of the state. */ public String toString() { String strBuf = "("; for (int i = 0; i < board.length; i++) { if(board[i] != BLANK){ strBuf += board[i]; }else { strBuf += "B"; } if (i < board.length - 1) { strBuf += " "; } } strBuf += ")"; return strBuf; } /** * Writing a Heuristic for the * The Goal State is: Goal State (1 2 3 B) * Returning the sum of how far each piece * is away from their ending goal position. * * This may be an overestimation given that we may be double * counting the distance if we are indeed switching coins * with blanks, thus we divide by two in order to be * admissable */ public int h(){ int mh = 0; for (int i = 0; i<BOARD_SIZE; i++){ if (board[i] == BLANK){ mh += Math.abs(i-3); } else if (board[i] == 1) { mh += Math.abs(i); }else if (board[i] == 2) { mh += Math.abs(i-1); }else if (board[i] == 3) { mh += Math.abs(i-2); } } return mh/2; } /* * All actions are valid: Just switching one of three coins * with the blank spot. */ public boolean isValidAction(CoinPuzzleAction action){ return true; } private CoinState(CoinState parent, CoinPuzzleAction action) { // initialize to clone parent for (int i = 0; i < board.length; i++){ board[i] = parent.board[i]; } this.blankX = parent.blankX; //Find the position of the coin type of the action //And switch it with the blank square. int type = action.getType(); for (int i = 0; i < board.length; i++){ if (board[i] == type){ //Switch the values; board[blankX] = board[i]; board[i] = BLANK; blankX = i; break; } } } //Expand the node, all actions are valid, switching coins with the //blank space. public List<GraphNode> expandNode() { List<GraphNode> list = new ArrayList<GraphNode>(); for (CoinPuzzleAction a : CoinPuzzleAction.values()) { if (this.isValidAction(a)) { CoinState newNode = new CoinState(this, a); list.add(new GraphNode(newNode)); } } return list; } public boolean isGoal() { return this.equals(goalNode); } /** * The equals function is constructed from the two rows of the configuration * array. */ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final CoinState other = (CoinState) obj; if (!Arrays.equals(board, other.board)) return false; return true; } /** * The hash function is constructed from the two rows of the configuration * array. */ @Override public int hashCode() { final int PRIME = 31; return PRIME + Arrays.hashCode(board); } } @Override public CoinState getState(String stateString) { return new CoinState(stateString); } public void setGoalNode(CoinState goalNode) { this.goalNode = goalNode; } public CoinState getGoalNode() { return goalNode; } enum CoinPuzzleAction { MOVEPENNY(MOVE_PENNY), MOVENICKLE(MOVE_NICKLE), MOVEQUARTER(MOVE_QUARTER); private final int type; private CoinPuzzleAction(int type){ this.type = type; } public int getType(){ return type; } } }