Okay, I am building a game in my free time for fun. This is not for work or school. I graduated. What I didn't do is graduate in game programming so I am learning it as I go. I have developed a couple of games, but this one is probably my most advanced.
I have about 5 2d arrays. One to hold the Bitmaps, one to hold X locations, one to hold Y locations, one to hold the state of the element (they are birds, status is for flying state and hit state), and one identifying what the element is. Each array works together to give me a game board with birds flapping their wings. I can touch the game board, I get the location back. This part all works.
What I am trying to do is create a way to detect if elements nearby are the same element as the flagged element (the touched item). When this happens I basically want to flag all of these elements as "Hit". This is not just getting the 8 blocks around the element, this is getting every item touching the flagged item and identifying them as "Hit". You probably have played games like this, maybe. They are puzzle like games (mines cooler because the birds poop on you and it kills your character :) ). I created another 2D array to tack hit state (in this latest iteration of my attempt to solve the problem, so I don't double count a hit, and I am now using it to flag items I don't count with a -1.
My arrays are all [8][8] arrays. They don't change, they will always be that way. This should make it easier. So as an example:
sample from game
The circled red birds in the image should have been flagged as hit as well.
On to the code:
public void calculateGunDestruction(){
int currentRow = targetBirdRow;
int currentCol = targetBirdCol;
int hitbird = whichBird[targetBirdRow][targetBirdCol];
boolean[] pathState = new boolean[5];
countedArray = new int[8][8];
for(int j = 0; j < countedArray.length;j++){
for(int jj = 0; jj< countedArray[j].length;jj++){
countedArray[j][jj] = 0;
}
}
for(int i = currentRow;i < whichBird.length; i++) {
for (int ii = currentCol; ii < whichBird[i].length; ii++) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i > -1; i--) {
for (int ii = currentCol; ii < whichBird[i].length; ii++) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i < whichBird.length; i++) {
for (int ii = currentCol; ii > - 1; ii--) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
for(int i = currentRow;i > -1; i--) {
for (int ii = currentCol; ii > - 1; ii--) {
pathState = pathMatch(i,ii,hitbird);
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
if(!pathState[0] && !pathState[1] && !pathState[2] && !pathState[3]){
break;
}
}
}
public boolean[] pathMatch(int i, int ii, int hitbird){
boolean pathTop = false;
boolean pathBottom = false;
boolean pathLeft = false;
boolean pathRight = false;
if(findMatch(i,ii,hitbird)) {
if(countedArray[i][ii] == 0) {
countedArray[i][ii] = 1;
destroyedBirds(i, ii);
}
if((i - 1) > -1) {
if (countedArray[i - 1][ii] == 0) {
if (findMatch(i - 1, ii, hitbird)) {
countedArray[i - 1][ii] = 1;
destroyedBirds(i - 1, ii);
pathLeft = true;
} else {countedArray[i - 1][ii] = -1;pathLeft = false;}
} else {pathLeft = false;}
} else {pathLeft = false;}
if((i + 1) < 8) {
if (countedArray[i + 1][ii] == 0) {
if (findMatch(i + 1, ii, hitbird)) {
countedArray[i + 1][ii] = 1;
destroyedBirds(i + 1, ii);
pathRight = true;
} else {countedArray[i + 1][ii] = -1;pathRight = false;}
} else {pathRight = false;}
}else {pathRight = false;}
if((ii - 1) > -1) {
if (countedArray[i][ii - 1] == 0) {
if (findMatch(i, ii - 1, hitbird)) {
countedArray[i][ii - 1] = 1;
destroyedBirds(i, ii - 1);
pathTop = true;
} else {countedArray[i][ii - 1] = -1;pathTop = false;}
} else {pathTop = false;}
} else {pathTop = false;}
if((ii + 1) < 8) {
if (countedArray[i][ii + 1] == 0) {
if (findMatch(i, ii + 1, hitbird)) {
countedArray[i][ii + 1] = 1;
destroyedBirds(i, ii + 1);
pathBottom = true;
} else {countedArray[i][ii + 1] = -1;pathBottom = false;}
} else {pathBottom = false;}
}else {pathBottom = false;}
}else{
countedArray[i][ii] = -1;
}
return new boolean[]{pathTop,pathBottom,pathLeft,pathRight};
}
public boolean findMatch(int row,int col,int hitbird){
if(hitbird == whichBird[row][col]){
return true;
}
return false;
}
public void destroyedBirds(int row,int col){
destroyedBirdsRow.add(0, row);
destroyedBirdsCol.add(0, col);
}
I have tried several different ways, this is just the latest as of this writing. Some were closer than others, but none quite got it. These seems like it should be a real simple thing to do, but I am not a game programmer professionally, game programming is something I am trying to learn.
The code above basically breaks the 2D array out into quadrants from the point at which you touch. I added the control "countedArray" with this attempt because I knew there was no real way to stop double counting with this method without some sort of way to track it. It goes left/down, left,up, right/down, right/up from the touch point.
the touch points near the top:
int currentRow = targetBirdRow;
int currentCol = targetBirdCol;
int hitbird = whichBird[targetBirdRow][targetBirdCol];
hit bird holds the identifier of the touched bird or element. This is not a terribly elegant solution, but it also doesn't work. At this point elegance is thrown out the window, I am about to write a long nasty looking piece of code that no one can read checking each item individual with no looping and hope that works. So cobbled solutions are okay.
For reference sake - the area the above code is called from looks like this:
if(playerObj.getWeaponSelected() == wGun) {
calculateGunDestruction();//CALLING FUNCTION
for (int i = 0; i < destroyedBirdsRow.size(); i++) {
state[destroyedBirdsRow.get(i)][destroyedBirdsCol.get(i)] = hit;
}
destroyedBirdsRow.clear();
destroyedBirdsCol.clear();
Full Game Image
I have edited to add a full game image. Questions on why it is over complicated prompted me to explain a bit. (And yes, there may be better ways to do it - I am certainly no expert on this). The idea is, as the birds are hit, to display a brief hit image, make the hit bird disappear, and then move birds in from the left, adding a new bird(s) to replace the hit birds. That structure all works - if not perhaps over complicated in design. Single hits work, bomb hits work (when you shoot a bomb that randomly appears on the board as a new bird). I use the position arrays to guide the animation of the birds that need to move in to fill the gap left by the hit birds. I wouldn't be surprised to learn of a better way, and I am here and doing this to learn so feel free to comment outside of the original scope of the question. It may help others trying to learn, and me; and I think that really is the most important point of a site like this.
As far as the part I am having difficulty getting to work. When I shoot the gun, the idea was to have it peg all birds of the same color/species that are touching the hit bird, mark them as hit, and make them disappear. And yes, the code I currently have for that section is probably the most complicated version I wrote. I started over at least a dozen times now. The simplest version involved two groups of loops and less outside methods (it was half the size) - it worked about as well as this one, but tended to flag extra birds. This one doesn't seem to flag extra birds, but doesn't get them all.
I have built a few small games in the past - but this one is by far the most complex one I have attempted and its looking pretty good so far - except for the whole grouped kill thing.
Possibly your downfall with this is over complication in design. I am struggling to understand why you need 5 8x8 arrays to store a 2D game board.
Your solution for marking hit tiles and those around it should be something like this for simplicity:
public static final int NOTHING=0,BIRD_FLYING=1,BIRD_HIT=2,
TILE_LAND=0,TILE_WATER=1;
private int[][] gameBoard=new int[8][8]; // Store terrain etc.
private int[][] birds=new int[8][8]; // Store entities on the map
private int tileSize=32; // Store the size of the tile in pixels
private Image landImg,seaImg,flyingImg,hitImg; // Store the tile images (or use 1 tilesheet and segment it in the draw method)
private void hitTile(int x,int y)
{
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{
birds[hitX+i,hitY+j]=BIRD_HIT;
}
}
}
private void drawTheTiles()
{
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
// Draw background tiles first
switch(gameBoard[i][j])
{
default:
case TILE_LAND:
g.drawImage(landImg,i*tileSize,j*tileSize,this);
break;
case TILE_SEA:
g.drawImage(seaImg,i*tileSize,j*tileSize,this);
break;
}
// Draw entities on tiles last
switch(gameBoard[i][j])
{
default:
case NOTHING:
// Don't draw anything
break;
case BIRD_FLYING:
g.drawImage(flyingImg,i*tileSize,j*tileSize,this);
break;
case BIRD_HIT:
g.drawImage(hitImg,i*tileSize,j*tileSize,this);
break;
}
}
}
}
Of course, you'll need to add in your own handling, image loading, rendering, and array entries but there should be no need to store positions in an array for a 2D game board like this, as the positions can be implied by the tile index.
I hope this gives you something to go on. But I'm afraid I don't fully understand your original program/premise.
Related
I recently started a minesweeper project in Java. I planned to first develop the game mechanics while using the Console as output, but now I want to develop a GUI using the code I have. The problem is I have very little experience in GUI design.
One of my previous experiences in JavaFX was designing a Chess game. For the chess board, I created a 2D ImageView grid in the fxml. The images each ImageView would display would change as the pieces moved.
For my minesweeper game, I've implemented a variably sized board so I can't just create another ImageView 2D array again. I'm wondering if there is something in JavaFX that I don't know about that could help me approach this issue.
I would just like to know a good way to display the board of tiles itself. All the other GUI aspects of the game, I could figure out later.
The best thing that I could think of right now with what I know about JavaFX is that I can create a 2D ImageView array with the max size of the board. I could set all the height and width of the unused tiles to 0. However, even though this is the best idea that I have, I feel that it is a very bad way to approach the problem.
Here are the fields of the Board object
public class Board {
public final static int EASY_SIZE = 8;
public final static int MED_SIZE = 12;
public final static int HARD_SIZE = 15;
public final static int MIN_SIZE = 5;
public final static int MAX_SIZE = 26;
public int size;
protected int mine_count;
public boolean ended = false;
protected Tile[][] board;
The board output to the Console. The board.display() function:
public void display() {
System.out.println();
System.out.println(mine_count + " remaining mines");
for (int i = 0; i< 10 && i < size; i++) {
System.out.print(" _");
}
for(int i = 10; i < size; i++) {
System.out.print(" __");
}
System.out.println();
// For each row
for (int r = 0; r < size; r++) {
// For each col
for (int c = 0; c < size && c < 10; c++) {
// if the tile is flagged
if (board[r][c].flag == true) {
System.out.print("|F");
}
// else if the tile has not been clicked
else if (board[r][c].clicked == false) {
System.out.print("|#");
}
// else if mine clicked
else if (board[r][c].mine == true) {
System.out.print("|*");
}
// else if zero adj_mines
else if (board[r][c].adj_mines == 0) {
System.out.print("|_");
}
// else show number
else {
System.out.print("|" + board[r][c].adj_mines);
}
}
for (int c = 10; c < size; c++) {
// if the tile is flagged
if (board[r][c].flag == true) {
System.out.print("|F_");
}
// else if the tile has not been clicked
else if (board[r][c].clicked == false) {
System.out.print("|#_");
}
// else if mine clicked
else if (board[r][c].mine == true) {
System.out.print("|*_");
}
// else if zero adj_mines
else if (board[r][c].adj_mines == 0) {
System.out.print("|__");
}
// else show number
else {
System.out.print("|" + board[r][c].adj_mines + "_");
}
}
System.out.println("| " + r);
}
for (int i = 0; i < size; i++) {
System.out.print(" " + i);
}
System.out.println();
}
...
Since my question is regarding what sort of JavaFX layout I could use to approach the problem, I started looking more into my options. In my previous experiences in JavaFX, I designed all of the GUI elements in an FXML file, but I realize that since I'm not certain about the size of the board on bootup, I should not be doing this.
Instead, I'm planning to use a GridPane layout and I will change my Tile class to extend Button to set an EventHandler on left and right mouse clicks. If anyone has insight on why this might not work, I would love to hear it, but otherwise, I'll report back if this works on not!
Sorry for the possibly confusing title, that was the best I could come up with to describe the problem I'm having.
Anyway, down to brass tacks: I am working on a Java assignment(yes, this is classwork, hear me out) to create a dice game, called FiveDice2.java. What I need to do is have it roll five dice each for a CPU and human player, and then determine which "player" won the game, by determining if they have a pair, two of a kind, three of a kind, etc. My problem lies in that last part. I can't seem to get an idea of how to actually accomplish that goal.
My best idea was to create an array of all the possible roll values, 1-6, and compare the dice rolls to that array, and keep track of how many matches each number got in a separate variable(int ones, int twos, int threes, etc.), but the more I thought about it, the more I realized how much validation that would take, and I can't help but feel I'm not on the right track.
My best attempt goes a little something like this:
public class FiveDice2
{
public static void main(String[] args)
{
//Declare arrays for cpu and player dice
Die[] cpuDice = new Die[5];
Die[] playerDice = new Die[5];
int possibleValues = new int[]{1,2,3,4,5,6};
int ones, twos, threes, fours, fives, sixes;
int cpuHand, playerHand;
//Instantiate five Dice objects for the "CPU"
for(cpu = 0; cpu < cpuDice.length; cpu++)
{
cpuDice[cpu] = new Die();
}
//Instantiate five more Dice objects for the player
for(p = 0; p < cpuDice.length; p++)
{
playerDice[p] = new Die();
}
//Print rules of game to the console
System.out.println("Dice Game");
System.out.println("Two sets of dice will be thrown.");
System.out.println("Winner is determined by who has the better \"hand\" of dice.");
System.out.println("Each combination in the table beats all the ones below it: ");
System.out.println("-Five of a kind");
System.out.println("-Four of a kind");
System.out.println("-Three of a kind");
System.out.println("-Pair");
System.out.println("If none of the above, whoever has the highest total wins.");
//Output values of the "CPU's" rolls
System.out.println("CPU dice:");
for(cpu = 0; cpu < cpuDice.length; cpu++)
{
System.out.println(cpuDice[cpu].getDieValue());
}
System.out.println();
//Output values of the player's rolls
System.out.println("Player dice:");
for(p = 0; p < playerDice.length; p++)
{
System.out.println(playerDice[p].getDieValue());
}
System.out.println();
for(int i = 0; i < possibleValues.length; ++i)
{
if(cpuDice[i] == possibleValues[i])
{
if(cpuDice[i] == 1)
{
ones++;
}
if(cpuDice[i] == 2)
{
twos++;
}
if(cpuDice[i] == 3)
{
threes++;
}
if(cpuDice[i] == 4)
{
fours++;
}
if(cpuDice[i] == 5)
{
fives++;
}
if(cpuDice[i] == 6)
{
sixes++;
}
}
if(playerDice[i] == possibleValues[i])
{
if(playerDice[i] == 1)
{
ones++;
}
if(playerDice[i] == 2)
{
twos++;
}
if(playerDice[i] == 3)
{
threes++;
}
if(playerDice[i] == 4)
{
fours++;
}
if(playerDice[i] == 5)
{
fives++;
}
if(playerDice[i] == 6)
{
sixes++;
}
}
}
}
}
It would be great if someone could help me understand where I'm going wrong or how I should be changing my thinking about how to do this; I'm not asking to be hand-fed an answer, I'd just like some assistance in understanding how to get there myself. Thanks in advance.
Well, for starters:
int possibleValues = new int[]{1,2,3,4,5,6};
You don't need that array. The value at any index i is just i+1 - so just use i+1 where you need it. On the other hand,
int ones, twos, threes, fours, fives, sixes;
This is what an array is for. Rather than having six separate variables, you could have a single array; something like:
int [] valueCounts = new int[6];
The idea is that the count of ones would be in valueCounts[0], of twos would be in valueCounts[1], and so on. However, I assume you really want to count the player dice and CPU dice separately; therefore, you should either use two separate arrays, or a two dimensional array:
int [] playerValueCounts = new int[6];
int [] cpuValueCounts = new int[6];
// or just:
int [][] valueCounts = new int[2][6];
(In the latter case, valueCounts[0] represents the 6 value counts for the player, and valueCounts[1] represents the 6 value countes for the CPU - but if you haven't come across multidimensional arrays yet, just go with the first option).
Then, instead of:
if(cpuDice[i] == 1)
{
ones++;
}
if(cpuDice[i] == 2)
{
twos++;
}
... etc, just do:
cpuValueCounts[cpuDice[i].getDieValue() - 1]++;
In fact, you don't need this loop:
for(int i = 0; i < possibleValues.length; ++i)
Instead, you should loop through the dies themselves:
for(int i = 0; i < playerDice.length; ++i)
The body of the loop can literally be replaced with two lines:
playerValueCounts[playerDice[i].getDieValue() - 1]++;
cpuValueCounts[cpuDice[i].getDieValue() - 1]++;
This will increment the count of the appropriate value for each player and CPU die in turn. Now, the problem of determining the N for "N-of-a-kind" is just a matter of looping through the playerValueCounts (and then the cpuValueCounts) and looking for the highest value - that is, the value count which occurs most often.
So for one of my game models, there is an array of elements represented as a string "--X-X" so that this board has 5 boxes, and positions are 0-4. Each 'X' can only move left. There is an "X-Index" so that if I run getIXPosition(1) it will return the position of the first X which is 2. And getXPosition(2) will return second X's position which is 4. So the string board is "--X-X" but in my code it's represented as an array as "00102" so that I can keep track of xIndex.
Now my issue is that I need to make a move method that prevents the second x from skipping over the first X into position 1 or 0. That is not allowed in this game. I thought I wrote the method correctly but my tests aren't passing when I test to make sure second X can not hop over any X's before it.
public void move(int xIndex, int newPosition)
{
int oldPosition = getXPosition(xIndex);
for(int i= oldPosition - 1; i >= 0;i--)
{
while (board[i] == 0 )
{
board[oldPosition] = '0'; // so this spot is open '-'
board[newPosition] = xIndex;
}
throw new IllegalArgumentException("Error cannot move X to new position");
}
}
What am I doing wrong?
If you know the position you want to move to, you don't have to search for it, just move there.
if (board[newPosition] == '0') {
board[newPosition[ = xIndex;
board[oldPosition] = '0';
} else {
throw new IllegalArgumentException("Error cannot move X to new position");
}
Note: The character '0' is not the value 0 (Actually it is 48 in ASCII)
I think the code is a bit flawed. First, I don't think you need to iterate all the way to 0. You should only iterate until you hit newPosition.
Then the while loop doesn't make much sense. I think you were after an if.
Lastly, personally I wouldn't throw an IllegalArgumentException in this case (actually, you're throwing after the first iteration regardless, so that's another flaw). It's the state of the board that's problematic, not the arguments. I would maybe throw IllegalArgumentException if one of the arguments was negative etc.
public void move(int xIndex, int newPosition) {
int oldPosition = getXPosition(xIndex);
for(int i= oldPosition - 1; i >= newPosition; i--) {
if(board[i] == '0') {
board[oldPosition] = '0';
board[i] = xIndex;
oldPosition = i;
} else {
//throw some custom exception; we found the other X here.
}
}
}
I'm currently working on an Android App containing TicTacToe with an AI opponent.
I've come real far, but for some reason not all moves the opponent calculates are leading him to victory or a tie.
I've made a recursive MiniMax algorithm (no pruning yet), and I hope somebody is able to see what's wrong here, since I really couldn't find it. I've drawn many trees so far and
I've done a lot of debugging in Eclipse ADE and every step seems to be right, but in the end, it's obviously not.
So here's my function:
public int[] MiniMax( int[][] gameboard, boolean isCPUTurn, int[] move, int depth ){
if (gameboard[1][1]==EMPTY) { // Always take middle if possible
int[] best = {2,1,1};
return best;
}
// Recursion base-case
int winner = checkWinner();
if ( winner == 1 || winner == 2 || winner == 3 ){ // Game is over
int[] returnSet = {-1,-1,-1}; // leeg
// adjust scores for Minimax Player<----Tie---->CPU
// 0 1 2
switch (winner){
case 1:
returnSet[0] = 0 ; // Player wins
break;
case 2:
returnSet[0] = 2; // CPU wins
break;
case 3:
returnSet[0] = 1; // TIE
break;
default: // impossible..
returnSet[0] = 1;
}
// The last move led to the end of the game, return this outcome and move
returnSet[1] = move[0];
returnSet[2] = move[1];
return returnSet;
}
// A move is still possible, the game's not over yet
else{
int[] bestMove = null; // contains the best move found for this turn
for (int i=0; i<3; i++){ // check all child nodes/boards
for (int j=0; j<3; j++){
if( gameboard[i][j] == EMPTY ) // if the spot's not yet filled
{
if (isCPUTurn) gameboard[i][j] = TWO; // this move is CPU's
else gameboard[i][j] = ONE; // this move is Player's
int[] thismove = {i,j}; // {move Row, move Column}
// recursion
int[] result = MiniMax( gameboard, !isCPUTurn, thismove, depth+1);
// result = { winner result, move Row, move Column}
gameboard[i][j] = EMPTY; // delete last move after recursion
// check if child is best option
if (bestMove == null) // first child is always the best initially
bestMove = result;
else {
if (!isCPUTurn) // for CPU, higher is better
{ if (result[0]>bestMove[0])
bestMove = result;
//if (bestMove[0]==2) return bestMove; Pruning thingy
}
else if (isCPUTurn) // for Player, lower is better
{ if (result[0]<bestMove[0])
bestMove = result;
//if (bestMove[0]==0) return bestMove; Pruning thingy
}
}
}
}
}
return bestMove; // returns the best move found after checking all possible childs
}
}
As you can see, this function calls itself directly, using middle recursion.
It also calls the checkWinner function, which has 4 possible outcomes: 0 if nobody has won yet and the board is not filled, 1 if player 1 (you) has won, 2 if player 2 (AI) has won and 3 if it's a tie (and thus the board is filled).
So yea, I hope somebody finds the solution :D
Thanks in advance!
The problem is that you are always returning the move from the deepest level. In the lines where you check if you have improved
, you should set bestMoveto contain the coordinates iand j, not the coordinates from the move one level deeper, namely result.
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"?