I have a grid composed with JLabel. Each cell has 3 states:
public enum Token{
VIDE, CERCLE_ROUGE, CERCLE_BLEU
}
An empty cell == Token.VIDE
I have this simple algorithm that find all neighbors of a given cell then I use swing custom painting to draw a polygon following the path having the center of the label as points.
private CellsList getNeighbors(int row, int col) {
CellsList neighbors = new CellsList();
for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1 ) {
for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1 ) {
if(!((colNum == col) && (rowNum == row))) {
if(withinGrid (rowNum, colNum ) ) {
neighbors.add( new int[] {rowNum, colNum});
}
}
}
}
return neighbors;
}
here is the condition to have a path:
if((size() >= MIN_PATH_LEGTH) && neighbors.contains(origin) ) {
add(cell);
return true;
}
Now I want to make it more precise by adding conditions such as a path can be valid if only there are at least 4 cells with the same value found in the neighborhood AND at least one opposite value.
Example:
Red cells at (11, 10)(10, 11)(11, 12)(12, 11) can form a valid path if only there is at least a Blue cell at (11, 11). (See image below)
And the following can't be a valid path, so no drawing:
For now, the algorith finds a path only with the minimal value (int MIN_PATH_LEGTH = 3) I've defined but I can't find a way to define the second condition (at least one opposite cell value within the neighborhood)
I'll edit with new elements if needed.
test before calling this method if the redCell's path OK:
public boolean isThereABlueCellWithinMyRedPath() {
// height = height of your map
// width = width of yout map
// this array is suppose to be in an other place...
Cell[][] cellArray = new Cell[height][width];
// ... so are those two lists
List<Cell> blueCellsList = new ArrayList<>();
List<Cell> redCellsList = new ArrayList<>();
//foreach blueCell on the map ...
for (Cell blueCell : blueCellsList) {
boolean north = false;
boolean east = false;
boolean south = false;
boolean west = false;
int originX = blueCell.getX();
int originY = blueCell.getY();
// ... if there is a redCell at north...
for (int i = originY; i >= 0; i--) {
if (redCellsList.contains(cellArray[originX][i])) {
north = true;
break;
}
}
// ... East ...
for (int i = originX; i < cellArray[originX].length; i++) {
if (redCellsList.contains(cellArray[i][originY])) {
east = true;
break;
}
}
// ... South ...
for (int i = originY; i < cellArray.length; i++) {
if (redCellsList.contains(cellArray[originX][i])) {
south = true;
break;
}
}
// ... West ...
for (int i = originX; i >= 0; i--) {
if (redCellsList.contains(cellArray[i][originY])) {
west = true;
break;
}
}
// ... I am surrended by redCell
if (south && east && north && west)
return true;
}
return false;
}
I did not try the code but it seems to work.
My answer is based on the code posted in a previous answer.
Path class
Add isWithinLoop to check if a cell is within path by checking if it has a
path-cell to its left, right, top and bottom.
Also add getContainedWithin which returns a collection of all cells that are bounded by the path, and their token is of the opposite color of the path
//returns a collection of all cells that are bounded by the path
//and their token is of the opposite color of the path
List<int[]> getContainedWithin() {
//find path max and min X values, max and min Y values
minPathRow = grid[0].length; //set min to the largest possible value
maxPathCol = grid.length;
maxPathRow = 0; //set max to the largest possible value
maxPathCol = 0;
//find the actual min max x y values of the path
for (int[] cell : this) {
minPathRow = Math.min(minPathRow, cell[0]);
minPathCol = Math.min(minPathCol, cell[1]);
maxPathRow = Math.max(maxPathRow, cell[0]);
maxPathCol = Math.max(maxPathCol, cell[1]);
}
//todo remove after testing
System.out.println("x range: "+minPathRow + "-"
+ maxPathRow + " y range: " + minPathCol + "-" + maxPathCol);
List<int[]> block = new ArrayList<>(25);
int[] cell = get(0);//get an arbitrary cell in the path
Token pathToken = grid[cell[0]][cell[1]]; //keep a reference to its token
//iterate over all cells within path x, y limits
for (int col = minPathCol; col < (maxPathCol); col++) {
for (int row = minPathRow; row < (maxPathRow); row++) {
//check cell color
Token token = grid[row][col];
if ((token == pathToken) || (token == Token.VIDE)) {
continue;
}
if (isWithinLoop(row,col)) {
block.add(new int[] {row, col});
}
}
}
return block;
}
//check if row, col represent a cell with in path by checking if it has a
//path-cell to its left, right, top and bottom
private boolean isWithinLoop(int row, int col) {
if( isPathCellOnLeft(row, col)
&&
isPathCellOnRight(row, col)
&&
isPathCellOnTop(row, col)
&&
isPathCellOnBottom(row, col)
) {
return true;
}
return false;
}
private boolean isPathCellOnLeft(int cellRow, int cellCol) {
for ( int col = minPathCol; col < cellCol ; col++) {
if(getPath().contains(new int[] {cellRow, col})) {
return true;
}
}
return false;
}
private boolean isPathCellOnRight(int cellRow, int cellCol) {
for ( int col = cellCol; col <= maxPathCol ; col++) {
if(getPath().contains(new int[] {cellRow, col})) {
return true;
}
}
return false;
}
private boolean isPathCellOnTop(int cellRow, int cellCol) {
for ( int row =minPathRow; row < cellRow ; row++) {
if(getPath().contains(new int[] {row, cellCol})) {
return true;
}
}
return false;
}
private boolean isPathCellOnBottom(int cellRow, int cellCol) {
for ( int row = cellRow; row <= maxPathRow; row++) {
if(getPath().contains(new int[] {row, cellCol})) {
return true;
}
}
return false;
}
}
Model class
Add a getter method to access getContainedWithin:
List<int[]> getContainedWithin() {
return (path == null ) ? null : path.getContainedWithin();
}
Control class
Update ModelListener to ignore a path if getContainedWithin is empty:
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
IndexedPropertyChangeEvent iEvt = (IndexedPropertyChangeEvent)evt;
int index = iEvt.getIndex();
int row = index / Model.COLS;
int col = index % Model.COLS;
Token token = (Token) evt.getNewValue();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
view.setCell(token, row, col);
CellsList path = model.getPath();
//ignore path if null, empty or encloses no cell
if((path == null) || path.isEmpty()
|| model.getContainedWithin().isEmpty()) {
return;
}
view.addPath(path);
view.refresh();
}
});
}
}
The complete code can be downloaded for this repo.
Maybe you can make a list of blue cells and redcells and if you want to test if a there's a blue cell within the red cell's path you:
go to NORTH until you meet a redCell or a map limit : if it 's a map limit you test with the next blueCell, else you go back to your position and do the same for EAST, SOUTH, WEST.
If on the 4 sides you find a redCell first, you return true else you return false.
It can be enough if you want to test in a quite small map. But if the shape made by the red cell can be free I don't find a better way.
Related
I'm currently developing an algorithm for my Tictactoe Android App (using Java) with Minimax, but first, I just want to make sure the algorithm works, so I made the algorithm to "suggests" the next move to the player.
The player is declared as String player = "x"; in the Minimax class.
Minimax.java
package com.firaz.tictactoe;
public class Minimax {
public static class Move {
int row, col;
}
static String player = "x";
static String opponent = "o";
String[][] board = new String[3][3];
// This function returns true if there are moves
// remaining on the board. It returns false if
// there are no moves left to play.
static Boolean isMovesLeft(String[][] board) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i][j].equals("_"))
return true;
return false;
}
// This is the evaluation function
static int evaluate(String[][] b) {
// Checking for Rows for X or O victory.
for (int row = 0; row < 3; row++) {
if (b[row][0].equals(b[row][1]) &&
b[row][1].equals(b[row][2])) {
if (b[row][0].equals(player))
return +10;
else if (b[row][0].equals(opponent))
return -10;
}
}
// Checking for Columns for X or O victory.
for (int col = 0; col < 3; col++) {
if (b[0][col].equals(b[1][col])
&& b[1][col].equals(b[2][col])) {
if (b[0][col].equals(player))
return +10;
else if (b[0][col].equals(opponent))
return -10;
}
}
// Checking for Diagonals for X or O victory.
if (b[0][0].equals(b[1][1])
&& b[1][1].equals(b[2][2])) {
if (b[0][0].equals(player))
return +10;
else if (b[0][0].equals(opponent))
return -10;
}
if (b[0][2].equals(b[1][1])
&& b[1][1].equals(b[2][0])) {
if (b[0][2].equals(player))
return +10;
else if (b[0][2].equals(opponent))
return -10;
}
// Else if none of them have won then return 0
return 0;
}
// This is the minimax function. It considers all
// the possible ways the game can go and returns
// the value of the board
static int minimax(String[][] board,
int depth, Boolean isMax) {
int score = evaluate(board);
// If Maximizer has won the game
// return his/her evaluated score
if (score == 10)
return score;
// If Minimizer has won the game
// return his/her evaluated score
if (score == -10)
return score;
// If there are no more moves and
// no winner then it is a tie
if (isMovesLeft(board) == false)
return 0;
// If this maximizer's move
if (isMax) {
int best = -1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = player;
// Call minimax recursively and choose
// the maximum value
best = Math.max(best, minimax(board,
depth + 1, !isMax));
// Undo the move
board[i][j] = "_";
}
}
}
return best;
}
// If this minimizer's move
else {
int best = 1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = opponent;
// Call minimax recursively and choose
// the minimum value
best = Math.min(best, minimax(board,
depth + 1, !isMax));
// Undo the move
board[i][j] = "_";
}
}
}
return best;
}
}
// This will return the best possible
// move for the player
static Move findBestMove(String[][] board) {
int bestVal = -1000;
Move bestMove = new Move();
bestMove.row = -1;
bestMove.col = -1;
// Traverse all cells, evaluate minimax function
// for all empty cells. And return the cell
// with optimal value.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = player;
// compute evaluation function for this
// move.
int moveVal = minimax(board, 0, false);
// Undo the move
board[i][j] = "_";
// If the value of the current move is
// more than the best value, then update
// best/
if (moveVal > bestVal) {
bestMove.row = i;
bestMove.col = j;
bestVal = moveVal;
}
}
}
}
return bestMove;
}
public void setBoard(String[][] Board){
this.board = Board;
Move bestMove = findBestMove(board);
PlayerVersusComputer playerVersusComputer = new PlayerVersusComputer();
playerVersusComputer.setRow(bestMove.row);
playerVersusComputer.setCol(bestMove.col);
}
}
I'm expecting a return integer (row and col) from the Minimax.java to the PlayerVersusComputer.java in this code snippet.
PlayerVersusComputer.java
String buttonID = getResources().getResourceEntryName(v.getId()); //button_ij
int pointerOne = Integer.parseInt(buttonID.substring(7, buttonID.length()-1)); //obtain i
int pointerTwo = Integer.parseInt(buttonID.substring(buttonID.length()-1)); //obtain j
if (player1Turn) {
((Button) v).setText("x");
board[pointerOne][pointerTwo] = "x";
} else {
((Button) v).setText("o");
board[pointerOne][pointerTwo] = "o";
checkBestMove();
}
protected void checkBestMove() {
minimax.setBoard(board);
row = getCol();
col = getRow();
tvAI.setText(row+"R"+" "+col+"C");
}
This is the board Array. The board will "update" itself every time I pressed the ((Button) v).setText("x"); or ("o"), then passed to Minimax.java when it's O's turn.
String[][] board = {{"_","_","_"},
{"_","_","_"},
{"_","_","_"}};
Since the row and col is not returned to the checkBestMove() function (I'm not sure if it's not returned or just can't "update" itself), the tvAI.setText keeps saying "0R0C" instead of updating itself (to 2R2C, then 0R1C, etc).
Update to the row and col are expected because the tvAI.setText is intended to suggest the player's next move. So why it's not updating?
In function setBoard is the call of findBestMove(board); but the return value of type Move is ignored. Maybe you can assign it to a variable.
this.bestMove = findBestMove(board);
I have a grid of colors (in a 2D ArrayList). I need to be able to count the number of cells that share the same color in a particular color block (they have to be adjacent on 4 edges). I can do this easily recursively, but the problem is that some images Overflow the stack since color blocks can be so big.
Here's the recursive function:
private int getBlockCount(PietCodel codel) {
if (codel.getValue() != PietCodel.DEFAULT && codel.getValue() != PietCodel.CHECKED) {
return codel.getValue();
}
ArrayList<PietCodel> list = blockCountHelper(codel);
list.add(codel);
// Use the array of codels in the block, and
// use the size to for each value in the array.
int result = list.size();
for (PietCodel item : list) item.setValue(result);
System.out.println("Block count: " + result);
return result;
}
private ArrayList<PietCodel> blockCountHelper(PietCodel codel) {
ArrayList<PietCodel> result = new ArrayList<>();
codel.setValue(PietCodel.CHECKED);
int col = codel.getCol();
int row = codel.getRow();
// Right
PietCodel ajac = get(col + 1, row);
if (ajac != null && codel.equals(ajac.getColor()) && ajac.getValue() == PietCodel.DEFAULT) {
ArrayList<PietCodel> nextCodels = blockCountHelper(ajac);
result.add(ajac);
result.addAll(nextCodels);
}
// Down
ajac = get(col, row + 1);
if (ajac != null && codel.equals(ajac.getColor()) && ajac.getValue() == PietCodel.DEFAULT) {
ArrayList<PietCodel> nextCodels = blockCountHelper(ajac);
result.add(ajac);
result.addAll(nextCodels);
}
// Left
ajac = get(col - 1, row);
if (ajac != null && codel.equals(ajac.getColor()) && ajac.getValue() == PietCodel.DEFAULT) {
ArrayList<PietCodel> nextCodels = blockCountHelper(ajac);
result.add(ajac);
result.addAll(nextCodels);
}
// Up
ajac = get(col, row - 1);
if (ajac != null && codel.equals(ajac.getColor()) && ajac.getValue() == PietCodel.DEFAULT) {
ArrayList<PietCodel> nextCodels = blockCountHelper(ajac);
result.add(ajac);
result.addAll(nextCodels);
}
return result;
}
Any thoughts on an alternative with loops or something?
The idea is to make the "stack/queue" explicit in your application code. Note that this doesn't use less memory then the recursive approach, it just
has more memory to play with by utilizing the heap. The following code is an example. Note that you can call queue.addFirst or queue.addLast, this will
not change the end result but will give you different traversals of the board which is something you may or may not care about.
private ArrayList<PietCodel> blockCountHelper(PietCodel codel) {
ArrayList<PietCodel> accumulator = new ArrayList<>();
LinkedList<PietCodel> queue = new LinkedList<>();
queue.add(codel);
while (!queue.isEmpty()) {
PietCodel ajac = queue.remove();
if (ajac != null && codel.equals(ajac.getColor()) .... ) {
accumulator.add(ajac);
}
if ( get(col + 1, row) != null ) {queue.addFirst(get(col + 1, row));}
if ( get(col , row + 1) != null ) {queue.addFirst(get(col, row + 1));}
if ( get(col - 1, row) != null ) {queue.addFirst(get(col - 1, row));}
if ( get(col , row - 1) != null ) {queue.addFirst(get(col, row- 1));}
}
return accumulator;
}
Standard way to get rid of recursion is to use Stack data structure, as recursion is essentially a stack manipulation. But in your concrete situation you can use breadth-first search. You can implement it using queue:
int rows = 10;
int cols = 10;
PietCodel codels[][] = new PietCodel[rows][cols];
boolean used[][] = new boolean[rows][cols];
private void test() {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < rows; ++j) {
int color = (int) (Math.random() * 3);
PietCodel codel = new PietCodel(i, j, color);
codels[i][j] = codel;
System.out.print(color + " ");
}
System.out.println();
}
System.out.println();
System.out.println(getBlockCount(get(0, 0)));
}
private int getBlockCount(PietCodel codel) {
used = new boolean[rows][cols];
Queue<PietCodel> q = new LinkedList<>();
q.add(codel);
used[codel.getRow()][codel.getCol()] = true;
int color = codel.getColor();
int count = 0;
while (!q.isEmpty()) {
PietCodel ajacent = q.poll();
int col = ajacent.getCol();
int row = ajacent.getRow();
++count;
addColored(q, col + 1, row, color);
addColored(q, col - 1, row, color);
addColored(q, col, row + 1, color);
addColored(q, col, row - 1, color);
}
return count;
}
private PietCodel get(int col, int row) {
return col < 0 || col >= cols || row < 0 || row >= rows ? null : codels[row][col];
}
private void addColored(Queue<PietCodel> q, int col, int row, int color) {
if (col < 0 || col >= cols || row < 0 || row >= rows) {
return;
}
PietCodel codel = codels[row][col];
if (codel.getColor() != color || used[row][col]) {
return;
}
used[row][col] = true;
q.add(codel);
}
static class PietCodel {
static final int DEFAULT = 0;
static final int CHECKED = -1;
static final int USED = -2;
final int row;
final int col;
final int color;
int value;
public PietCodel(int row, int col, int color) {
this.col = col;
this.row = row;
this.color = color;
}
public int getCol() {
return col;
}
public int getRow() {
return row;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getColor() {
return color;
}
public boolean same(PietCodel ajac) {
return color == ajac.getColor();
}
}
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'm having trouble coding the 8 queens problem. I've coded a class to help me solve it, but for some reason, I'm doing something wrong. I kind of understand what's supposed to happen.
Also, we have to use recursion to solve it but I have no clue how to use the backtracking I've read about, so I just used it in the methods checking if a position is legitimate.
My board is String [] [] board = { { "O", "O"... etc etc with 8 rows and 8 columns.
If I'm getting anything wrong conceptually or making a grave Java mistake, please say so :D
Thanks!
public void solve () {
int Queens = NUM_Queens - 1;
while (Queens > 0) {
for (int col = 0; col < 8; col++) {
int row = -1;
boolean c = false;
while (c = false && row < 8) {
row ++;
c = checkPos (row, col);
}
if (c == true) {
board[row][col] = "Q";
Queens--;
}
else
System.out.println("Error");
}
}
printBoard ();
}
// printing the board
public void printBoard () {
String ret = "";
for (int i = 0; i < 8; i++) {
for (int a = 0; a < 8; a++)
ret += (board[i][a] + ", ");
ret += ("\n");
}
System.out.print (ret);
}
// checking if a position is a legitimate location to put a Queen
public boolean checkPos (int y, int x) {
boolean r = true, d = true, u = true, co = true;
r = checkPosR (y, 0);
co = checkPosC (0, x);
int col = x;
int row = y;
while (row != 0 && col != 0 ) { //setting up to check diagonally downwards
row--;
col--;
}
d = checkPosDD (row, col);
col = x;
row = y;
while (row != 7 && col != 0 ) { //setting up to check diagonally upwards
row++;
col--;
}
d = checkPosDU (row, col);
if (r = true && d = true && u = true && co = true)
return true;
else
return false;
}
// checking the row
public boolean checkPosR (int y, int x) {
if (board[y][x].contentEquals("Q"))
return false;
else if (board[y][x].contentEquals("O") && x == 7)
return true;
else //if (board[y][x].contentEquals("O"))
return checkPosR (y, x+1);
}
// checking the column
public boolean checkPosC (int y, int x) {
if (board[y][x].contentEquals("Q"))
return false;
else if (board[y][x].contentEquals("O") && y == 7)
return true;
else //if (board[y][x].contentEquals("O"))
return checkPosR (y+1, x);
}
// checking the diagonals from top left to bottom right
public boolean checkPosDD (int y, int x) {
if (board[y][x].contentEquals("Q"))
return false;
else if (board[y][x].contentEquals("O") && (x == 7 || y == 7))
return true;
else //if (board[y][x].contentEquals("O"))
return checkPosR (y+1, x+1);
}
// checking the diagonals from bottom left to up right
public boolean checkPosDU (int y, int x) {
if (board[y][x].contentEquals("Q"))
return false;
else if (board[y][x].contentEquals("O") && (x == 7 || y == 0))
return true;
else //if (board[y][x].contentEquals("O"))
return checkPosR (y-1, x+1);
}
}
As this is homework, the solution, but not in code.
Try to write a method that only handles what needs to happen on a single column; this is where you are supposed to use recursion. Do backtracking by checking if a solution exists, if not, undo your last change (i.e. change the queen position) and try again. If you only focus on one part of the problem (one column), this is much easier than thinking about all columns at the same time.
And as Quetzalcoatl points out, you are assigning false to your variable in the first loop. You probably do not want to do that. You should always enable all warnings in your compiler (run javac with -Xlint) and fix them.
You are trying some kind of brute-force, but, as you already mentioned, you have no recursion.
Your programs tries to put a queen on the first possible position. But at the end no solution is found. It follows that your first assumption (the position of your first queen) is invalid. You have to go back to this state. And have to assume that your checkPos(x,y) is false instead of true.
Now some hints:
As mentioned before by NPE int[N] queens is more suitable representation.
sum(queens) have to be 0+1+2+3+4+5+6+7=28, since a position has to be unique.
Instead of checking only the position of the new queen, you may check a whole situation. It is valid if for all (i,j) \in N^2 with queen(i) = j, there exists no (k,l) != (i,j) with abs(k-i) == abs(l-j)
Hello I am writing a program for a programming class and I am getting a:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 18
at Life.countNeighbors(Life.java:200)
at Life.genNextGrid(Life.java:160)
I've gotten ArrayIndexOutOfBoundsException errors before, usually caused when I try to add or substract a index of an array. However this time it is quite different and I was hoping someone here can point out why the error is occuring.
info about my program: the program is like John Conway game of life. using a 2d array and then setting certain elements to (true = alive) or (false = dead) the program then determine if a cell lives or dies in the next generation base on the number of neighbors it has. (3 neighbors = birth of a new cell) (2,3 neighbor = they stay alive) anything else they died in the next generation.
The IndexOutOfBound error is caused by this line according to my editor:
if(!(grid[1][1]) && !(grid[1][18]) && !(grid[18][1]) && !(grid[18][18]))
I created the above line as a constraint, and it should not tell java to search a index beyond the bounds of the original array since they are merely booleans (true/false)statement in the end. If anyone can help me debug this error that would be splendid.
HERE IS MY CODE: (does not include main method)
public static void clearGrid ( boolean[][] grid )
{
int col;
int row = 1;
while(row < 18){
for(col = 1; col < 18; col++){
grid[row][col]= false;//set each row to false
}
row++;
}//set all elements in array to false
}
public static void genNextGrid ( boolean[][] grid )
{
//new tempprary grid
boolean[][] TempGrid = new boolean[GRIDSIZE][GRIDSIZE];
TempGrid= grid; // copy the current grid to a temporary grid
int row = 1;
int col = 1;
countNeighbors(TempGrid, row, col); // passes the TempGrid to countNieghbor method
for(row = 1; row < 18; row++){
countNeighbors(TempGrid, row, col);
for(col = 1; col < 18; col++){
countNeighbors(TempGrid, row, col);
if(countNeighbors(grid, row, col) == 3)
{
TempGrid[row][col] = true;
}
else if(countNeighbors(grid, row, col) == 2 || countNeighbors(grid, row, col) == 3)
{
TempGrid[row][col] = true;
}
else
{
TempGrid[row][col] = false;
}
}
}
}
public static int countNeighbors ( final boolean[][] grid, final int row, final int col )
{
int n = 0; //int used to store the # of neighbors
int temprow = row;
int tempcol = col;
//count # of neighbors for the cell on the edge but not the corner
for(temprow = row; temprow <= 18; temprow++)
{
for(tempcol = row; tempcol <= 18; tempcol++)
{
if(temprow == 1 || temprow == 18 || tempcol == 1 || tempcol ==18)
{
if(!(grid[1][1]) && !(grid[1][18]) && !(grid[18][1]) && !(grid[18][18]))
{
if(grid[temprow][tempcol] == true)
{
n++;
}
}
}
}
}
//count # of neighbors for the corner cells
for(temprow = row; temprow <= 18; temprow++)
{
for(tempcol = row; tempcol <= 18; tempcol++)
{
if(grid[1][1] || grid[1][18] || grid[18][1] || grid[18][18])
{
if(grid[temprow][tempcol] == true)
{
n++;
}
}
}
}
// count the cells that are not on the edge or corner
while(temprow >= 2 && tempcol >= 2 && temprow <= 17 && tempcol <= 17)
{
for(temprow = row; temprow-1 <= temprow+1; temprow++)
{
for(tempcol = col; tempcol-1 <= tempcol+1; tempcol++)
{
if(grid[temprow][tempcol] == true)
{
n++;
}
}
}
}
return n; // return the number of neighbors
}
Without a full stack trace and an indication as to where the problem lies, this is my best guess:
grid[18][1]
The value 18 is beyond the size of the array you can access, in Java arrays are zero based (0). Since I have seen 17 all throughout your post, this seems like the most logical reason why.
In Java, array indices are numbered from 0 to n-1. From looking at your code, it would appear that it assumes that they are numbered from 1 to n.