What is wrong with my recursive back-tracker - java

I wanted to make an algorithm to create a maze using recursive back-tracker algorithm. I get this problem when running the program:
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Vector.firstElement(Vector.java:481)
at DC_files.Maze.RBT(MazeAlgorithm.java:131)
at DC_files.MazeAlgorithm.main(MazeAlgorithm.java:222)
and honestly, at this point after finding couple of bugs and error i have no idea what might be wrong. The only clue that i have, is that in the switch there is an error, that the stack.firstElement() doesn't update and is always the same and in the cases something is being inserted wrongly.
while(vis < toVis){
Vector<Integer> neighbours = new Vector<>();
int x = stack.firstElement().getKey(); (line 131 with the error.)
int y = stack.firstElement().getValue();
//North neighbour
if(y-1 >= 0 && !matrix[y - 1][x].vis){
neighbours.add(0);
}
entire code
package DC_files;
import javafx.util.Pair;
import java.util.Random;
import java.io.*;
import java.util.*;
class Cell{
/*
each Cell holds information of:
-the state of it's walls,
-the value that the Cell holds(will be used later to customize rooms in the maze),
-and information if the Cell was visited(will be used in the algorithm)
*/
boolean[] walls;
boolean vis;
int value;
public boolean isVis() {
return vis;
}
public boolean[] getWalls() {
return walls;
}
public int getValue() {
return value;
}
/*
by default, each Cell has the value of each wall set to true(they exist/there are no corridors),
vis as visited set to false, because we weren't in the room yet,
value set to zero, for further development phase (0 - not visited, 1 - visited, 2 - ??? (...))
*/
Cell(){
walls = new boolean[]{true, true, true, true}; // {N, S, E, W}
vis = false;
value = 0;
}
}
class Maze{
/*
Maze class was created in order to generate an array of Cells,
that will be later used in the algorithm.
First we create integer ,,size,, to store size of the array, then
we create a matrix, that will hold Cell objects.
In the constructor of the class, we set the required integer s as size,
allocate memory to the matrix, by setting its dimensions to size x size
and we create Cell objects for each spot int the matrix, without any modifications.
*/
int size;
Cell[][] matrix;
Maze(int s){
size = s;
matrix = new Cell[size][size];
for(int y = 0; y < size; y++){
for(int x = 0; x < size; x++){
matrix[y][x] = new Cell();
//System.out.print("1");
}
}
}
void showMaze(){
for(int y = 0; y < size; y++){
for(int x = 0; x < size; x++){
System.out.print(matrix[y][x].getValue() + " ");
}
System.out.println();
}
}
/*
Class ,,MazeAlgorithm'' is responsible for creating connections between cells in matrix.
It uses RBT, which stands for Recursive Back-Tracker, to achieve that goal.
In order to use RBT, we need to give it starting point. After we do that,
it will check, if there is any neighbour, that fulfills the required conditions,
and then goes to that neighbour setting visited to true and value to 1.
After that, it repeats the process, and constantly adds the cells coordinates
on top of the stack, to keep track of the road it went through. If it gets stuck,
it will go back to the previous cell, by removing the top coordinates of the stack,
and going to NOW top coordinates (previous move), checking if that cell has any
neighbours which can fulfill the conditions. If it cannot find any neighbour,
it means the maze was created.
*/
void RBT(int startX, int startY){
//Setting cells to visit to size x size, and the value of visited cells to 0
int toVis = size*size;
int vis = 0;
//Declaring a stack, that will hold the information about our road
Stack<Pair<Integer, Integer>> stack = new Stack<>();
//Setting starting position: pushing it onto the stack,
//setting it's values as visited and 1,
//incrementing visited rooms.
stack.push(new Pair<>(startX, startY));
matrix[startY][startX].vis = true;
matrix[startY][startX].value = 1;
vis += 1;
//we will check, if our current node has any neighbours we can go to,
//saving those neighbours inside a vector
while(vis < toVis){
Vector<Integer> neighbours = new Vector<>();
int x = stack.firstElement().getKey();
int y = stack.firstElement().getValue();
//North neighbour
if(y-1 >= 0 && !matrix[y - 1][x].vis){
neighbours.add(0);
}
//South neighbour
if(y+1 < size && !matrix[y + 1][x].vis){
neighbours.add(1);
}
//East neighbour
if(x+1 < size && !matrix[y][x + 1].vis){
neighbours.add(2);
}
//West neighbour
if(x-1 >= 0 && !matrix[y][x - 1].vis){
neighbours.add(3);
}
//checking, if there are any neighbours we can visit
//if yes, we do our job
//if not, we pop our stack and repeat the process
if(!neighbours.isEmpty()){
Random rand = new Random();
int randDir = neighbours.get(rand.nextInt(neighbours.size()));
switch (randDir) {
//North
case 0 -> {
matrix[y][x].walls[0] = false;
stack.push(new Pair<>(x, y - 1));
matrix[y - 1][x].value = 1;
matrix[y - 1][x].vis = true;
matrix[y - 1][x].walls[1] = false;
}
//South
case 1 -> {
matrix[y][x].walls[1] = false;
stack.push(new Pair<>(x, y + 1));
matrix[y + 1][x].value = 1;
matrix[y + 1][x].vis = true;
matrix[y + 1][x].walls[0] = false;
}
//East
case 2 -> {
matrix[y][x].walls[2] = false;
stack.push(new Pair<>(x + 1, y));
matrix[y][x + 1].value = 1;
matrix[y][x + 1].vis = true;
matrix[y][x + 1].walls[3] = false;
}
//West
case 3 -> {
matrix[y][x].walls[3] = false;
stack.push(new Pair<>(x - 1, y));
matrix[y][x - 1].value = 1;
matrix[y][x - 1].vis = true;
matrix[y][x - 1].walls[2] = false;
}
}
vis += 1;
}else{
stack.pop();
}
}
}
}
public class MazeAlgorithm {
public static void main(String[] args){
Maze m = new Maze(3);
m.RBT(1, 1);
m.showMaze();
}
so i just run the code again, and the error line changed to line 131, i am also watching some tutorial about the Intellij debugger rn.

EDIT: Problem resolved. Had to change lines:
int x = stack.firstElement().getKey();
int y = stack.firstElement().getValue();
into:
int x = stack.peek().getKey();
int y = stack.peek().getValue();
Turns out method .firstElement() doesn't give you first element (the one on top of the stack) but the one that was first inserted (always the same object) so in the end all the operation were performed on one, single element and not multiple ones.

Related

DFS Algorithm - 8-Puzzle or nXn-Game

I want to solve an DFS algorithm. It is about the game 8-puzzles or N x N puzzle. At the beginning i have two arrays like (the Zero represents an empty field):
int[][] start = {{0,1,2}, {4,5,3}, {7,8,6}};
int[][] target = {{1,2,3}, {1,5,6}, {7,8,0}};
This arrays goes into my generic DFS class, which works fine. I used it of other tasks correctly. But for the completeness here is the basic part of my DFS class:
private static boolean search(State node, State target) {
if (node.equals(target))
return true;
for (State neighbour : node.getNeighbours()) {
if (!visited.contains(neighbour)) {
predMap.put(neighbour,node);
visited.add(neighbour);
if (search(neighbour, target)){
return true;
}
}
}
return false;
}
So at first my start array will pass as the first parameter and my target array as the second.
In my Stateclass i want to implement the getNeighbours()method which should return all possibles states. In the first Round something like:
First:
|0|1|2|
|4|5|3|
|7|8|6|
Second (rotated zero):
|1|0|2|
|4|5|3|
|7|8|6|
etc...
And here is my problem. How can u do that? It works for the first 4 operations but then i get an exception (The zero or the empty field is not on the position as excepted or there are two zeros). What is wrong there?
#Override
public List<State> getNeighbours() {
List<State> neighbours = new LinkedList<>();
// possibles moves...
final int startX = (freeX - 1 < 0) ? freeX : freeX - 1;
final int startY = (freeY - 1 < 0) ? freeY : freeY - 1;
final int endX = (freeX + 1 > N - 1) ? freeX : freeX + 1;
final int endY = (freeY + 1 > N - 1) ? freeY : freeY + 1;
for (int row = startX; row <= endX; row++) {
for (int column = startY; column <= endY; column++) {
int tmp = board[row][column];
board[row][column] = board[freeX][freeY];
board[freeX][freeY] = tmp;
// Just show the table...
System.out.println("=== BEFORE ===");
for (int[] x : board) {
System.out.println(Arrays.toString(x));
}
neighbours.add(new State(board, freeX + row, freeY + column));
board[freeX][freeY] = board[row][column];
board[row][column] = tmp;
// Just show the table...
System.out.println("=== AFTER ===");
for (int[] x : board) {
System.out.println(Arrays.toString(x));
}
}
}
return neighbours;
}
complete code https://gist.github.com/T0bbes/66d36326aa8878d5961880ce370ba82d
I checked your code, the reason of get that exception is, the board array is shared by every state. You should make a deep copy of that array, and you can try this code:
public Board(int[][] board, int x, int y){
if (board[x][y]!=0)
throw new IllegalArgumentException("Field (" +x+","+y+") must be free (0).");
this.board = new int[board.length][board[0].length];
for (int i = 0; i < this.board.length; i++)
for (int j = 0; j < this.board[i].length; j++)
this.board[i][j] = board[i][j];
this.freeX = x;
this.freeY = y;
this.N = board.length;
}
But there are still some problems in your code:
DFS may recursion a lot and get a StackOverflow -- you should increase stack size(-Xss100m works for me). After increase stack size, your code can output a solution, but it takes 197144 steps...
Indeed, as you see, DFS output only a valid solution(if your code is correct), not optimal solution. You should try BFS.

Saving a Integer[] as an array

I am trying to figure out how to save the return value of the method which is Integer[]{longest, count, possible} as a local variable and use the local variable as the return.
One problem I am running into, when I compile my code I get an error that a int cannot be converted into a boolean, and it is referring to "possible" in the return statement.
private Integer[] longestLength(int col, int row, boolean color)
{
// longest equals length of the longest pattern of the same color
// count equals number of the longest pattern of the same color
// possible equals number of spots, that you can play off of.
int longest = 0;
int count = 0;
int possible = 0;
//this for loop counts to 4, the patterns of the 4 possible wins
for (int i = 1; i <= 4; i++) {
//lengthOfColor saves the lengthOfColor() method to avoid calling it multiple times throughout longestLength.
Integer[] lengthOfColor = lengthOfColor(col, row, i, color);
int length = lengthOfColor[0];
//if a new longest is found, its new value is now set to itself
if (length > longest) {
longest = length;
count = 0;
possible = lengthOfColor[1];
}
//if length is the same as longest, we increase the count, and make possible equal too the larger one
if (longest != 0 && length == longest) {
count++;
possible = Math.max(lengthOfColor[1], possible);
}
}
return new Integer[]{longest, count, possible};
}
Here is my lengthOfColor method
private Integer[] lengthOfColor(int col, int row, int pattern, boolean color) {
int x = 0;
int y = 0;
if (pattern == 1) {
// vertical pattern
y = 1;
} else if (pattern == 2) {
// horizontal pattern
x = 1;
} else if (pattern == 3) {
// diagonal slope left pattern
x = 1;
y = 1;
} else {
// diagonal slope right pattern
x = 1;
y = -1;
}
// length = how many neighbor slots are of same color
// possible equals number of slots, that you can play off of.
// whichSide = left or right if horizontal and top or bottom if vertical.
int length = 0;
int possible = 0;
Integer[] whichSide = new Integer[]{1, -1};
for (int side : whichSide) {
int i = 1;
boolean complete = false;
//while complete is false continue the loop
while (!complete) {
//mainX == horizontal pattern distance
//mainY == vertical pattern distance
int mainX = x * i * side;
int mainY = y * i * side;
//if still inbounds and if the same slot is filled and it matches the color, increment length
if (!outOfBounds(col, row, mainX, mainY) && getIsFilled(col, row, mainX, mainY) &&
checkColor(col, row, mainX, mainY) == color)
{
length++;
}
//if still inbounds and if the same slot is empty, increment possible number of spots and change complete to true
else if (!outOfBounds(col, row, mainX, mainY) && !getIsFilled(col, row, mainX, mainY) &&
getLowestEmptyIndex(myGame.getColumn(col + mainX)) == getLowestEmptyIndex(myGame.getColumn(col)) + mainY - row)
{
possible++;
complete = true;
}
//finish the statement to avoid a infinite loop if neither conditions are met.
else
{
complete = true;
}
// If not complete, then check one slot further.
i = i + 1;
}
}
return new Integer[] {length, possible};
}
Figured it out, I was thinking to hard. I simply need to save the integer before returning it, so it is easier to access later on and has a name.
Integer[] longestLengthArray = new Integer[]{longest, count, possible};
return longestLengthArray;
Simplest solution:
return new Integer[]{longest, count, possible? 1 : 0};
But in general it is good idea create new helper class:
class LongestLengthRersponse{
private int longest;
private int count;
private boolean possible;
//constructor, and getters

write 0's and 1's on each line where the last 2 weren't the same

There's an error in the logic of what I've build at the moment.
What should be happening is that my code should display a grid of 0's and 1's.
Like so:
001001
101101
010110
110010
001101
So what has to happen here is that:
For each row there can't be more than 2 numbers of the same type consecutively
the numbers are picked randomly
for each column there can't be more than 2 numbers of the same type consecutively
there can be a maximum of 3 of each type of number going by column or row
edit: to further clarify
ok so I have a row like this:
0 1 0 1 1 0
- As you can see there will always be 3 x 1, and 3 x 0
- the order of numbers is picked randomly (so it might go 0 1, or 1 1, or 0 0 to start etc)
- there can never be more than 2 numbers of the same type consecutively, for instance if it's 001100, you can see that there were 2 0's, then it had to display a 1, but then there were 2 1's, so it had to display an 0. So 011100 couldn't happen (3 1's consecutively) or 000101 (3 0's consecutively)
Based upon this, but for now not essential, the same no 2 numbers consecutively must apply in columns (so in my successful example it goes 001001 across, there are at most 2 0's consecutively. But looking down you get 010101 (that is to say, once again, no more than 2 consecutively)
So my code is as follows:
import java.util.Random;
public class Main {
public static void main(String[] args) {
int l = 6;
int w = 6;
Random rd = new Random();
// Create a grid that is 6 x 6
int[][] grid = new int[l][w];
// for each row
for (int i = 0; i < l; i++) {
int zCount = 0;
int oCount = 0;
int current;
int lastA = 2;
int lastB = 2;
// for each item in the row
for (int j = 0; j < w; j++) {
// set the current item to either 0 or 1
current = rd.nextInt(2);
// make sure there aren't already (e.g. 3 items out of 6)
// items in the row
if (j % 2 == 1) {
// hold every second element
lastA = current;
} else {
// hold every first element
lastB = current;
}
if (current == 1) {
if (oCount != 3) {
if (lastA != lastB) {
// if the two previous items aren't the same
grid[i][j] = current;
// add to the counter
oCount++;
}
}
}
if (current == 0) {
if (zCount != 3) {
if (lastA != lastB) {
// if the two previous items aren't the same
grid[i][j] = current;
// add to the counter
zCount++;
}
}
}
System.out.print(grid[i][j]);
}
System.out.println(" ");
}
}
}
The problem is it generates as follows:
010010
100001
100010
000010
100001
001000
So obviously it doesn't conform to the first, third or fourth points.
I have absolutely no idea why! Except for the columns (third point) which I haven't initialised.
Can anybody work out what the logical failure is in my code?
Thanks for your help!
Here is my procedural solution which tries to keep the amount of required code as small as possible. It is capable of computing 2D-Arrays with arbitrary rows and columns like [6, 6] or [4, 7] or [3, 8] for example. The complexity of the algorithm is O(n) with n = rows * columns.
The program computes an arbitrary 2D-Array (grid) populated with either a 0 or 1. The grid guarantees the following characteristics, formulated mathematically:
∀ r,c ∈ Integer | 0 ≤ r < grid.rows, 0 ≤ c < grid.columns :
r - 2 ≥ 0 ⇒ cardinality( distinct( grid[r][c], grid[r-1][c], grid[r-2][c] )) = 2
r + 2 < grid.rows ⇒ cardinality( distinct( grid[r][c], grid[r+1][c], grid[r+2][c] )) = 2
c - 2 ≥ 0 ⇒ cardinality( distinct( grid[r][c], grid[r][c-1], grid[r][c-2] )) = 2
c + 2 < grid.columns ⇒ cardinality( distinct( grid[r][c], grid[r][c+1], grid[r][c+2] )) = 2
or in other words:
the grid does neither contain a row nor a column which has three or more consecutive 0's or 1's.
Below the Java code I will explain how the algorithm works and why it is designed as it is:
public static void main(String[] args) {
int[][] grid = anyGrid(8, 13);
}
private static int[][] anyGrid(int rows, int cols) {
int[][] grid = new int[rows][cols];
int row = 0;
for (int col = 0; col - row < cols; col++) {
for (int r = row; r >= 0 && col - r < cols;) {
setBit(grid, r, col - r--);
}
if (row < rows - 1) row++;
}
return grid;
}
private static void setBit(int[][] grid, int row, int col) {
int vInd = calcVerticalIndicator(grid, row, col);
int hInd = calcHorizontalIndicator(grid, row, col);
if (isPartiallyRestricted(vInd, hInd)) {
grid[row][col] = flip(vInd);
} else if (isFullyRestricted(vInd, hInd)) {
grid[row][col] = vInd;
grid[row - 1][col] = flip(vInd);
} else {
grid[row][col] = Math.abs(vInd) <= 1
? flip(vInd)
: Math.abs(hInd) <= 1 ? flip(hInd) : anyBit();
}
}
private static boolean isPartiallyRestricted(int vInd, int hInd) {
return vInd == hInd;
}
private static boolean isFullyRestricted(int vInd, int hInd) {
return vInd + hInd == 1;
}
private static int calcVerticalIndicator(int[][] grid, int row, int col) {
return calcIndicator(grid, row - 1, col, row - 2, col, 2);
}
private static int calcHorizontalIndicator(int[][] grid, int row, int col) {
return calcIndicator(grid, row, col - 1, row, col - 2, 4);
}
private static int calcIndicator(int[][] grid, int row1, int col1, int row2, int col2, int unrestricted) {
try {
return grid[row1][col1] * grid[row2][col2] + (grid[row1][col1] - grid[row2][col2]) * unrestricted;
} catch (IndexOutOfBoundsException e) {
return unrestricted;
}
}
private static int anyBit() {
return (int) (Math.random() * 2);
}
private static int flip(int bit) {
return bit == 0 ? 1 : 0;
}
The challenge we face is not to ensure that there are no three consecutive 0's or 1's in a row only or in a column only. The challenge is to ensure that no three consecutive 0's or 1's are neither in a row nor in a column by providing an efficient algorithm.
The tricky situation we may run into looks like this:
Let's consider the situation where all the cells at the top and to the left of the cell outlined in blue are already populated and do not violate the rules define above.
picture a) we want to populate the cell having a blue outline. The two cells at it's top are populated with two 0's while the cells at it's left are populated with two 1's. Which value should we choose? Due to symmetry it doesn't matter if we choose a 0 or a 1. Hence, let's go with a 0.
picture b) populating the cell outlined in blue with a 0 violates one rule defined above: the grid does not contain a column with three or more consecutive 0's or 1's. Hence we have to change the value of one of the two cells above of the blue cell.
picture c) say we change the value of the cell which is immediately above the blue cell, from 0 to 1. This could result in the violation of some rules, caused by the already populated cells to the left of the modified cell.
picture d) but a violation would mean that both cells to the left must have a value of 1.
picture e) this would imply that both cells to their top must have a value of 0 which is a contradiction to a situation we assumed. Therefore, changing the cell immediately at the top of the cell outlined in blue will not cause any violation of the rules.
To address the precondition, that no cells to the right of the modified cell are already populated, the algorithm populates the grid in a diagonal way. The population of cells occur in the order as shown below:
The final thing I like to explain is how the algorithm decides which values are available to choose from for each cell. For each cell it inspects the two top-most and two left-most cells and calculates an indication value. This value is used to determine the possible values for a cell by using arithmetic calculation as follows:
if the two cells inspected are both populated with 0's return an indicator value of 0.
if the two cells inspected are both populated with 1's return an indicator value of 1.
I have selected those two values because they communicate the fact, that this values are not permitted, in an intuitive way.
Then I selected a function to communicate if both, the column cells and the row cells, restrict the cell to populate by the same value. This is the case if both indicator values are equal. Keep this characteristic in mind, because we have to find values for the situation when no restriction applies from the column cells or the row cells.
If both indicators restrict the value to populate the cell with by a different value, the sum of them is 1. This is the second characteristic we have to keep in mind when searching for proper indicator values when no restriction applies.
The last thing the algorithm has to achieve is to find proper values when no restriction applies without compromising the unique indicators defined above.
Preserving the indication when the cell is restricted by the same value can be achieved by selecting values for the row and column indicators which are different from 0 and 1 and different from each other.
Preserving the indication when the cell is restricted by both values can be achieved by selecting values being greater than 1 and having a delta to each other of at least 2.
The algorithm does indicate no restriction for a row by the values 2 and -2 and for a column by the values 4 and -4. This values do not conflict with the operations used to identify the other two cases.
I hope this documentation helps to understand the whole program and how it does solve the problem statement. I am glad to hear your comments.
Many of the solutions given are extremely long and complicated. Here's a solution with very minimal code (Ideone Example here):
int row, col, n = 8;
int[][] grid = new int[n][n], cCount = new int[n][2], rCount = new int[n][2];
Deque<Entry<Integer,Integer>> freeInd = new ArrayDeque<Entry<Integer,Integer>>();
Random rand=new Random();
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
// Calcualte constraints: row, col = {-1, 0, 1}, -1 => no constraint.
row = j > 1 && grid[i][j-2] == grid[i][j-1] ? (grid[i][j-1] == 0 ? 1:0):
(rCount[i][0] >= n/2 ? 1: // too many 0's
(rCount[i][1] >= n/2 ? 0:-1)); // too many 1's
col = i > 1 && grid[i-2][j] == grid[i-1][j] ? (grid[i-1][j] == 0 ? 1:0):
(cCount[j][0] >= n/2 ? 1: // too many 0's
(cCount[j][1] >= n/2 ? 0:-1)); // too many 1's
grid[i][j] = row == -1 && col == -1 ? rand.nextInt(2):(row > -1 ? row:col);
// Handle Constraints
if( row == -1 && col == -1){ // no constraint
freeInd.push(new SimpleEntry<Integer,Integer>(i, j)); // add to free indices
} else if( (row > -1 && col > -1 && row != col) // constraint conflict
|| (row > -1 && rCount[i][row] >= n/2) // count conflict
|| (col > -1 && cCount[j][col] >= n/2)){ // count conflict
Entry<Integer, Integer> last = freeInd.pop(); // grab last free index
while(i > last.getKey() || j > last.getValue()){
j = (j-1+ n)%n; // step indices back
i = (j == n-1) ? i-1:i;
rCount[i][grid[i][j]]--; // reduce counters
cCount[j][grid[i][j]]--;
}
grid[i][j] = grid[i][j] == 0 ? 1:0; // flip value
}
rCount[i][grid[i][j]]++; // increment counters
cCount[j][grid[i][j]]++;
}
}
The idea here is that you walk along each row of the matrix adding 0's and 1's abiding by the following rules:
If the current index is unconstrained (i.e. it can be 0 or 1) we choose a value randomly.
If the current index is constrained we force it to have the constrained value.
If there are multiple constraints that do not agree, we revert back to the last unconstrained index (freeInd) by first incrementally stepping backwards along the rows of the matrix, decrementing the count for the given value (0 or 1). E.g. this is done for rows with rCount[i][grid[i][j]]--. When the unconstrained vertex is finally reached, flip it's value.
Finally, increment the count of the value (0 or 1) for the current row and column. E.g. this is done for rows with rCount[i][grid[i][j]]++
The 1st problem which i found in your solution is it's initializing the value of counter value (ocount and zcount) as zero and the only way grid(array) is assigned a value is when if it's greater than three, and the way i see if i am not mistaken the value of counter is incremented in the loop in which they are checked to be greater than 3, and that condition can never be reached .
To solve this problem use the algo of backtracking by assigning the new value to a different value if the calue
A working code in jsFiddle (for 6x6 grids):
$(function(){
function print(str){
$("body").append(str + "<br/>");
}
function toBin(num, length){
if(!length){
length = 3;
}
var str = num.toString(2);
while(str.length < length){
str = 0 + str;
}
return str;
}
var wrongAnds = [
parseInt('000000111', 2),
parseInt('000111000', 2),
parseInt('111000000', 2),
parseInt('100100100', 2),
parseInt('010010010', 2),
parseInt('001001001', 2),
];
var wrongOrs = [
parseInt('111111000', 2),
parseInt('111000111', 2),
parseInt('000111111', 2),
parseInt('011011011', 2),
parseInt('101101101', 2),
parseInt('110110110', 2),
];
function test(mask){
for (var i = 0; i < 6; i++) {
if((wrongAnds[i] & mask) == wrongAnds[i]){
return false;
}
if((wrongOrs[i] | mask) == wrongOrs[i]){
return false;
}
}
return true;
}
var threeGrid = [];
var toRight = [];
var toBottom = [];
for(var mask = 1<<9-1; mask >= 0; mask--){
if(test(mask)){
threeGrid.push(mask);
}
}
function numberOfSetBits(i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
function getCol(grid, col){
var ret = 0;
for(var i=0; i<3; i++){
ret += (grid & (1 << (i*3+col))) >> (i*2+col);
}
return ret;
}
var wrongAnds6 = [
parseInt('011100', 2),
parseInt('001110', 2)
];
var wrongOrs6 = [
parseInt('100011', 2),
parseInt('110001', 2)
];
for(var i = 0; i < threeGrid.length; i++){
for(var j = 0; j < threeGrid.length; j++){
var grid1 = threeGrid[i];
var grid2 = threeGrid[j];
var toRightOk = true;
var toBottomOk = true;
var printit = (i==0);
for(var k=0;k<3;k++){
var row = ((grid1 & wrongAnds[k]) << 3 >> (k*3)) + ((grid2 & wrongAnds[k]) >> (k*3));
var col = ((getCol(grid1, k)) << 3) + ((getCol(grid2, k)));
if(numberOfSetBits(row) != 3
|| ((wrongAnds6[0] & row) == wrongAnds6[0])
|| ((wrongAnds6[1] & row) == wrongAnds6[1])
|| ((wrongOrs6[0] | row) == wrongOrs6[0])
|| ((wrongOrs6[1] | row) == wrongOrs6[1])
) {
toRightOk = false;
}
if(numberOfSetBits(col) != 3
|| ((wrongAnds6[0] & col) == wrongAnds6[0])
|| ((wrongAnds6[1] & col) == wrongAnds6[1])
|| ((wrongOrs6[0] | col) == wrongOrs6[0])
|| ((wrongOrs6[1] | col) == wrongOrs6[1])
) {
toBottomOk = false;
}
}
if(toRightOk){
if(!toRight[grid1]){
toRight[grid1] = [];
}
toRight[grid1].push(grid2);
}
if(toBottomOk){
if(!toBottom[grid1]){
toBottom[grid1] = [];
}
toBottom[grid1].push(grid2);
}
}
}
function intersect(arr1, arr2){
var results = [];
for (var i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) !== -1) {
results.push(arr1[i]);
}
}
return results;
}
var found = false;
while(!found){
var grid1 = threeGrid[0];
var grid1 = threeGrid[Math.floor(Math.random()*threeGrid.length)];
var grid2 = toRight[grid1][Math.floor(Math.random()*toRight[grid1].length)];
var grid3 = toBottom[grid1][Math.floor(Math.random()*toBottom[grid1].length)];
var arr4 = intersect(toBottom[grid2], toRight[grid3]);
if(arr4.length > 0){
var grid4 = arr4[Math.floor(Math.random()*arr4.length)];
found = true;
}
}
function gridToStrings(grid){
var rowS = [];
for(var i=0; i<3; i++){
rowS.push(toBin(((grid & wrongAnds[i]) >> (i*3))));
}
return rowS;
}
var grid1S = gridToStrings(grid1);
var grid2S = gridToStrings(grid2);
var grid3S = gridToStrings(grid3);
var grid4S = gridToStrings(grid4);
print(grid1S[0] + grid2S[0]);
print(grid1S[1] + grid2S[1]);
print(grid1S[2] + grid2S[2]);
print(grid3S[0] + grid4S[0]);
print(grid3S[1] + grid4S[1]);
print(grid3S[2] + grid4S[2]);
});
Theory:
Find all possible 3x3 grids
Find all possible left-to-right and top-to-bottom pairings
get 4 random grids to form the 6x6 grid
Implementation:
Represent 3x3 grids as 9bit integers. A 3x3 grid is wrong if there are 3 1s or 3 0s in it. This can be easily filtered with a couple bitwise operations.
Test the Cartesian product of these 3x3 grids (Compare every grid with every grid). Check if there are exactly 3 0s and 3 1s in all rows and columns (put the second grid right to the first grid to check 3 rows, and put it below the first grid to check 3 columns), and that there are no consecutive 3 0s or 1s.
get the top-left, top-right and bottom-right grids. Check if there is an available 4th grid that can go below the top-right grid and right to the bottom-left grid. If there is none, restart step 4, otherwise pick one.
A couple outputs:
011010
100101
001011
110100
101100
010011
110010
101100
010011
001101
100110
011001
001101
110010
010011
101100
110100
001011
Edit:
there is only 1120 solutions to this problem (jsFiddle). There are 2^36 ways to fill a 6x6 grid with 0s and 1s. If you used brute force (get a random 6x6 grid, then check if its right), that would mean an average ~61356676 (6.1*10^7) executions to find a correct solution. Even thought your method is somewhat faster (it can fail sooner if its not the last digit thats wrong), it might still be slow.
I think there are two problems with your code:
If oCount or zCount have become 3 there are no more assignments grid[i][j]=current if the random value is not acceptable. You get zeroes at these positions (to which the grid was initialized).
Near the right bottom there might not be any more valid solutions. You would have to undo previous assignments, i.e. you would need to do some kind of backtracking.
I would recommend starting with a valid solution and transforming this solution step by step according to random values for grid positions - but only if this is possible without breaking validity. If have prepared an example implementation:
public static void main(String[] args) {
int l = 6, w = 6;
Grid g = new Grid(l, w);
Random rd = new Random();
// initialize with checkerboard pattern (which is a valid solution)
for (int y = 0; y < l; y++) for (int x = 0; x < w; x++) g.arr[y][x] = (x ^ y) & 1;
// construct a valid grid by transformation of grids while preserving validity
for (int y = 0; y < l; y++) for (int x = 0; x < w; x++) {
int v = rd.nextInt(2), v2 = v ^ 1;
if (g.arr[y][x] == v) continue;
// try to modify current grid by exchanging values: 01/10=>10/01 or 10/01=>01/10
// (keep parts of the grid which have already been adapted to random values)
rotating: for (int y2 = y + 1; y2 < l; y2++) for (int x2 = x; x2 < w; x2++) {
if (g.arr[y2][x] == v && g.arr[y][x2] == v && g.arr[y2][x2] == v2) {
g.rotate(x, y, x2, y2);
// keep result if grid is still valid, undo otherwise
if (g.rotatedOk(x, y, x2, y2)) break rotating;
g.rotate(x, y, x2, y2);
}
}
}
g.printOn(System.out);
}
public static class Grid {
int l, w;
int[][] arr;
Grid(int l, int w) {
this.arr = new int[this.l = l][this.w = w];
}
void rotate(int x, int y, int x2, int y2) {
int v;
v = arr[y][x]; arr[y][x] = arr[y2][x]; arr[y2][x] = v;
v = arr[y][x2]; arr[y][x2] = arr[y2][x2]; arr[y2][x2] = v;
}
boolean rotatedOk(int x, int y, int x2, int y2) { // check after rotation
return okAt(x, y) && okAt(x2, y) && okAt(x, y2) && okAt(x2, y2);
}
private boolean okAt(int x, int y) { // check single position in grid
int v = arr[y][x];
if (count(x, y, -1, 0, v) + count(x, y, 1, 0, v) > 1) return false;
if (count(x, y, 0, -1, v) + count(x, y, 0, 1, v) > 1) return false;
return true;
}
private int count(int x, int y, int dx, int dy, int v) {
for (int n = 0; ; n++) {
x += dx; y += dy;
if (x < 0 || x >= w || y < 0 || y >= l || arr[y][x] != v) return n;
}
}
void printOn(PrintStream s) {
for (int y = 0; y < l; y++) { for (int x = 0; x < w; x++) s.print(arr[y][x]); s.println(); }
}
}
The problem with your approach is that you need a mechanism that handles when a new value can't be used because it follows two similar values, but the other value can't be used because it is under two other values. For example, say your grid has got this far:
101010
011010
00?
You would then need to slowly roll back positions and try different values.
The following code solves that problem using recursion:
import java.util.Random;
public class Main {
final int height = 6;
final int width = 6;
int[][] grid;
Random rd = new Random();
public static void main(final String[] args) {
Main main = new Main();
main.process();
}
private void process() {
// Create a grid that is 6 x 6
grid = new int[height][width];
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
grid[x][y] = -1;
}
}
recurseFillMatrix(0, 0);
}
private boolean recurseFillMatrix(final int x, final int y) {
// first, try putting a random number in the cell
int attempt = 1;
grid[x][y] = Math.abs(rd.nextInt()%2);
do {
if(isGridValid()) {
if(x == (width - 1) && y == (height - 1)) {
printGrid();
return true;
}
boolean problemSolved;
if(x == (width - 1)) {
problemSolved = recurseFillMatrix(0, y + 1);
} else {
problemSolved = recurseFillMatrix(x + 1, y);
}
if(problemSolved) {
return true;
}
}
attempt++;
grid[x][y] = 1 - grid[x][y];
} while(attempt <= 2);
grid[x][y] = -1;
return false;
}
private boolean isGridValid() {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
// if the current item is -1, then we are finished
if(grid[x][y] == -1) {
return true;
}
// if we are after the second column
if(x > 1) {
if(grid[x-2][y] == grid[x-1][y] && grid[x-1][y] == grid[x][y]) {
return false;
}
}
// if we are after the second row
if(y > 1) {
if(grid[x][y-2] == grid[x][y-1] && grid[x][y-1] == grid[x][y]) {
return false;
}
}
// total the values in this column
int total = 0;
for(int i = 0; i <= y; i++) {
total += grid[x][i];
}
if(y == (height - 1)) {
if(total != 3) {
return false;
}
} else {
if(total > 3) {
return false;
}
}
// total the values in this row
total = 0;
for(int i = 0; i <= x; i++) {
total += grid[i][y];
}
if(x == (width - 1)) {
if(total != 3) {
return false;
}
} else {
if(total > 3) {
return false;
}
}
}
}
return true;
}
private void printGrid() {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
System.out.print(grid[x][y]);
}
System.out.println("");
}
}
}
The isGridValid() method uses your defined rules to check if the grid (as it is filled so far) complies with the rules. At the first sign that it does not, it returns false.
If I have to change your solution to achieve the result, here is what it should look like..
Take the incrementors for oCount and zCount in a separate if-else
Take the assignment to grid(i,j) outside the loop
Your if-else block is not taking into account every condition possible, like
What about when last 2 items are same
What about when the zCount or oCount has reached 3
Taking into account these consideration, this code works fine.
import java.util.Random;
public class Main {
public static void main(String[] args) {
int l = 6;
int w = 6;
Random rd = new Random();
// Create a grid that is 6 x 6
int[][] grid = new int[l][w];
// for each row
for (int i = 0; i < l; i++) {
int zCount = 0;
int oCount = 0;
int current;
int lastA = 2;
int lastB = 2;
// for each item in the row
for (int j = 0; j < w; j++) {
// set the current item to either 0 or 1
current = rd.nextInt(2);
// make sure there aren't already (e.g. 3 items out of 6)
// items in the row
if (current == 1) {
if (oCount != 3) {
if (lastA == lastB) {
current = lastA == 1 ? 0 : 1;
}
} else {
current = current == 1 ? 0 : 1;
}
} else if (current == 0) {
if (zCount != 3) {
if (lastA == lastB) {
current = lastA == 1 ? 0 : 1;
}
} else {
current = current == 1 ? 0 : 1;
}
}
grid[i][j] = current;
if (current == 1) {
oCount++;
} else {
zCount++;
}
if (j % 2 == 1) {
// hold every second element
lastA = current;
} else {
// hold every first element
lastB = current;
}
System.out.print(grid[i][j]);
}
System.out.println(" ");
}
}
}
Again, This solution takes care of row conditions only. You would need to do similar checks for columns as well, to achieve the full result
HTH
here I tested you problem and seems that it is what you need.
I used a functional approach using Guava, it is quite simple, readable and has a short code.
#Test
public void test_permutations()
{
List<Integer> binary = Lists.newArrayList(1,0,1,0,1,0); // Domain list
Set<String> flattenSet = Sets.newHashSet(); // Store non-repetitive values
// Create list of possible values
Collection<List<Integer>> permutations = Collections2.permutations(binary);
for (List<Integer> permutation : permutations)
{
String joinString = StringUtils.join(permutation, "");
flattenSet.add(joinString);
}
// Create predicate to filter positive values
Predicate<String> predicate = new Predicate<String>() {
public boolean apply(String input) {
// Discard wrong values
if (input.contains("000") || input.contains("111")) {
return false;
} else {
return true;
}
}
};
// Use predicate to filter values
Collection<String> filteredList = Collections2.filter(flattenSet, predicate);
// Display result
for (String result : filteredList) {
System.out.println(result);
}
}
It is simple, I've commented the code to be clear but you can debug it to understand step by step.
The generated output is:
010011
110010
010101
010110
100110
101001
011010
110100
001011
001101
011001
101010
101100
100101
Hope to help
I think that its a mistake to think of generating it one element at at time. Instead imagine that I generate the entire set of permissible rows {001100,101010,....etc} There are only 6!/(3!3!)=20 ways to arrange three ones and three and some of them will be excluded. Now I am going to generate a game tree by saying that a move is selecting a valid row for the next row. If I discover at some point that there are no more valid moves then i will back track and try a different move.
To generate a move I randomly select a row, if its a valid move, I try to select another move, if that is impossible I backtrack, effectively doing a (random) depth first search of the game tree.
public class gametree {
public static ImmutableList<Row> allValidRows = // create a list of all valid rows.
public static List<Rows> getValidMoves(Move parent){ //Backtracks up the
//tree to the root to find the current state of the board, and returns
//which ever of allValidRows are valid given the game board.
}
public class Move {
public final Move parent;
public List<Rows> validMoves;
public final Row thisMove;
public int depth=0;
Move(Move parent, Row thisMove){
this.thisMove = thisMove;
this.parent = parent;
this.validMoves = getValidMoves(parent);
Move hold=parent;
while(hold!=null){
depth++; hold = parent.parent;
}
}
}
void run {
//pick first move
Move Root = new Move(null, Collections.Shuffle(allValidRows).get(0));
Move FinalMove = search(Root);
//Something to print out the answer here
}
public Move search(Move move){
if(depth==5){ return Move} //If I get to row six I win.
else if(move.validMoves.isEmpty()) { //If there are no valid moves,
//then this move wasnt valid, to strip it from the parent's
//possible moves and try again
move.parent.validMoves.remove(move.thisMove);
search(move.parent);
} else { //pick a random valid move and create a nextMove
Move nextMove = new Move(move, Collection.Shuffle(move.getValidMoves).get(0))
search(nextMove);
}
}
The worst case for this algorithm is that there is only one victory state and it has to try every possible state, but in practice this game does not seem very restrictive so it will probably not take long at all.
This code is strictly illustrative.

Finding the girth g(G) (if there is one) and diameter diam(G) on a Graph in Adjacency Matrix [Java]

Really stumped on this one, don't know where to begin. I thought about making a separate method which calculates the paths between two different vertices, but I don't know how I would implement that.
I thought that finding the minimum degree of the graph and then adding one would give me the girth, but that is assuming there is a cycle in the graph. So I would need the program to scan through the adjacency matrix, somehow uses the true values to find out if there is a cycle or not and then calculate the distance of that cycle.
Looking for a step in the right direction.
Also I'm looking to not use the ArrayList approach, hence why I'm stumped
This is what I have so far:
import java.util.Scanner;
import java.util.*;
public class Graph {
// Setup privately modified variables which will define the graph
// These two parameters are storage variables for edges and vertices
//These variables were changed from Vertex and Edge to numVertices and numEdges.
private int numVertices;
private int numEdges;
// This will be the adjacency matrix to represent our graph, this will
// represent edges.
// adj_Matrix_Edges was previously static meaning it did not have access to multiple graphs, onyl one graph.
private boolean[][] adj_Matrix_Edges;
// first step will be to setup the graph, using this constructor
public Graph(int vertices) {
numVertices = vertices;
if (numVertices < 0) {
throw new RuntimeException(
"Number of vertices cannot be a nonnegative value");
}
System.out.println("There are now " + numVertices
+ " vertices in the graph.");
// A graph is created based on the specifications, N X N or (n^2)
// graph.
adj_Matrix_Edges = new boolean[vertices][vertices];
}
// This method validates whether or not two vertices are adjacent, returns
// true if adjacent false otherwise.
public boolean adjacent(int i, int j) {
if (adj_Matrix_Edges[i][j] == true) {
return true;
} else {
return false;
}
}
// I needed a review of this class so I had to read about ArrayList class, but it allows
// you to iterate the columns in the adjacency matrix.
// It also allows you to print out integers values instead of booleans
// The for loop, loops over a column you would select, and then the if
// statement checks for an incident in that column.
public List<Integer> neighbors(int vertex) {
List<Integer> neighbors = new ArrayList<Integer>();
for (int i = 0; i < adj_Matrix_Edges.length; i++) {
// The if statement here does not need an equality sign since
// booleans are in the 2-d matrix.
if (adj_Matrix_Edges[vertex][i]) {
// adds that vertex i to the list
neighbors.add(i);
}
}
System.out.println("The neighbors of vertex " + vertex + " are " + neighbors);
return neighbors;
}
//This method will count the number of neighbors for a specific vertex.
public double averageDegree(){
//create a variable for the count, and initialize the counter to 0.
double neighborCount = 0;
for (int i = 0; i < adj_Matrix_Edges.length; i++){
for (int j = 0; j < adj_Matrix_Edges[i].length; j++){
//this if statement scans the specific vertex for true statements in the boolean array
if (adj_Matrix_Edges[i][j]){
// this logical expression adds up the count of the true statements, in graph theory this is adding up the
// degree of that specific vertex.
neighborCount++;
}
}
}
neighborCount= neighborCount / numVertices;
System.out.println("The average degree of the graph is " + neighborCount);
return neighborCount;
}
public boolean[][] addVertex() {
// add an extra vertex to the graph.
numVertices++;
// secondly we have to copy over the contents of the old array into a new array.
// Initialize a new array
boolean[][] new_adj_Matrix_Edges = adj_Matrix_Edges;
// setup a for loop which sets up new values for
for (int i = 0; i < adj_Matrix_Edges.length; i++){
for (int j = 0; j < adj_Matrix_Edges.length; j++){
adj_Matrix_Edges[i][j] = new_adj_Matrix_Edges[i + 1][j + 1];
}
}
return new_adj_Matrix_Edges;
}
public boolean[][] removeVertex(int vertex){
// set a local variable.
int vertex_Selected = vertex;
// create a new 2-d array where you can copy the old one over.
boolean[][] new_adj_Matrix_Edges = adj_Matrix_Edges;
//create a for loop setup to copy over all data from old array to the new array.
for (int g = 0; g < adj_Matrix_Edges.length; g++){
for (int h = 0; h < adj_Matrix_Edges[g].length; h++){
adj_Matrix_Edges[g][h] = new_adj_Matrix_Edges[g][h];
}
}
// now that a new array has been created, and all information is copied over we can then set
// all values of the selected vertex to false.
for (int i = 0; i < new_adj_Matrix_Edges.length; i++){
for (int j = 0; j < new_adj_Matrix_Edges[i].length; j++){
if (new_adj_Matrix_Edges[vertex_Selected][j] == true){
new_adj_Matrix_Edges[vertex_Selected][j] = false;
}
if (new_adj_Matrix_Edges[i][vertex_Selected] == true){
new_adj_Matrix_Edges[i][vertex_Selected] = false;
}
}
}
return new_adj_Matrix_Edges;
}
public void addEdge(int vertex_add_1, int vertex_add_2) {
if (adj_Matrix_Edges[vertex_add_1][vertex_add_2] == false) {
adj_Matrix_Edges[vertex_add_1][vertex_add_2] = true;
adj_Matrix_Edges[vertex_add_2][vertex_add_1] = true;
} else {
System.out.println("There is already an edge between vertex "
+ vertex_add_1 + " and vertex " + vertex_add_2 + ".");
}
}
// This method removes an edge if the two int values in the 2-d boolean
// array are true, converts to false, otherwise it stays false if no edge
// present
public void removeEdge(int vertex_remove_1, int vertex_remove_2) {
if (adj_Matrix_Edges[vertex_remove_1][vertex_remove_2] == true) {
adj_Matrix_Edges[vertex_remove_1][vertex_remove_2] = false;
adj_Matrix_Edges[vertex_remove_1][vertex_remove_2] = false;
} else {
System.out.println("There is no edge between vertex "
+ vertex_remove_1 + " and vertex " + vertex_remove_2);
}
}
// setup a method which finds the shortest cycle in the graph.
// We want to set the method to return an int value, because the girth of the graph will represent an integer value
// which cannot be a negative value.
// if we find the diameter of a graph then the shortest cycle will be the girth, which will be the
// 2diam(G) + 1.
public int girth(){
// Set the
}
public int pathVertices(int vertex_1, int vertex_2){
if (vertex_1 != vertex_2){
}
}
public static void main(String[] args) {
// Make an arbritary graph with 5 vertices.
Graph graph = new Graph(10);
graph.addEdge(1, 2);
graph.removeEdge(0, 1);
graph.adjacent(1, 2);
graph.adjacent(2, 1);
graph.neighbors(1);
graph.neighbors(4);
graph.addVertex();
graph.removeVertex(0);
graph.averageDegree();
// for (int i = 0; i < adj_Matrix_Edges.length; i++) {
// for (int j = 0; j < adj_Matrix_Edges[i].length; j++) {
// System.out.println(adj_Matrix_Edges[i][j] + " ");
// }
// System.out.println("-----");
// }
}
}

Representing game states in Tic Tac Toe

The goal of the assignment that I'm currently working on for my Data Structures class is to create a of Quantum Tic Tac Toe with an AI that plays to win.
Currently, I'm having a bit of trouble finding the most efficient way to represent states.
Overview of current Structure:
AbstractGame
Has and manages AbstractPlayers (game.nextPlayer() returns next player by int ID)
Has and intializes AbstractBoard at the beginning of the game
Has a GameTree (Complete if called in initialization, incomplete otherwise)
AbstractBoard
Has a State, a Dimension, and a Parent Game
Is a mediator between Player and State, (Translates States from collections of rows to a Point representation
Is a StateConsumer
AbstractPlayer
Is a State Producer
Has a ConcreteEvaluationStrategy to evaluate the current board
StateTransveralPool
Precomputes possible transversals of "3-states".
Stores them in a HashMap, where the Set contains nextStates for a given "3-state"
State
Contains 3 Sets -- a Set of X-Moves, O-Moves, and the Board
Each Integer in the set is a Row. These Integer values can be used to get the next row-state from the StateTransversalPool
SO, the principle is
Each row can be represented by the binary numbers 000-111, where 0 implies an open space and 1 implies a closed space.
So, for an incomplete TTT board:
From the Set<Integer> board perspective:
X_X R1 might be: 101
OO_ R2 might be: 110
X_X R3 might be: 101, where 1 is an open space, and 0 is a closed space
From the Set<Integer> xMoves perspective:
X_X R1 might be: 101
OO_ R2 might be: 000
X_X R3 might be: 101, where 1 is an X and 0 is not
From the Set<Integer> oMoves perspective:
X_X R1 might be: 000
OO_ R2 might be: 110
X_X R3 might be: 000, where 1 is an O and 0 is not
Then we see that x{R1,R2,R3} & o{R1,R2,R3} => board{R1,R2,R3}
The problem is quickly generating next states for the GameTree. If I have player Max (x) with board{R1,R2,R3}, then getting the next row-states for R1, R2, and R3 is simple..
Set<Integer> R1nextStates = StateTransversalPool.get(R1);
The problem is that I have to combine each one of those states with R1 and R2.
Is there a better data structure besides Set that I could use? Is there a more efficient approach in general? I've also found Point<->State mediation cumbersome. Is there another approach that I could try there?
Thanks!
Here is the code for my ConcretePlayer class. It might help explain how players produce new states via moves, using the StateProducer (which might need to become StateFactory or StateBuilder).
public class ConcretePlayerGeneric extends AbstractPlayer {
#Override
public BinaryState makeMove() {
// Given a move and the current state, produce a new state
Point playerMove = super.strategy.evaluate(this);
BinaryState currentState = super.getInGame().getBoard().getState();
return StateProducer.getState(this, playerMove, currentState);
}
}
EDIT: I'm starting with normal TTT and moving to Quantum TTT. Given the framework, it should be as simple as creating several new Concrete classes and tweaking some things.
My suggestion:
Consider representing individual squares rather than rows, whereby +1 == O, -1 == X and 0 implies an empty square. This allows you to detect an end state by checking whether the sum of a horizontal, vertical or diagonal row equals +3 or -3.
Secondly "flatten" this 2D 3x3 matrix into a single array whereby elements[0-2] represent the first row, elements[3-5] represent the second row and elements[6-8] represent the third row.
Use either recursion or an iterative approach to generate subsequent game states given the current state of the board.
EDIT
I got bored and so decided to write some "toy code" to implement the game board, including methods to determine if it is in a terminal state and to generate the set of board states after the next move is made. It should generalise to any size board although I haven't tried. Enjoy ...
Sample Output
$ java Board
Creating board:
---
---
---
Initialising board:
-OX
O--
XO-
Terminal state: false
Generating next move states:
XOX
O--
XO-
-OX
OX-
XO-
-OX
O-X
XO-
-OX
O--
XOX
Code
import java.util.List;
import java.util.LinkedList;
import java.util.Random;
public class Board {
private final int[] squares;
public Board() {
this.squares = new int[9];
}
protected Board(int[] squares) {
this.squares = squares;
}
public void init() {
Random rnd = new Random();
int turn = 1; // 'O' always goes first.
for (int i=0; i<squares.length; ++i) {
double d = rnd.nextDouble();
if (d < 0.75) {
squares[i] = turn;
turn = turn == 1 ? -1 : 1; // Flip to other player's turn.
} else {
squares[i] = 0; // Empty square.
}
if (isTerminalState()) {
break;
}
}
}
public boolean isTerminalState() {
boolean ret = false;
boolean foundEmpty = false;
int hSum = 0;
int[] vSum = new int[3];
for (int i=0; i<squares.length; ++i) {
hSum += squares[i];
if (isWinningRow(hSum)) {
ret = true;
break;
} else if (i == 2 || i == 5) {
hSum = 0;
}
int col = i % 3;
vSum[col] += squares[i];
if (isWinningRow(vSum[col])) {
ret = true;
break;
}
if (squares[i] == 0) {
foundEmpty = true;
}
}
if (!ret) {
if (!foundEmpty) {
ret = true;
} else {
int diag1 = 0;
int diag2 = 0;
int rowSz = (int)Math.sqrt(squares.length);
for (int i=0; i<squares.length; ++i) {
if (i % (rowSz + 1) == 0) {
diag1 += squares[i];
if (isWinningRow(diag1)) {
ret = true;
break;
}
}
if (i > 0 && i % (rowSz - 1) == 0) {
diag2 += squares[i];
if (isWinningRow(diag2)) {
ret = true;
break;
}
}
}
}
}
return ret;
}
private boolean isWinningRow(int rowSum) {
return rowSum == 3 || rowSum == -3;
}
public List<Board> getNextStates() {
List<Board> ret = new LinkedList<Board>();
int tmp = 0;
for (int i=0; i<squares.length; ++i) {
tmp += squares[i];
}
// Next turn is 'O' (i.e. +1) if the board sums to 0.
// Otherwise it's 'X's turn.
int turn = tmp == 0 ? 1 : -1;
if (!isTerminalState()) {
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 0) { // Empty square
int[] squaresA = new int[squares.length];
System.arraycopy(squares, 0, squaresA, 0, squares.length);
squaresA[i] = turn;
ret.add(new Board(squaresA));
}
}
}
return ret;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 1) {
sb.append('O');
} else if (squares[i] == -1) {
sb.append('X');
} else {
assert squares[i] == 0;
sb.append('-');
}
if (i == 2 || i == 5) {
sb.append('\n');
}
}
return sb.toString();
}
public static void main(String[] args) {
System.err.println("Creating board:\n");
Board bd = new Board();
System.err.println(bd);
System.err.println("\nInitialising board:\n");
bd.init();
System.err.println(bd);
System.err.println("Terminal state: " + bd.isTerminalState() + '\n');
System.err.println("\nGenerating next move states:\n");
List<Board> nextStates = bd.getNextStates();
for (Board bd1 : nextStates) {
System.err.println(bd1.toString() + '\n');
}
}
}
Shouldn't each square have only three possible states (, X, O)?
Either store a grid of 3-state squares, or store 2 lists of moves. You don't need to store the overall board because it is defined by the moves.
Also, what do you mean by:
generating next states for the
GameTree
What is a GameTree? and what are some examples of "next states"?

Categories