Related
Given the game http://mypuzzle.org/sliding, which starts with square grid containing tiles with numbers
from 1 to N and one empty block represented with X.The goal is to put the tiles according to their numbers.The
moving is done by moving a tile from left,right,top,bottom to the position of the empty tile.I have to solve this problem using IDA* and Manhattan approach
My goal is to output
1.On the first row output the length of the "optimal" path from the start to the destination state
2.The steps which solve the problem in order to reach our final state. The steps are left,right,up,down
Here is my current code working on solution for 3x3 :
/**
*
* Problem Name: Eight Algorithm: IDA* (Iterative deepening A*) using Manhattan Distance Heuristic
* Source: AI: A Modern Approach
*
*/
// Beware my messy code follows!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
*
*
*
*
* #class used to keep State Machine of Eight Puzzle with f,g,h along with board
*
*/
class StateEightPuzzle {
private int f, g, h;
private StringBuilder stateofBoard;
private int xEmptyTile, yEmptyTile;
private int pre;
public void setStateofBoard(StringBuilder stateofBoard) {
this.stateofBoard = stateofBoard;
}
public void setPre(int pre) {
this.pre = pre;
}
public void setXEmptyTile(int xEmptyTile) {
this.xEmptyTile = xEmptyTile;
}
public void setYEmptyTile(int yEmptyTile) {
this.yEmptyTile = yEmptyTile;
}
public void setF(int f) {
this.f = f;
}
public void setG(int g) {
this.g = g;
}
public void setH(int h) {
this.h = h;
}
public int getPre() {
return pre;
}
public int getXEmptyTile() {
return xEmptyTile;
}
public int getYEmptyTile() {
return yEmptyTile;
}
public StringBuilder getStateofBoard() {
return stateofBoard;
}
public int getF() {
return f;
}
public int getG() {
return g;
}
public int getH() {
return h;
}
}
/**
*
* #class used as return type where flimit is used as current flimit and sol indicates whether
* solution is found
*
*/
class ReturnResult {
int flimit;
StateEightPuzzle sol;
}
public class Main {
/**
* #param args
*/
static int flag;
static int[] tracePath;
static private int ROWSIZE = 3;
static private int BOARDSIZE = 9;
/*
*
* function used to see whether the current Eight puzzle can be solvable that is can we subjugate
* our beloved using inversion permutation
*/
public static boolean isSolvable(String dpState) {
int inversion = 0;
for (int i = 0; i < BOARDSIZE; i++)
for (int j = i + 1; j < BOARDSIZE; j++)
if (dpState.charAt(i) > '0' && dpState.charAt(j) > '0'
&& dpState.charAt(i) > dpState.charAt(j))
inversion++;
if (inversion % 2 == 1)
return false;
else
return true;
}
/*
*
* getManhattanDistance returns Manhattan distance between current Node of Eight puzzle State
* Machine and Goal State(Final Destination)
*/
public static int getManhattanDistance(StringBuilder knightBoard) {
int manhattanVal = 0;
for (int i = 0; i < ROWSIZE; i++)
for (int j = 0; j < ROWSIZE; j++) {
int pos = i * ROWSIZE + j;
int val = (knightBoard.charAt(pos) - '0') - 1;
if (val == -1)
continue;
manhattanVal = manhattanVal + Math.abs((val / ROWSIZE) - i) + Math.abs((val % ROWSIZE) - j);
}
return manhattanVal;
}
/**
*
*
*
* #param stat
* #param x
* #param y
* #return
*
* function used to generate next State Machine of Eight Puzzle aka Successor Node(Child
* Node) of Current State(Father Node)
*
*/
public static StateEightPuzzle findSuccessor(StateEightPuzzle fatherNode, int x, int y, int pre) {
int nextXCordiante, nextYCordiante;
nextXCordiante = fatherNode.getXEmptyTile();
nextYCordiante = fatherNode.getYEmptyTile();
StateEightPuzzle childNode = new StateEightPuzzle();
if ((nextXCordiante + x) < 0 || (nextYCordiante + y) < 0 || (nextXCordiante + x) > (ROWSIZE - 1)
|| (nextYCordiante + y) > (ROWSIZE - 1)) {
flag = 0;
return childNode;
}
int nextEmptyTile = (nextXCordiante + x) * ROWSIZE + (nextYCordiante + y);
StringBuilder s1 = new StringBuilder(fatherNode.getStateofBoard());
char ch = s1.charAt(nextEmptyTile);
s1.setCharAt(nextEmptyTile, '0');
s1.setCharAt(ROWSIZE * nextXCordiante + nextYCordiante, ch);
childNode.setStateofBoard(s1);
childNode.setXEmptyTile(nextXCordiante + x);
childNode.setYEmptyTile(nextYCordiante + y);
childNode.setG(fatherNode.getG() + 1);
childNode.setH(getManhattanDistance(s1));
childNode.setPre(pre);
int maxfValue = (fatherNode.getF()) > (childNode.getG() + childNode.getH()) ? fatherNode.getF()
: (childNode.getG() + childNode.getH());
childNode.setF(maxfValue);
flag = 1;
return childNode;
}
/**
*
* #param init
* #param flimit
* #param res
* #return function known as Iterative Deepening DFS for A* which uses f-cost for limiting it
* search depth. Once the search inside a given contour has been completed , a new
* iteration is started with new f-cost for the next DFS-CONTOUR.pls consult Artificial
* Intelligence A Modern Approach, 1st edition by Approach By Russell
*
*/
public static ReturnResult DFS_CONTOUR(StateEightPuzzle Node, int flimit, ReturnResult res) {
int newf = Integer.MAX_VALUE;
ReturnResult resTemp = new ReturnResult();
StateEightPuzzle stat;
tracePath[Node.getG()] = Node.getPre();
// distance matrix for Eight Puzzle which helps to get successor
int dist[][] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
// if current node exceeds flimit return it as flimit
if (Node.getF() > flimit) {
resTemp.flimit = Node.getF();
resTemp.sol = null;
return resTemp;
}
// I see : IDA* is going to give me juicy cool!
if (Node.getH() == 0) {
resTemp.flimit = flimit;
resTemp.sol = Node;
String sol = "uldr";
for (int i = 1; i <= Node.getG(); i++)
System.out.print(sol.charAt(tracePath[i]));
System.out.println("");
return resTemp;
}
// create next valid successor
for (int i = 0; i < 4; i++) {
if (Math.abs(i - Node.getPre()) == 2)
continue;
stat = findSuccessor(Node, dist[i][0], dist[i][1], i);
if (flag == 0)
continue;
resTemp = DFS_CONTOUR(stat, flimit, res);
if (resTemp.sol != null) {
resTemp.flimit = res.flimit;
return resTemp;
}
newf = resTemp.flimit < newf ? resTemp.flimit : newf;
}
resTemp.flimit = newf;
resTemp.sol = null;
return resTemp;
}
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s_2;
while ((s_2 = in.readLine()) != null) {
String str = "";
tracePath = new int[1000];
int emptySquare = 0;
String[] s2 = s_2.split("\\s+");
for (int i = 0; i < s2.length; i++) {
if (s2[i].equals("x") == false)
str += s2[i];
else
str += "0";
}
if (isSolvable(str) == false) {
System.out.println("unsolvable");
} else {
StringBuilder str_bld = new StringBuilder(str);
for (int i = 0; i < str_bld.length(); i++)
if (str_bld.charAt(i) == '0') {
emptySquare += i;
break;
}
// Create Initial Eight puzzle State Machine and let him dance
// with
// prima donna :-)
StateEightPuzzle init = new StateEightPuzzle();
init.setStateofBoard(str_bld);
init.setG(0);
init.setH(getManhattanDistance(str_bld));
init.setF(init.getH() + init.getG());
init.setXEmptyTile(emptySquare / ROWSIZE);
init.setYEmptyTile(emptySquare % ROWSIZE);
init.setPre(5);
int flimit = init.getF();
ReturnResult result = new ReturnResult();
result.flimit = flimit;
result.sol = init;
// loop loop i m a strange loop living a mundane life with hopes
for (;;) {
ReturnResult resTemp = DFS_CONTOUR(init, flimit, result);
if (resTemp.sol != null) {
break;
}
flimit = resTemp.flimit;
}
}
}
}
}
The problem I find is for example for the puzzle when inputed 1 2 3 4 5 6 x 7 8 , it
should input just "ll" moving the "x" tile to the right two times however it inputs "urdlurdlurdr"
any ideas where the problem can be?
In my method newminimax499 I have a minimax algorithm that utilizes memoization and alpha beta pruning. The method works normally for 3x3 games, however when I play 4x4 games I get strange, unexpected position choices for the computer. He still never loses, but he doesn't seem to be playing to win. To illustrate the problem here is a scenario from 2 games in 3x3 and 4x4. First here is a scenario from a 3x3 game where the player is X and makes the first move:
This isn't bad, in fact it's what one would expect the computer to do. Now take a look at a scenario from a 4x4 game. Again O is the computer and X starts:
As you can see, the computer is simply placing Os in a systematic order one after the other and only breaking that order to block X when it has a potential win. This is very defensive play, unlike what was seen in the 3x3 game. So why is the method behaving differently for 3x3 and 4x4?
Here is the code:
//This method returns a 2 element int array containing the position of the best possible
//next move and the score it yields. Utilizes memoization and alpha beta
//pruning to achieve better performance.
public int[] newminimax499(int a, int b){
//int bestScore = (turn == 'O') ? +9 : -9; //X is minimizer, O is maximizer
int bestPos=-1;
int alpha= a;
int beta= b;
int currentScore;
//boardShow();
String stateString = "";
for (int i=0; i<state.length; i++)
stateString += state[i];
int[] oldAnswer = oldAnswers.get(stateString);
if (oldAnswer != null)
return oldAnswer;
if(isGameOver()!='N'){
int[] answer = {score(), bestPos};
oldAnswers.put (stateString, answer);
return answer;
}
else{
for(int x:getAvailableMoves()){
if(turn=='X'){ //X is minimizer
setX(x);
//System.out.println(stateID++);
currentScore = newminimax499(alpha, beta)[0];
revert(x);
if(currentScore<beta){
beta=currentScore;
bestPos=x;
}
if(alpha>=beta){
break;
}
}
else { //O is maximizer
setO(x);
//System.out.println(stateID++);
currentScore = newminimax499(alpha, beta)[0];
revert(x);
if(currentScore>alpha){
alpha=currentScore;
bestPos=x;
}
if(alpha>=beta){
break;
}
}
}
}
if(turn=='X'){
int[] answer = {beta, bestPos};
oldAnswers.put (stateString, answer);
return answer;
}
else {
int[] answer = {alpha, bestPos};
oldAnswers.put (stateString, answer);
return answer;
}
}
Following are the other components and complementary methods needed for you guys to run the code.
The fields and constructor used in my class State2:
private char [] state; //Actual content of the board
private char turn; //Whose turn it is
private Map<String,int[]> oldAnswers; //Used for memoization. It saves every state along with the score it yielded which allows us to stop exploring the children of a certain node if a similar node's score has been previously calculated. The key is the board state(i.e OX------X for example), the int array is a 2 element array containing the score and position of last placed seed of the state.
private Map<Integer, int []> RowCol; //A mapping of positions from a board represented as a normal array to a board represented as a 2d array. For example: The position 0 maps to 0,0 on a 2d array board, 1 maps to 0,1 and so on.
private static int n; //Size of the board
private static int stateID; //An simple incrementer used to show number of recursive calls in the newminiax49 method.
private static int countX, countO; //Number of placed Xs and Os
private static int lastAdded; //Position of last placed seed
private char [][] DDState; //A 2d array representing the board. Contains the same values as state[]. Used for simplicity in functions that check the state of the board.
public State2(int n){
int a=0;
State2.n=n;
state=new char[n*n];
RowCol=new HashMap<Integer, int []>();
countX=0;
countO=0;
//Initializing the board with empty slots
for(int i = 0; i<state.length; i++){
state[i]='-';
}
//Mapping
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
RowCol.put(a, new int[]{i, j});
a++;
}
}
a=0;
DDState=new char[n][n];
//Initializing the 2d array with the values from state[](empty slots)
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
DDState[i][j]=state[a];
a++;
}
}
oldAnswers = new HashMap<String,int[]>();
}
Complementary methods:
getAvailableMoves, returns an array with the empty slots on the board(i.e. the possible next moves).
public int[] getAvailableMoves(){
int count=0;
int i=0;
for(int j=0; j<state.length; j++){
if(state[j]=='-')
count++;
}
int [] availableSlots = new int[count];
for(int j=0; j<state.length; j++){
if(state[j]=='-')
availableSlots[i++]=j;
}
return availableSlots;
}
isGameOver2(), simply checks the current state of the board for whether the game is over. returns a char 'X', 'O', 'D' and 'N' which stand for X won, O won, Draw, and Not gameover respectively.
public char isGameOver2(){
char turnOpp;
int count;
if(turn=='X'){
count=countO;
turnOpp='O';
}
else {
count=countX;
turnOpp='X';
}
if(count>=n){
for(int i=0; i<n; i++){
if(DDState[i][RowCol.get(lastAdded)[1]]!=turnOpp)
break;
if(i==(n-1)){
return turnOpp;
}
}
//Check row for win
for(int i=0; i<n; i++){
if(DDState[RowCol.get(lastAdded)[0]][i]!=turnOpp)
break;
if(i==(n-1)){
return turnOpp;
}
}
//Check diagonal for win
if(RowCol.get(lastAdded)[0] == RowCol.get(lastAdded)[1]){
//we're on a diagonal
for(int i = 0; i < n; i++){
if(DDState[i][i] != turnOpp)
break;
if(i == n-1){
return turnOpp;
}
}
}
//check anti diagonal
for(int i = 0; i<n; i++){
if(DDState[i][(n-1)-i] != turnOpp)
break;
if(i == n-1){
return turnOpp;
}
}
//check for draw
if((countX+countO)==(n*n))
return 'D';
}
return 'N';
}
boardShow, returns a matrix display of the current state of the board:
public void boardShow(){
if(n==3){
System.out.println(stateID);
for(int i=0; i<=6;i+=3)
System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]");
System.out.println("***********");
}
else {
System.out.println(stateID);
for(int i=0; i<=12;i+=4)
System.out.println("["+state[i]+"]"+" ["+state[i+1]+"]"+" ["+state[i+2]+"]"+" ["+state[i+3]+"]");
System.out.println("***********");
}
}
score, is a simple evaluation function which returns +10 for an O win, -10 for an X win and 0 for a draw:
public int score(){
if(isGameOver2()=='X')
return -10;
else if(isGameOver2()=='O')
return +10;
else
return 0;
}
The seed setters:
//Sets an X at a certain location and updates the turn, countX and lastAdded variables
public void setX(int i){
state[i]='X';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='X';
turn='O';
countX++;
lastAdded=i;
}
//Sets an O at a certain location and updates the turn, countO and lastAdded variables
public void setO(int i){
state[i]='O';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='O';
turn='X';
countO++;
lastAdded=i;
}
Revert, simply reverts a move. For example if an X has been placed in position 0 revert(0) sets a '-' in it's place and updates the variables changed by setX:
public void revert(int i){
state[i]='-';
DDState[RowCol.get(i)[0]][RowCol.get(i)[1]]='-';
if(turn=='X'){
turn = 'O';
countO--;
}
else {
turn = 'X';
countX--;
}
}
How the main method might look like:
public static void main(String[] args) {
State2 s=new State2(4);
int [] results=new int[2];
s.setX(0);
long startTime = System.currentTimeMillis();
results=s.newminimax499(Integer.MIN_VALUE,Integer.MAX_VALUE);
long endTime = System.currentTimeMillis();
System.out.println("Score: "+results[0]+" Position: "+ results[1]);
System.out.println("Run time: " + (endTime-startTime));
s.boardShow();
}
I'm not convinced that there's a bug here -- if O plays in one of the earlier positions, it gets forked, whereas if it plays in the center, it forces a draw. Presumably the 4x4 game is even harder to win/lose, so the computer indifferently chooses the first open square.
Below, 1 denotes the forced response by O, 2 denotes the forking move by X, and ? denotes a possible win location.
X|O|
-+-+-
2|X|?
-+-+-
?| |1
X| |O
-+-+-
X|2|?
-+-+-
1| |?
X|2|?
-+-+-
O|X|
-+-+-
|?|1
I gave up trying to get my head around the code - the room began to get way too noisy for my collegues.
However - I did put together my own solution - which I feel may be worth posting. It is not designed to be especially efficient - in fact I am still waiting for it to make it's first move in the 4X4 test but it does seem to do the right thing on a 3X3 (i.e. draw against itself and win against a stupid strategy).
It should be able to handle boards of unequal sizes. A diagonal is any line of squares reaching from one side of the board to the other using diagonal steps.
As soon as it finishes it's 4X4 run I'll post it's moves for comparison.
public class TicTacToe {
/**
* Strategies for players.
*/
interface Strategy {
/**
* Think up a place to move.
*
* #param board - The current board position.
* #return Where to move.
*/
Optional<Board.Square> think(Board board, Player player);
}
/**
* Stupid strategy just finds the first empty square and plays it.
*/
static final Strategy stupid = (Board board, Player player) -> {
List<Board.Square> empty = board.getEmpties();
if (empty.size() > 0) {
return Optional.of(empty.get(0));
}
return Optional.empty();
};
/**
* Minimax strategy.
*/
static final Strategy minimax = new Strategy() {
// Divide by this at each depth step.
private static final double FACTOR = 3;
// Memoize each discovered best move.
Map<Player, Map<String, Board.Place>> best = new HashMap<>();
{
// One map for each player.
for (Player p : Player.values()) {
best.put(p, new HashMap<>());
}
}
#Override
public Optional<Board.Square> think(Board board, Player player) {
Optional<Board.Square> win = Optional.empty();
// Seen this one before?
Board.Place seen = best.get(player).get(board.toString());
if (seen == null) {
double winValue = Double.NEGATIVE_INFINITY;
for (Board.Square play : board.getEmpties()) {
double value = value(board, player, play, 1);
if (value > winValue) {
win = Optional.of(play);
winValue = value;
}
}
if (win.isPresent()) {
// Record my result.
best.get(player).put(board.toString(), win.get().place);
} else {
System.out.println("No best found for " + board);
}
} else {
// Reuse it.
win = Optional.of(board.square(seen));
}
return win;
}
private double value(Board board, Player player, Board.Square move, double scale) {
// My value.
double value = 0;
// Make the move.
board.mark(player, move);
try {
// A win?
if (!board.win()) {
// Remaining empties.
List<Board.Square> empties = board.getEmpties();
if (!empties.isEmpty()) {
// Record it's value.
double[] values = new double[empties.size()];
for (int i = 0; i < values.length; i++) {
// Try each further move negating and downscaling at each level.
values[i] = value(board, player.next(), empties.get(i), (scale / FACTOR) * -1);
}
// Add them up.
value += DoubleStream.of(values).sum();
}
} else {
// Somebody already won.
value = scale;
}
} finally {
//System.out.println("Value of " + board + " to player " + player + " = " + value);
// Unroll the move.
board.unmark(player, move);
}
return value;
}
};
/**
* There are exactly two players.
*/
enum Player {
O, X;
/**
* Each player has a strategy.
*
* Defaults to stupid.
*/
private Strategy strategy = stupid;
/**
* What mark they make on the board.
*
* #return String representing the mark they make.
*/
public char mark() {
// The mark they make is the first character of their name.
return name().charAt(0);
}
/**
* Who is the next player.
*
* #return The other player.
*/
public Player next() {
return other(this);
}
/**
* Who is the other player.
*
* #return The other player.
*/
static Player other(Player it) {
return it == O ? X : O;
}
public Strategy getStrategy() {
return strategy;
}
public Player setStrategy(Strategy strategy) {
this.strategy = strategy;
return this;
}
}
/**
* The board.
*/
static class Board {
/**
* Top left corner is 0,0. Access is [y][x].
*/
final Square[][] board;
// The board as columns.
final List<List<Square>> columns;
// The dagonals.
final List<List<Square>> diagonals;
// For sense checking.
final int width;
final int height;
// Counts for each player - useful optimisation.
final int[] counts = new int[Player.values().length];
// A clear square.
private static final char Clear = ' ';
public Board(int width, int height) {
this.width = width;
this.height = height;
board = new Square[height][];
for (int y = 0; y < height; y++) {
board[y] = new Square[width];
// Fill it.
for (int x = 0; x < width; x++) {
board[y][x] = new Square(x, y);
}
}
// Build my columns lists.
columns = new ArrayList<>();
for (int x = 0; x < width; x++) {
List<Square> column = new ArrayList<>();
for (int y = 0; y < height; y++) {
column.add(board[y][x]);
}
columns.add(column);
}
// And the diagonals.
diagonals = new ArrayList<>();
for (int y = 0; y <= height - width; y++) {
List<Square> diagonal = new ArrayList<>();
// Left to right.
for (int x = 0; x < width; x++) {
diagonal.add(board[y + x][x]);
}
diagonals.add(diagonal);
// Right to left.
diagonal = new ArrayList<>();
for (int x = 0; x < width; x++) {
diagonal.add(board[y + x][width - 1 - x]);
}
diagonals.add(diagonal);
}
}
public Board(int size) {
this(size, size);
}
private Stream<List<Square>> asRows() {
// Map each row to a list.
return Arrays.stream(board).map(r -> Arrays.asList(r));
}
private Stream<List<Square>> asCols() {
// Walk the columns.
return columns.stream();
}
private Stream<List<Square>> asDiagonals() {
// Walk the diagonals.
return diagonals.stream();
}
private boolean winning(List<Square> row) {
//System.out.println("winner(" + row + ")");
if (row.isEmpty()) {
return false;
}
Optional<Player> owner = row.get(0).owner;
// First square owned.
if (!owner.isPresent()) {
return false;
}
return !row.stream()
//.peek(s -> System.out.println(s))
.anyMatch((s) -> (!s.owner.isPresent() || s.owner.get() != owner.get()));
}
public Square square(Place place) {
return board[place.y][place.x];
}
private Optional<Player> getWinner() {
Optional<Player> winner = Optional.empty();
// Must be at least enough plays by one player to reach across/down the board.
OptionalInt min = IntStream.of(counts).min();
if (!min.isPresent() || min.getAsInt() < Math.min(width, height)) {
// Nobody can posibly have won.
return winner;
}
List<List<Square>> winners = asRows()
.filter(r -> winning(r))
.collect(Collectors.toList());
if (winners.isEmpty()) {
winners = asCols()
.filter(r -> winning(r))
.collect(Collectors.toList());
}
if (winners.isEmpty()) {
winners = asDiagonals()
.filter(r -> winning(r))
.collect(Collectors.toList());
}
if (!winners.isEmpty()) {
winner = winners.get(0).get(0).owner;
}
return winner;
}
private boolean isDrawn() {
// All square occupied - counts add up to width*height.
return IntStream.of(counts).sum() == width * height;
}
private boolean win() {
return getWinner().isPresent();
}
/**
* One square of the board.
*
* Remember that a Square is ON a board - i.e. it is a sub-object of a Board. Do not attempt to memoize Squares as they will hold a reference to their parent board.
*/
class Square {
// The owner of it.
Optional<Player> owner = Optional.empty();
// where it is on the board.
final Place place;
public Square(Place place) {
this.place = place;
}
public Square(int x, int y) {
this(new Place(x, y));
}
public char getMark() {
return owner.isPresent() ? owner.get().mark() : Clear;
}
public boolean isTaken() {
return owner.isPresent();
}
#Override
public String toString() {
return place + "(" + (owner.isPresent() ? owner.get().toString() : "") + ")";
}
private void take(Player player) {
if (!isTaken()) {
owner = Optional.of(player);
// Keep track of the counts.
counts[player.ordinal()] += 1;
} else {
throw new IllegalStateException("Attempt to take taken square!"
+ " Square=" + this
+ " Player=" + player
+ " board=" + Board.this.toString()
);
}
}
private void release(Player player) {
if (isTaken()) {
if (owner.get() == player) {
owner = Optional.empty();
// Keep track of the counts.
counts[player.ordinal()] -= 1;
} else {
throw new IllegalStateException("Attempt to release other player's square!"
+ " Square=" + this
+ " Player=" + player
+ " board=" + Board.this.toString()
);
}
} else {
throw new IllegalStateException("Attempt to release untaken square!"
+ " Square=" + this
+ " Player=" + player
+ " board=" + Board.this.toString()
);
}
}
}
private List<Square> getEmpties() {
// Walk all squares.
return Arrays.stream(board)
// Unroll each row.
.flatMap(l -> Arrays.stream(l))
// All not taken.
.filter(s -> !s.isTaken())
// As a list.
.collect(Collectors.toList());
}
/**
* A place on the board.
*/
class Place {
final int x;
final int y;
public Place(int x, int y) {
// Sense check.
if (x < 0 || x >= width) {
throw new IllegalArgumentException("Off board: x = " + x);
}
if (y < 0 || y >= height) {
throw new IllegalArgumentException("Off board: x = " + x);
}
this.x = x;
this.y = y;
}
#Override
public String toString() {
return "{" + x + "," + y + '}';
}
}
/**
* Mark a place on the board.
*
* #param player
*/
public void mark(Player player, Square square) {
// Take the square.
square.take(player);
}
/**
* UnMark a place on the board.
*
* Confirms the mark first.
*/
public void unmark(Player player, Square square) {
square.release(player);
}
#Override
public String toString() {
return Arrays.stream(board)
.map(r -> Arrays.stream(r)
.map(s -> String.valueOf(s.getMark()))
.collect(Collectors.joining("", "[", "]")))
.collect(Collectors.joining(""));
}
}
class Game {
// The current board state.
final Board board;
public Game(Board board) {
this.board = board;
}
public Game(int size) {
this(new Board(size));
}
public Game(int width, int height) {
this(new Board(width, height));
}
private void play(Player firstPlayer) {
boolean gameFinished = false;
Player player = firstPlayer;
do {
Optional<Board.Square> move = player.getStrategy().think(board, player);
if (move.isPresent()) {
board.mark(player, move.get());
System.out.println("Moved: " + move.get() + " -> " + board);
} else {
System.out.println("No move!");
}
// Has anyone won?
Optional<Player> winner = board.getWinner();
if (winner.isPresent()) {
System.out.println("Player " + winner.get() + " won! " + board);
gameFinished = true;
} else {
if (board.isDrawn()) {
System.out.println("Draw! " + board);
gameFinished = true;
}
}
// Next player.
player = player.next();
} while (!gameFinished);
}
}
private void testStupid() {
// Both start off stupid.
new Game(3).play(Player.X);
}
private void testMinimax() {
// Test minimax strategy.
Board testBoard = new Board(3);
// Set it up like http://neverstopbuilding.com/minimax
testBoard.mark(Player.O, testBoard.board[0][0]);
testBoard.mark(Player.X, testBoard.board[0][2]);
testBoard.mark(Player.X, testBoard.board[1][0]);
testBoard.mark(Player.X, testBoard.board[2][0]);
testBoard.mark(Player.O, testBoard.board[2][1]);
testBoard.mark(Player.O, testBoard.board[2][2]);
// What does the strategy do?
Optional<Board.Square> play = minimax.think(testBoard, Player.X);
System.out.println("minimax(" + testBoard + ") = " + play);
}
private void test3X3Game() {
// Play a game.
// Change strategies.
new Game(3).play(Player.X);
}
private void test4X4Game() {
// Play a game.
new Game(4).play(Player.X);
}
public void test() {
testStupid();
testMinimax();
System.out.println("Test minmax vs stupid");
Player.X.setStrategy(minimax);
test3X3Game();
System.out.println("Test minmax vs minmax");
Player.O.setStrategy(minimax);
test3X3Game();
System.out.println("Test 4 X 4");
test4X4Game();
}
public static void main(String args[]) {
try {
new TicTacToe().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
When playing minimax against itself it moves:
Moved: {1,1}(X) -> [ ][ X ][ ]
Moved: {0,0}(O) -> [O ][ X ][ ]
Moved: {2,2}(X) -> [O ][ X ][ X]
Moved: {0,2}(O) -> [O ][ X ][O X]
Moved: {0,1}(X) -> [O ][XX ][O X]
Moved: {2,1}(O) -> [O ][XXO][O X]
Moved: {1,0}(X) -> [OX ][XXO][O X]
Moved: {1,2}(O) -> [OX ][XXO][OOX]
Moved: {2,0}(X) -> [OXX][XXO][OOX]
Draw! [OXX][XXO][OOX]
Note that this os not the moves OP posted so there is clearly something wrong with OP's algorithm.
I am making a tic tac toe game for n number of players on a nxn board, but the winning condition is aways 3 on a row. My so far solution to the problem is: when a move is made the program will check the following square for 3 on a row.
(x-1,y+1) (x,y+1) (x+1,y+1)
(x-1,y) (x,y) (x+1,y)
(x-1,y-1) (x,y-1) (x+1,y-1)
It will check the top (x-1,y+1) (x,y+1) (x+1,y+1) bottom(x-1,y-1) (x,y-1) (x+1,y-1)
sides(x+1,y+1) (x+1,y) (x+1,y-1) , (x-1,y+1) (x-1,y) (x-1,y-1) , the diagonals and the ones going through the middle(x,y).
my code so far is:
public int checkWinning() {
for(int a = 1; a < size-1; a++){
for(int b = 1; b < size-1; b++){
if (board[a][b] == board[a+1][b] && board[a][b] == board[a-1][b]){
return board[a][b];
}else if(board[a][b] == board[a][b+1] && board[a][b] == board[a][b-1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b-1] && board[a][b] == board[a-1][b+1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b+1] && board[a][b] == board[a-1][b-1]){
return board[a][b];
}
}
}
for(int d = 1; d < size-1; d++){
if (board[0][d] == board[0][d-1] && board[0][d] == board[0][d+1]){
return board[0][d];
} else if (board[size-1][d] == board[size-1][d-1] && board[size-1][d] == board[size-1][d+1]){
return board[size-1][d];
}
}
for(int c = 1; c < size-1; c++){
if (board[c][0] == board[c-1][0] && board[c][0] == board[c+1][0]){
return board[c][0];
}else if(board[c][size-1] == board[c-1][size-1] && board[c][size-1] == board[c+1][size-1]){
return board[c][size-1];
}
}
return 0;
}
where the first section is where I check the ones through the middle and diagonals. the second section I check the top an bottom and the top and the thrid section checks the sides.
When it returns 0 is means that there are no winner yet.
#override
public void checkResult() {
int winner = this.board.checkWinning();
if (winner > 0) {
this.ui.showResult("Player "+winner+" wins!");
}
if (this.board.checkFull()) {
this.ui.showResult("This is a DRAW!");
}
}
Board[x][y] -> 2-dimensional array representing the board, The coordinates are counted from top-left (0,0) to bottom-right (size-1, size-1), board[x][y] == 0 signifies free at position (x,y), board[x][y] == i for i > 0 signifies that Player i made a move on (x,y), just so you know it.
my problem is that when i expands the board to a size larger than 3x3 the program somehow overwrites it self or a does not check every thing sides top and bottom every time, and I can't seem too se why.
EDIT:
played with the app for a few minutes... interesting results
java -jar tic-tac-toe.jar 5 20
It was a cats game!!
|1|1|5|5|1|3|5|3|1|5|2|5|1|1|2|
|2|3|2|3|1|5|3|5|3|2|3|1|5|2|2|
|5|4|5|4|1|5|5|4|2|1|4|5|4|2|2|
|3|2|1|5|5|5|2|4|5|3|4|1|2|4|2|
|3|4|1|2|5|4|1|1|4|5|1|3|3|4|1|
|1|5|4|4|3|2|5|1|3|5|1|3|5|3|4|
|2|5|1|4|3|3|3|5|3|1|1|4|3|4|4|
|1|4|5|1|1|5|4|5|2|4|1|1|5|4|3|
|1|3|2|1|4|2|4|3|3|4|5|2|4|3|3|
|5|1|1|3|3|4|4|4|2|2|1|4|3|2|5|
|2|2|3|1|5|5|4|1|3|5|3|2|3|3|2|
|2|4|2|4|4|1|3|1|1|3|1|2|1|2|2|
|2|5|5|1|4|3|4|5|5|4|5|3|3|5|2|
|4|5|2|1|5|3|2|1|3|2|2|2|2|4|4|
|4|1|1|4|5|4|5|4|2|2|3|3|2|2|3|
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 0
Number wins by Player3: 0
Number wins by Player4: 0
Number wins by Player5: 0
Number of ties: 100
didn't scroll through all 100 games to find the winning board, but I thought this was interesting:
java -jar tic-tac-toe.jar 2 10
Player2 won the game!
|1|1|2|1|2|2| |2|1|2|
|2|2|2|2|2|2|2|2|2|2|
|2|1|2|2|2|1|1|1|1|1|
|1|1|1|1|2|1|2|1|1|1|
|2|2| |1|2|1|1|1|1|2|
|2|2|2|1|1|1| |1|2|2|
|2|2|1|2|2|2|2|2|1|1|
| | |2|2|2|2| |1|1|1|
|1|1|2|2|2|1|1|1|1| |
| | |1|1|1|1|1|2|1| |
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 1
Number of ties: 99
This does answer your question... but I took it a bit far... decided to implement the solution.
Instead of counting matches... I just check from teh point the last player plays, if all marks in a row column and diagnal match the players, he wins.
package com.clinkworks.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TicTacToe {
private static final String TIE = "TIE";
private static final Map<String, Integer> gamesToWinsMap = new HashMap<String, Integer>();
/**
* accepts input in the following format:
*
* playerCount rowCount columnCount (sets the game with the n players, n columns, and n rows)
* - java -jar tic-tac-toe.jar 2 3 3
* PlayerCount squareSize (defaults to a game with rows and cols the same as squareSize and the player count given)
* - java -jar tic-tac-toe.jar 2 3
* PlayerCount (defaults to a 3 by 3 game)
* - java -jar tic-tac-toe.jar 2
* no input (defaults to a 3 by 3 game with 2 players)
* - java -jar tic-tac-toe.jar
* #param args
*/
public static void main(String[] args) {
int playerCount = 2;
int rows = 3;
int cols = 3;
if(args.length == 3){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = Integer.valueOf(args[2]);
}
if(args.length == 2){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = rows;
}
if(args.length == 1){
playerCount = Integer.valueOf(args[0]);
}
for(int i = 1; i <= playerCount; i++){
gamesToWinsMap.put("Player" + i, 0);
}
//lets play 100 games and see the wins and ties
playGames(100, playerCount, rows, cols);
for(int i = 1; i <= playerCount; i++){
System.out.println("Number wins by Player" + i + ": " + gamesToWinsMap.get("Player" + i));
}
System.out.println("Number of ties: " + gamesToWinsMap.get(TIE));
}
public static void playGames(int gamesToPlay, int playerCount, int rows, int cols) {
//play a new game each iteration, in our example, count = 100;
for (int i = 0; i < gamesToPlay; i++) {
playGame(playerCount, rows, cols);
}
}
public static void playGame(int playerCount, int rows, int cols) {
//create a new game board. this initalizes our 2d array and lets the complexity of handling that
// array be deligated to the board object.
Board board = new Board(playerCount, rows, cols);
//we are going to generate a random list of moves. Heres where we are goign to store it
List<Move> moves = new ArrayList<Move>();
//we are creating moves for each space on the board.
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
moves.add(new Move(row, col));
}
}
//randomize the move list
Collections.shuffle(moves);
//do each move
for (Move move : moves) {
board.play(move);
if(gameOver(board)){
break;
}
}
}
public static boolean gameOver(Board board){
if (board.whoWon() != null) {
System.out.println(board.whoWon() + " won the game!");
System.out.println(board);
Integer winCount = gamesToWinsMap.get(board.whoWon());
winCount = winCount == null ? 1 : winCount + 1;
gamesToWinsMap.put(board.whoWon(), winCount);
return true;
} else if (board.movesLeft() == 0) {
System.out.println("It was a cats game!!");
System.out.println(board);
Integer tieCount = gamesToWinsMap.get(TIE);
tieCount = tieCount == null ? 1 : tieCount + 1;
gamesToWinsMap.put(TIE, tieCount);
return true;
}
return false;
}
public static class Move {
private int row;
private int column;
public Move(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() {
return row;
}
public int getColumn() {
return column;
}
}
public static class Board {
private final int rowSize;
private final int columnSize;
private final Integer[][] gameBoard;
private int playerCount;
private int currentPlayer;
private String winningPlayer;
public Board() {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = 2;
}
public Board(int players) {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = players;
}
public Board(int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = 2;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
public Board(int players, int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = players;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
/**
*
* #return the amount of empty spaces remaining on the game board, or if theres a winning player, zero.
*/
public int movesLeft() {
if(whoWon() != null){
return 0;
}
int moveCount = 0;
for (int x = 0; x < getRowSize(); x++) {
for (int y = 0; y < getColumnSize(); y++) {
moveCount += getMoveAt(x, y) == null ? 1 : 0;
}
}
return moveCount;
}
/**
* If someone won, this will return the winning player.
*
* #return the winning player
*/
public String whoWon() {
return winningPlayer;
}
/**
* This move allows the next player to choose where to place their mark.
*
* #param row
* #param column
* #return if the game is over, play will return true, otherwise false.
*/
public boolean play(Move move) {
if (!validMove(move)) {
// always fail early
throw new IllegalStateException("Player " + getCurrentPlayer() + " cannot play at " + move.getRow() + ", " + move.getColumn() + "\n" + toString());
}
doMove(move);
boolean playerWon = isWinningMove(move);
if (playerWon) {
winningPlayer = "Player" + getCurrentPlayer();
return true;
}
shiftPlayer();
boolean outOfMoves = movesLeft() <= 0;
return outOfMoves;
}
public int getRowSize() {
return rowSize;
}
public int getColumnSize() {
return columnSize;
}
public int getCurrentPlayer() {
return currentPlayer;
}
public Integer getMoveAt(int row, int column) {
return gameBoard[row][column];
}
private void doMove(Move move) {
gameBoard[move.getRow()][move.getColumn()] = getCurrentPlayer();
}
private void shiftPlayer() {
if(getCurrentPlayer() == getPlayerCount()){
currentPlayer = 1;
}else{
currentPlayer++;
}
}
private int getPlayerCount() {
return playerCount;
}
private boolean validMove(Move move) {
boolean noMoveAtIndex = false;
boolean indexesAreOk = move.getRow() >= 0 || move.getRow() < getRowSize();
indexesAreOk = indexesAreOk && move.getColumn() >= 0 || move.getColumn() < getColumnSize();
if (indexesAreOk) {
noMoveAtIndex = getMoveAt(move.getRow(), move.getColumn()) == null;
}
return indexesAreOk && noMoveAtIndex;
}
private boolean isWinningMove(Move move) {
// since we check to see if the player won on each move
// we are safe to simply check the last move
return winsDown(move) || winsAcross(move) || winsDiagnally(move);
}
private boolean winsDown(Move move) {
boolean matchesColumn = true;
for (int i = 0; i < getColumnSize(); i++) {
Integer moveOnCol = getMoveAt(move.getRow(), i);
if (moveOnCol == null || getCurrentPlayer() != moveOnCol) {
matchesColumn = false;
break;
}
}
return matchesColumn;
}
private boolean winsAcross(Move move) {
boolean matchesRow = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnRow = getMoveAt(i, move.getColumn());
if (moveOnRow == null || getCurrentPlayer() != moveOnRow) {
matchesRow = false;
break;
}
}
return matchesRow;
}
private boolean winsDiagnally(Move move) {
// diagnals we only care about x and y being teh same...
// only perfect squares can have diagnals
// so we check (0,0)(1,1)(2,2) .. etc
boolean matchesDiagnal = false;
if (isOnDiagnal(move.getRow(), move.getColumn())) {
matchesDiagnal = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnDiagnal = getMoveAt(i, i);
if (moveOnDiagnal == null || moveOnDiagnal != getCurrentPlayer()) {
matchesDiagnal = false;
break;
}
}
}
return matchesDiagnal;
}
private boolean isOnDiagnal(int x, int y) {
if (boardIsAMagicSquare()) {
return x == y;
} else {
return false;
}
}
private boolean boardIsAMagicSquare() {
return getRowSize() == getColumnSize();
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
for(int y = 0; y < getColumnSize(); y++) {
for(int x = 0; x < getRowSize(); x++) {
Integer move = getMoveAt(x, y);
String moveToPrint = "";
if (move == null) {
moveToPrint = " ";
} else {
moveToPrint = move.toString();
}
stringBuffer.append("|").append(moveToPrint);
}
stringBuffer.append("|\n");
}
return stringBuffer.toString();
}
}
}
I have to revise my answer. If you want to have three in a row regardless of your board size, your loop code might be sufficient, but you are always checking whether the values of the fields are the same but never make a difference between empty and non-empty fields.
So “empty” can win too, which would effectively hide a possible win of a player. In other words, your code does not work correctly, even for a field size of three. You didn’t test it enough.
If I initialize the board as
int[][] board={
{ 1, 1, 1 },
{ 0, 0, 0 },
{ 0, 0, 0 },
};
your code returns 0 as the second row contains three zeros. I assumed that 0 represents the empty field but the actual value for “empty” doesn’t matter. You have to exclude empty fields from the three-in-a-row check.
You can simplify this a fair amount by breaking the logic up a bit.
First realize that you only need to check for a win around the piece you just placed.
Now we need a way to check whether that move is a winner.
First we need a simple function to check whether a cell matches a given value, returning true if its within bounds and matches.
private boolean cellMatches(int x, int y, int val) {
if (x<0||x>boardWidth)
return false;
if (y<0||y>boardHeight)
return false;
return board[x][y]==val;
}
Now a function that you give a starting position (x and y) and a delta (dx, dy) and it checks up to two cells in that direction returning a count of how many in a row matched value. The for loop may be overkill for two checks but it would easily allow you to expand up to longer lines being used.
private int countMatches(int x, int y, int dx, int dy, int val) {
int count = 0;
for (int step=1;step<=2;step++) {
if (cellMatches(x+dx*step, y+dy*step, val) {
count++;
} else {
return count;
}
}
return count;
}
Now we can use the previous method. When we place a new piece we can just count out in each matching pair of directions. The combined count is the total number in a row. (i.e. two in a row top + 1 bot = a total run length of 4). If any of those run lengths is three then it is a winning move.
private boolean makeMove(int x, int y, int val) {
board[x][y] = val;
int runlength=countMatches(x,y,0,1,val) + countMatches(x,y,0,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,0,val) + countMatches(x,y,-1,0,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,1,val) + countMatches(x,y,-1,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,-1,val) + countMatches(x,y,-1,1,val);
if (runLength >= 2)
return true;
return false;
}
Note that because we need to count the center piece that we placed we just need a run length of two or more.
I've been working on the code for alpha-beta pruning for a Connect Four game and I can't seem to get it right. I thought I implemented it correctly at first, but the algorithm would return the column 1 every time. I have two ints val and nodeVal that I use to determine my alpha and beta values. When I instantiate them as 0 inside my alphaBetaPruning() algorithm, the column that is returned by the alphaBetaSearch() method is always 1. If I instantiate these values outside of the alphaBetaPruning() algorithm, the alphaBetaSearch() algorithm results in a Null Pointer Exception
.
Note: the ConnectFourBoard class holds the best child of a board state.
Any help would be appreciated!
public class ConnectFourPlayer
{
private final int columns = 7;
private char currentPlayer;
private char computer = 'C';
private char opponent = 'P';
private int depth = 8;
//private int val = 0;
//private int nodeVal = 0;
private int minMax;
/**
* Constructor method creates the computer
* player.
*/
public ConnectFourPlayer()
{
}
/**
* Method returns the number of children
* given a board state.
*/
public int numberOfChildren(ConnectFourBoard board)
{
int count = 0;
//count number of columns with empty slots
for (int col = 0; col < columns; col++)
{
for (int row = 0; row < 6; row++)
{
if ( board.board[row][col] == ' ')
{
count++;
break;
}
}
}
return count;
}
/**
* Method generates the children of a given
* board state and stores them in the array
* boardChildren[]. The array boardChildren
* is the size of the number of possible
* children.
*/
public void generateChildren(ConnectFourBoard board)
{
board.boardChildren = new ConnectFourBoard[this.numberOfChildren(board)];
int childNumber = 0;
for (int boardCol = 0; boardCol < columns; boardCol++)
{
for (int boardRow = 0; boardRow < 6; boardRow++)
{
if ( board.board[boardRow][boardCol] == ' ' )
{
ConnectFourBoard childBoard = new ConnectFourBoard();
for(int row = 0; row < 6; row++)
{
for(int col = 0; col < columns; col++)
{
childBoard.board[row][col] = board.board[row][col];
}
}
childBoard.row = boardRow;
childBoard.column = boardCol;
childBoard.board[boardRow][boardCol] = currentPlayer;
board.boardChildren[childNumber] = childBoard;
childNumber++;
break;
}
}
}
}
/**
* Method implements the alphaBetaPruning algorithm.
* Returns the column that corresponds to that
* column with the highest alphaBetaPruning value.
*/
public int alphaBetaSearch(ConnectFourBoard board)
{
minMax = 1;
currentPlayer = computer;
alphaBetaPruning(board, this.depth, Integer.MIN_VALUE, Integer.MAX_VALUE);
return board.best.column;
}
/**
* Method returns the static evaluation function value of the
* best possible move that will maximize the computer player's
* move value and also minimize the opponent's move value,
* given a depth limit.
*/
public int alphaBetaPruning(ConnectFourBoard board, int depth, int alpha, int beta)
{
//create StaticEvaluationFunction object to evaluate each possible move
StaticEvaluationFunction evaluator = new StaticEvaluationFunction();
int val = 0;
int nodeVal = 0;
//check if the computer is in a goal state
if(board.gameOver() == 'C')
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
//check if depth limit was reached
if(depth == 0)
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
//fill the boardChildren[] array with all children of the current board
this.generateChildren(board);
int width = this.numberOfChildren(board);
//check if the current board state has any children
if(width == 0)
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
else
{
int i = 0;
boolean prune = false;
while((!prune) && (i < width))
{
//recursive call to alphaBetaPruning()
val = alphaBetaPruning(board.boardChildren[i], (depth - 1), alpha, beta);
if(i == 0)
nodeVal = val;
//check if current board is a minNode
if(minNode(board))
{
//check current child < smallest child so far
if(val < nodeVal)
{
//set nodeVal to smaller child
nodeVal = val;
//set best to this child board
board.best = board.boardChildren[i];
}
//check current child < smallest child so far (local)
if(val < beta)
//set beta to smaller child
beta = val;
}
//check if current board is a maxNode
if(maxNode(board))
{
//check current child > biggest child so far
if(val > nodeVal)
{
//set nodeVal to bigger child
nodeVal = val;
//set best to this child board
board.best = board.boardChildren[i];
}
//check current child > biggest child so far (local)
if(val > alpha)
//set alpha to bigger child
alpha = val;
}
//check max so far > min so far
if(alpha > beta)
{
//prune
prune = true;
return nodeVal;
}
else i++;
}//end while
}
return nodeVal;
}
/**
* Method returns true if the given board state is
* a max node.
*/
public boolean maxNode(ConnectFourBoard board)
{
if(minMax == 1)
{
minMax = 0;
currentPlayer = opponent;
return true;
}
return false;
}
/**
* Method returns true if the given board state is
* a min node.
*/
public boolean minNode(ConnectFourBoard board)
{
if(minMax == 0)
{
minMax = 1;
currentPlayer = computer;
return true;
}
return false;
}
}
/**Holds the state of the board*/
public class ConnectFourBoard
{
public final int rows = 6;
public final int columns = 7;
public char board[][] = new char[rows][columns];
public ConnectFourBoard boardChildren[];
public ConnectFourBoard best;
public int column;
public int row;
...
/**
* Method makes the computer player's move. First,
* checks if a move is allowed. If it is, that
* slot is set to the computer player's character.
* Returns true is a move is made and false
* otherwise.
*/
public int generateComputerPlayerMove(ConnectFourPlayer computer)
{
int column = computer.alphaBetaSearch(this);
//update grid
for(int row = 0; row < board.length; row++)
{
if(board[row][column] == ' ')
{
board[row][column] = 'C';
break;
}
}
return column;
}
}
I'm trying to write a program that can generate all the possible magic squares for a fixed N Dimension. I'm going about it by filling the diagonal cells with values and then filling in the rows with values.
I seem to be stuck in an infinite cycle when fillin in the rows, but can't seem to figure out how or why. I haven't implemented the sum check, to check whether the sum of the rows or columns is correct, but that is irrelevent here.
If anyone can help me out, i'd very greatful.
code bellow
public class Magic {
public static final int DIMENSION = 3;
public static final int DIMSQ = DIMENSION * DIMENSION;
public static int[][] array = new int[DIMENSION][DIMENSION];
public static boolean[] boolArray = new boolean[DIMENSION * DIMENSION];
public static final int sum = (DIMENSION * (DIMENSION * DIMENSION + 1)) / 2;
/*
* Inicializaljuk a matrixunkat, illetve a boolean matrixunkat
* Initializes the matrix and boolArray with values.
*/
public static void init() {
for (int e[] : array) {
for (int e2 : e) {
e2 = 0;
}
}
for (boolean e : boolArray) {
e = false;
}
}
/*
* Ki irassa a matrix jelenlegi allapotat konzolra
* Prints the array out to the console.
*/
public static void print() {
for (int i[] : array) {
for (int j : i) {
System.out.print(j + ",");
}
System.out.println();
}
System.out.println();
}
/*
* feltolti a foatlot adatokkal, majd meghivja a diagonal2-t
* fills diagonal cells with values
*/
public static void diagonal1(int x) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
boolArray[i] = true;
array[x][x] = i + 1;
if (x < DIMENSION - 1) {
diagonal1(x + 1);
} else
diagonal2(0);
boolArray[i] = false;
}
}
}
/*
* feltolti a mellekatlot adatokkal, majd meghivja a row(0,0,0)-t
* fills diagonal cells with values
*/
public static void diagonal2(int x) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[DIMENSION - 1 - x][x] == 0) {
boolArray[i] = true;
array[DIMENSION - 1 - x][x] = i + 1;
}
if (x < DIMENSION - 1) {
diagonal2(x + 1);
} else
row(0, 0);
boolArray[i] = false;
}
}
}
/*
* feltolti a sorokat adatokkal
* fills rows with values
*/
public static void row(int x, int y) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[x][y] == 0) {
boolArray[i] = true;
array[x][y] = i;
}
if (x < DIMENSION - 1) {
row(x + 1, y);
} else if(y < DIMENSION - 1) {
row(0,y+1);
} else print();
boolArray[i] = false;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
init();
print();
diagonal1(0);
}
}
My suspect is the row() method (see my comments below):
public static void row(int x, int y) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[x][y] == 0) {
boolArray[i] = true; // <-- this one
array[x][y] = i;
}
if (x < DIMENSION - 1) {
row(x + 1, y);
} else if(y < DIMENSION - 1) {
row(0,y+1);
} else print();
boolArray[i] = false; // <-- would be OVERWRITTEN by this one
}
}
}
I dont think it's infinite, but very long:
9-step loop in diag1,
3-deep recrusion to diag1,
then 9-step loop in diag2
3-deep recursion in diag2,
then 9-step loop in row
~6-deep recursion in row.
Even though not all loops perform complicated operations on each iteration, this can easily add up to hours if you also take into consideration that you print the state of the square at every "resolution" of the square -- printing to console takes time.