playTicTacToe.java
playTicTacToe.java
version XXXXXXXXXX
public class playTicTacToe {
static boolean DEBUG = false;
not final to allow local debug toggles, ie use debug in 1 method only
public static int[] buildBoards( TicTacToeNode root, char symbol ) {
A straight forward way to generate at most 9 children per level.
If the spot is already filled we skip that child, generating the
expected tree. Note we create the child after the if check to
avoid throwing our count of nodes created off.
for ( int i = 0; i < 3; i++ ) {
for ( int j = 0; j < 3; j++ ) {
char[][] newBoard = root.copyBoard();
if ( newBoard[i][j] == Character.MIN_VALUE ) {
newBoard[i][j] = symbol;
TicTacToeNode child = new TicTacToeNode( );
child.board = newBoard;
root.children.add( child );
if ( DEBUG )
System.out.println( child );
}
}
}
for ( TicTacToeNode child : root.children ) {
if ( child.isGameWon() ) {
stop creating
anches, we are at a leaf node
do not return from here so that the other siblings
will be co
ectly generated
if ( child.isGameWon('X') ) {root.xWins++; child.xWins++;}
if ( child.isGameWon('O') ) {root.oWins++; child.oWins++;}
} else if ( !child.isFull() ) {
if the board is full there's nothing to do, simply iterate
to the next sibling. If the board is not a win, nor a tie
then there are more children to create, thus the recursive
step appears here
int[] scores;
scores = buildBoards( child, symbol=='O'? 'X' : 'O' );
root.xWins += scores[0];
root.oWins += scores[1];
}
}
Once all children are processed we can return the results of the parent
node. To reach this return statement we will have already have created
all sub-
anches and counted their scores.
int[] scores = {root.xWins, root.oWins};
return scores;
}
public static void main( String[] args ) {
TicTacToeNode tttTree = new TicTacToeNode();
total number nodes < 1 + 9 + 9*8 + 9*8*7 + ... + 9*8*7*6*5*4*3*2*1 = 986 410
9! = 362,880
3^9 = 19,683
Choose Formula with "one too many moves" e
or = 6,046
Minimal without rotation = 5,478
Minimal with rotation
eflections = 765
buildBoards( tttTree, 'X' );
System.out.println(TicTacToeNode.nodes + " nodes created.");
System.out.printf("X: %d\t\tO: %d\n", tttTree.xWins, tttTree.oWins);
Demonstrate symmetry by presenting opening moves
for( TicTacToeNode child : tttTree.children ) {
System.out.println( child );
System.out.printf("X: %d\t\tO: %d\n", child.xWins, child.oWins);
}
System.out.println("");
}
}
TicTacToeNode.java
TicTacToeNode.java
version XXXXXXXXXX
import java.util.A
ayList;
import java.util.A
ays;
public class TicTacToeNode {
static int nodes = 0;
char[][] board;
A
ayList
children;
int xWins = 0;
int oWins = 0;
int minimaxScore = 0;
public TicTacToeNode() {
nodes++;
board = new char[3][3];
children = new A
ayList
();
}
@Ove
ide
public String toString() {
String boardStr;
boardStr = "---\n";
boardStr += A
ays.toString( board[0] ) + "\n";
boardStr += A
ays.toString( board[1] ) + "\n";
boardStr += A
ays.toString( board[2] ) + "\n";
boardStr += "---\n";
return boardStr;
}
public boolean isGameWon( ) {
return isGameWon( 'X' ) || isGameWon( 'O' );
}
public boolean isGameWon( char symbol ) {
boolean won;
2 diagonals
won = (board[1][1]==symbol) &&
(( board[0][0]==board[1][1] && board[1][1]==board[2][2] ) ||
( board[0][2]==board[1][1] && board[1][1]==board[2][0]) );
for ( int i = 0; i < 3; i++ ) {
3 rows
won = won || (board[i][0]==symbol && board[i][0] == board[i][1] && board[i][1] == board[i][2]);
3 columns
won = won || (board[0][i]==symbol && board[0][i] == board[1][i] && board[1][i] == board[2][i]);
}
return won;
}
public boolean isFull() {
boolean full = true;
for ( char[] row : board )
for ( char pos : row )
full &= ( pos != Character.MIN_VALUE );
return full;
}
public char[][] copyBoard() {
Java's .clone() does not handle 2D a
ays
by copying values, rather we get a clone of refs
char[][] boardCopy = new char[3][3];
for ( int i = 0; i < 3; i ++ )
System.a
aycopy(board[i], 0, boardCopy[i], 0, 3);
return boardCopy;
}
}
package wolfsheep;
import java.util.Deque;
import java.util.LinkedList;
public class WolfSheep {
/**
* Solves the sheep-ca
age-wolf riddle and prints out its solution
*
public static void solveSheepCa
ageWolf(){
XXXXXXXXXXsheepCa
ageWolf(false, false, false, false, new LinkedList());
}
/**
* Solves the sheep-ca
age-wolf riddle and prints out its solution (the actual worker method)
* @param sheep true if the sheep is on the right bank, false otherwise
* @param ca
age true if the ca
age is on the right bank, false otherwise
* @param wolf true if the wolf is on the right bank, false otherwise
* @param farmer true if the farmer (boat) is on the right bank, false otherwise
* @param solution partial solution
* @return false if the partial solution is invalid
*
private static boolean sheepCa
ageWolf(boolean sheep, boolean ca
age, boolean wolf, boolean farmer, Deque solution) {
XXXXXXXXXXif (sheep && ca
age && wolf && farmer) {
XXXXXXXXXXprintSolution(solution);
XXXXXXXXXXreturn true;
}
XXXXXXXXXXif (!checkConsistency(sheep, ca
age, wolf, farmer)) {
XXXXXXXXXXreturn false;
}
XXXXXXXXXXif (solution.isEmpty() || !solution.peek().equals("boatman")) {
XXXXXXXXXXsolution.addFirst("boatman");
XXXXXXXXXXif (sheepCa
ageWolf(sheep, ca
age, wolf, !farmer, solution)) {
XXXXXXXXXXreturn true;
}
XXXXXXXXXXsolution.pop();
acktrack
}
XXXXXXXXXXif (sheep == farmer && (solution.isEmpty() || !solution.peek().equals("sheep"))) {
XXXXXXXXXXsolution.addFirst("sheep");
XXXXXXXXXXif (sheepCa
ageWolf(!sheep, ca
age, wolf, !farmer, solution)) {
XXXXXXXXXXreturn true;
}
XXXXXXXXXXsolution.pop();
acktrack
}
XXXXXXXXXXif (ca
age == farmer && (solution.isEmpty() || !solution.peek().equals("ca
age"))) {
XXXXXXXXXXsolution.addFirst("ca
age");
XXXXXXXXXXif (sheepCa
ageWolf(sheep, !ca
age, wolf, !farmer, solution)) {
XXXXXXXXXXreturn true;
}
XXXXXXXXXXsolution.pop();
acktrack
}
XXXXXXXXXXif (wolf == farmer && (solution.isEmpty() || !solution.peek().equals("wolf"))) {
XXXXXXXXXXsolution.addFirst("wolf");
XXXXXXXXXXif (sheepCa
ageWolf(sheep, ca
age, !wolf, !farmer, solution)) {
XXXXXXXXXXreturn true;
}
XXXXXXXXXXsolution.pop();
acktrack
}
XXXXXXXXXXreturn false;
}
/**
* Check consistency of the partial solution
* @param sheep if the sheep is on the right bank, false otherwise
* @param ca
age if the ca
age is on the right bank, false otherwise
* @param wolf if the wolf is on the right bank, false otherwise
* @param farmer if the farmer is on the right bank, false otherwise
* @return true if the solution is consistent, false otherwise
*
private static boolean checkConsistency(boolean sheep, boolean ca
age, boolean wolf, boolean farmer) {
XXXXXXXXXXif (sheep == ca
age && sheep != farmer) {
XXXXXXXXXXreturn false;
} else if (sheep == wolf && sheep != farmer) {
XXXXXXXXXXreturn false;
}
XXXXXXXXXXreturn true;
}
/**
* Prints out the solution of the sheep-ca
age-wolf problem
* @param solution
*
private static void printSolution(Deque solution) {
XXXXXXXXXXwhile (!solution.isEmpty()) {
XXXXXXXXXXSystem.out.print(solution.pollLast() + " ");
}
XXXXXXXXXXSystem.out.println();
}
public static void main(String[] args) {
XXXXXXXXXXsolveSheepCa
ageWolf();
}
}