I have made a class where a 6x10 2D array is generated to act as a board.
A random starting location is then generated in the constructor.I only want adjacent moves to be possible.
For example, if the random location has been generated as (2,3) then for example the user enters (1,2) it would be a valid move, but (6,1) would be an invalid move.
Then if the user enters say (1,2), they can then go to any adjacent cell from (1,2).
I have included the class below, and the adjacent method I tried to make to test it, but I'm a bit confused on how I am approaching this.
import java.util.Arrays;
import java.util.Random;
public class Test {
public static final int ROWS = 6;
public static final int COLUMNS = 10;
public int[][] board;
public static void main(String[] args)
{
Test t = new Test();
t.getBoard();
t.makeMove(6,1); //I want this to be an invalid move.
t.getBoard();
t.makeMove(1,2); // this should be a valid move
t.getBoard();
}
public Test()
{
board = new int[ROWS][COLUMNS];
createRandomLocation();
}
public void createRandomLocation()
{
Random rand = new Random();
int x = rand.nextInt(6);
int y = rand.nextInt(10);
board[x][y] = 1;
}
public void makeMove(int x,int y){
if (Math.abs(x-cur_x)==0 || Math.abs(y-cur_y)==0) {
board[x][y] = 1;
}
public String getBoard() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
System.out.println();
return Arrays.deepToString(board);
}
}
Adjacent:
/*public boolean isMoveAllowed(int [][] array,int x, int y){
boolean adjacent = false;
int trueCount = 0;
if(array[x-1][y-1] == 0) trueCount++; //topleft
if(array[x-1][y] == 0) trueCount++; //top
if(array[x-1][y+1] == 0) trueCount++;//topright
if(array[x][y+1] == 0) trueCount++;//right
if(array[x][y-1] == 0) trueCount++;//left
if(array[x+1][y-1] == 0) trueCount++;//bottomleft
if(array[x+1][y] == 0) trueCount++;//bottom
if(array[x+1][y+1] == 0) trueCount++; //bottomright
if (trueCount == 8)
{
adjacent = true;
}
return adjacent;
}*/
Your problem description has the answer baked into it already. You want any move from (a,b) to (c,d) to be legal if the distance between a and c, and b and d, is zero or one. So if you see Math.abs(a-c)>1, that's an illegal move. So: have the current position stored in some variables, and compare them to the desired new location:
public static void main(String[] args)
{
Board b = new Board(6, 10);
try {
b.tryMove(6,1);
} catch(IllegalMoveException e) {
// do whatever you need to do to inform the user that move is illegal
}
}
With the Board class responsible for tracking coordinates:
class Board {
protected int cur_x, cur_y, rows, cols;
public Board(int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.setRandomPosition();
}
public void setRandomPosition() {
cur_x = (int) Math.round(Math.random() * cols);
cur_y = (int) Math.round(Math.random() * rows);
}
public void tryMove(int x, int y) throws IllegalMoveException {
if (Math.abs(x-cur_x)>1 || Math.abs(y-cur_y)>1) {
throw new IllegalMoveException(...);
}
// bounds check omitted here, but: ensure that
// 0<=x<cols and 0<=y<rows, otherwise throw an
// IllegalMoveException as well.
cur_x = x;
cur_y = y;
}
// with getters for the current x and y, etc.
}
It would be much easier to test for a true case rather than a false case like you currently have, the isMoveAllowed method should look something like this:
public boolean isMoveAllowed(int[][] array, int x, int y) {
return ((array[x + 1][y] == 1) ||
(array[x - 1][y] == 1) ||
(array[x][y + 1] == 1) ||
(array[x][y - 1] == 1));
}
This will return true if the move is adjacent to the current player position
Related
I have this program called knight tour where the knight moves around a chess board. I have been trying to figure out how to make the knight move randomly, instead of following a pattern.
I would like to know how to randomly move the knight.
Here's my code:
package assignment3;
import java.util.Random;
/*
* knows its current position (row and column)
* knows the eight types of moves it can make
* can tell you it’s current row and column
* can determine whether a move of a given type is legal or not
* can move
*/
public class Knight {
private int boardSize = 8;
private int[] rowMoves = {-1, -2, -2, -1, 1, 2, 2, 1};
private int[] colMoves = {2, 1, -1, -2, -2, -1, 1, 2};
public Knight() {
//ignore this constructor
}
public void InitializeBoard() {
//initialize board
for (int i = 0; i < boardSize; i++)
Arrays.fill(chessboard2[i], Integer.MIN_VALUE); //setting array to negative value
}
/**
* calls method canMove to check if knight can move
* moves knight
*/
public boolean move(int moveNum, int x, int y, int[][] chessboard2) {
Random rand = new Random();
//if moveNum == 64 all squares have been visited
if (moveNum == 64) {
System.out.println("\ntrue board is 64\n");
return true;
}
//int nextRow = rand.nextInt(boardSize);
//int nextCol = rand.nextInt(boardSize);
//for loop to try 8 possibe moves
for (int i = 0; i < rowMoves.length; i++) {
int nextRow = x + rowMoves[i];
int nextCol = y + colMoves[i];
//check if postion is valid and not visited yet
if (canMove(nextRow, nextCol) && chessboard2[nextRow][nextCol] == Integer.MIN_VALUE) {
//if move is valid knight moves
chessboard2[nextRow][nextCol] = moveNum + 1;
//make next move
if(move(moveNum + 1, nextRow, nextCol, chessboard2))
return true;
//move(moveNum + 1, nextRow, nextCol);
//if cant find next move: backtrack
chessboard2[nextRow][nextCol] = Integer.MIN_VALUE;
}
}
return false;
}
/**
* calls method moveLegal from class Chessboard to see if move is legal
* #return true if move is legal, else return false
*/
public boolean canMove(int x, int y) {
//if statement to check if currentRow and currentCol is whithin
//boundaries
return(x >= 0 && x < boardSize && y >= 0 && y < boardSize);
}
public void print() {
for (int i = 0; i < boardSize; i++)
System.out.println(String.join(" ", chessboard2[i]));
}
public void solve() {
//setting array location [0][0] to 0
chessboard2[0][0] = 1;
//check move
if (move(1, 0, 0)) // if true, it will print chess board
print();
else //if false, there is no solution
System.out.print("no solution");
}
}
public class TesterMain {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Knight test = new Knight();
test.solve();
}
}
Sorry if my code is a bit messy, I am still working on the program.
There is a solution but it would require some refactoring:
Create a ChessMove class that stores a row and a column move (integers)
Add a ChessMove[] to store all possible moves that your knight can possibly do
Refactor the move method:
Create an ArrayList<ChessMove> that stores all possible moves that your knight can do in its current position
Randomly select a move in this list using rand.nextInt(possibleMoves.size());
Here is the complete code:
package assignment3;
import java.lang.*;
import java.util.*;
public class Knight {
private int boardSize = 8;
private int[][] chessboard2 = new int[boardSize][boardSize];
private final ChessMove[] moves = {
new ChessMove(-1, 2),
new ChessMove(-2, 1),
new ChessMove(-2, -1),
new ChessMove(-1, -2),
new ChessMove(1, -2),
new ChessMove(2, -1),
new ChessMove(2, 1),
new ChessMove(1, 2)
};
public Knight() {
initializeBoard();
}
public void initializeBoard() {
for (int i = 0; i < boardSize; i++)
Arrays.fill(chessboard2[i], Integer.MIN_VALUE); //setting array to negative value
}
public boolean move(int moveNum, int x, int y) {
//if moveNum == 64 all squares have been visited
if (moveNum == 64) {
System.out.println("\ntrue board is 64\n");
return true;
}
ArrayList<ChessMove> possibleMoves = new ArrayList<ChessMove>();
for (ChessMove move : moves) {
int nextRow = x + move.row;
int nextCol = y + move.col;
//check if postion is valid and not visited yet
if (canMove(nextRow, nextCol) && chessboard2[nextRow][nextCol] == Integer.MIN_VALUE)
possibleMoves.add(move);
}
if (!possibleMoves.isEmpty()) {
Random rand = new Random();
// Move choice is done here
ChessMove chosenMove = possibleMoves.get(rand.nextInt(possibleMoves.size()));
int nextRow = x + chosenMove.row;
int nextCol = y + chosenMove.col;
//if move is valid knight moves
chessboard2[nextRow][nextCol] = moveNum + 1;
//make next move
move(moveNum + 1, nextRow, nextCol);
return true;
} else
return false;
}
public boolean canMove(int x, int y) {
return (x >= 0 && x < boardSize && y >= 0 && y < boardSize);
}
public void print() {
for (int i = 0; i < boardSize; i++) {
for (int cell : chessboard2[i])
if (cell == Integer.MIN_VALUE)
System.out.print("*** ");
else
System.out.print(String.format("%3d", cell) + " ");
System.out.println();
}
}
public void solve() {
chessboard2[0][0] = 1;
if (move(1, 0, 0)) // if true, it will print chess board
print();
else //if false, there is no solution
System.out.print("no solution");
}
class ChessMove {
int row = 0, col = 0;
ChessMove(int r, int c) {
this.row = r;
this.col = c;
}
}
}
public class TesterMain {
public static void main(String[] args) {
Knight test = new Knight();
test.solve();
}
}
The easiest way to randomise your move is to create a list of valid moves for a given position of the knight and then select one at random. List and Random APIs go hand in hand:
//List<Integer> moves = ...
int move = moves.get(new Random().nextInt(moves.size()));
Restructuring your move method to something like this should do the job:
public boolean move(int moveNum, int x, int y, int [][] chessboard2) {
// 1. List all valid moves
List<Integer> validMoves = new ArrayList<Integer>();
//for loop to try 8 possibe moves
for(int i = 0; i < rowMoves.length; i++) {
if (
canMove(x + rowMoves[i], y + colMoves[i])
&& chessboard2[x + rowMoves[i]][y + colMoves[i]] == Integer.MIN_VALUE
) {
validMoves.add(i);
}
}
// 2. Try to make the move if any available
if (validMoves.isEmpty()) {
return false;
}
Random rand = new Random();
int move = validMoves.get(rand.nextInt(validMoves.size()));
int nextRow = x + rowMoves[move];
int nextCol = y + colMoves[move]:
chessboard2[nextRow][nextCol] = moveNumb + 1;
return move(moveNum + 1, nextRow, nextCol, chessboard2);
}
You can use an enum, let's call it Move, to represent every single move, and then make a list of these moves using Move.values().
Then you can shuffle the list with Collections.shuffle every time you want to move and take the first legal move.
I am attempting to make a random maze generator using Java and the recursive backtracking algorithm. I am getting stack overflow when I try to run this code. I know some about stack, I don't think this is infinite recursion. My guess is that I have a big logic error. Do I have to allocate more memory?
The stack trace:
Exception in thread "main" java.lang.StackOverflowError
at java.base/java.util.Vector.elementAt(Vector.java:499)
at java.base/java.util.Stack.peek(Stack.java:103)
at java.base/java.util.Stack.pop(Stack.java:84)
at mazeMaker.Maze.generateMaze(Maze.java:115)
at mazeMaker.Maze.generateMaze(Maze.java:115)
...
at mazeMaker.Maze.generateMaze(Maze.java:115)
at mazeMaker.Maze.generateMaze(Maze.java:115)
Main.java
package mazeMaker;
public class Main
{
public static void main(String[] args)
{
Maze mainMaze = new Maze(20, 30);
}
}
Maze.java
package mazeMaker;
import java.util.Random;
import java.util.Stack;
public class Maze
{
public int xSize = 0;
public int ySize = 0;
public int totalDimensions = 0;
Random randomGenerator = new Random();
public Cell[][] cellData;
public Stack<Cell> cellStack = new Stack<Cell>();
Cell tempCell; // Temporary variable used for maze generation
public Maze(int xSize, int ySize)
{
cellData = new Cell[xSize][ySize];
this.xSize = xSize;
this.ySize = ySize;
this.totalDimensions = this.xSize * this.ySize;
// Initialize array objects
for (int i = 0; i < this.xSize; i++)
{
for (int j = 0; j < this.ySize; j++)
{
cellData[i][j] = new Cell();
}
}
// Assign x and y positions
for (int i = 0; i < this.xSize; i++)
{
for (int j = 0; j < this.ySize; j++)
{
cellData[i][j].xPos = i;
cellData[i][j].yPos = j;
}
}
initBoundries();
generateMaze();
}
private void initBoundries()
{
// Initialize the border cells as visited so we don't go out of bounds
int m = this.xSize;
int n = this.ySize;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (i == 0 || j == 0 || i == n - 1 || j == n - 1)
cellData[i][j].hasBeenVisited = true;
}
}
}
private void generateMaze(int x, int y)
{
// Set current cell as visited
cellData[x][y].hasBeenVisited = true;
// While there are unvisited neighbors
while (!cellData[x][y+1].hasBeenVisited || !cellData[x+1][y].hasBeenVisited || !cellData[x][y-1].hasBeenVisited || !cellData[x-1][y].hasBeenVisited)
{
// Select a random neighbor
while (true)
{
int r = randomGenerator.nextInt(4);
if (r == 0 && !cellData[x][y+1].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasNorthWall = false;
cellData[x][y+1].hasSouthWall = false;
generateMaze(x, y + 1);
break;
}
else if (r == 1 && !cellData[x+1][y].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasEastWall = false;
cellData[x+1][y].hasWestWall = false;
generateMaze(x+1, y);
break;
}
else if (r == 2 && !cellData[x][y-1].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasSouthWall = false;
cellData[x][y-1].hasNorthWall = false;
generateMaze(x, y-1);
break;
}
else if (r == 3 && !cellData[x-1][y].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasWestWall = false;
cellData[x-1][y].hasEastWall = false;
generateMaze(x-1, y);
break;
}
}
}
// There are no unvisited neighbors
tempCell = cellStack.pop();
generateMaze(tempCell.xPos, tempCell.yPos);
}
// Begin generating maze at top left corner
private void generateMaze()
{
generateMaze(1,1);
}
}
Cell.java
package mazeMaker;
public class Cell
{
public boolean isCurrentCell;
public boolean hasBeenVisited;
public boolean hasNorthWall;
public boolean hasSouthWall;
public boolean hasEastWall;
public boolean hasWestWall;
public int xPos;
public int yPos;
}
The method generateMaze can never terminate not even by chance for some simple reason:
For terminating the generateMaze method would need to finish it's execution - it has to return.
There are no return statements in this method, therefore it has to pass the while loops and then continue until the execution reaches and finishes the last statement of the method.
However the last statement is generateMaze(tempCell.xPos, tempCell.yPos); which starts a new recursion, therefore your code can never ever terminate!
I tried to run your project on my own environment but unfortunately, I was not able to reproduce your issue.
However, I was facing an IndexOutOfBound exception in the method generateMaze. While I was solving this, I figured out that there was an issue in the initBoudaries method.
Indeed, when you set the boolean hasBeenVisited to true, you do not use the right variable in the IF clause. Here is the version I tried instead :
private void initBoundries()
{
// Initialize the border cells as visited so we don't go out of bounds
for (int i = 0; i < this.xSize; i++)
{
for (int j = 0; j < ySize; j++)
{
if (i == 0 || j == 0 || i == xSize - 1 || j == ySize - 1)
cellData[i][j].hasBeenVisited = true;
}
}
}
Now about the emptyStackException, I think that if this stack is empty, this means that there is no more cell to handle (as you mentioned in your comment) and the program must end. If I am right, just make sure to test if your stack is empty before call the method pop() on it like this :
// There are no unvisited neighbors
if (!cellStack.isEmpty()) {
tempCell = cellStack.pop();
generateMaze(tempCell.xPos, tempCell.yPos);
}
Hope it will help.
I wrote a simple Tic Tac Toe console app in Java, using a bitboard approach (just for fun). It works well for two human players. My objective was to figure out the minimax algorith and implement a computer player. I did this before, for the (very naive) game of "Nim", and the same general object oriented approach worked. I wanted to use the same structure. But in this case, when the computer goes to make a move, it defaces the whole board variable when it searches for its next move. It shouldn't do so, because the makeMove method creates a brand new Board object. My question is, why does this strange thing happen? Here is the code, loosely commented, straight from NetBeans:
Thanks in advance for anybody who has the patience to take a look. I want to mention that I looked into the Cloneable interface and the clone() method, but to no avail. Then I figured that that shouldn't be the cause, because the way the makeMove method works. So why does the computer player destroy the board?
package tictactoe;
import java.util.*;
public class TicTacToe {
public static void main(String[] args) {
Game game = new Game();
game.start();
}
}
class Game {
ArrayList<Player> players = new ArrayList(); // An ArrayList for the players
public Game() { // Determine if players are to be human or CPU
Scanner input = new Scanner(System.in);
String answer;
System.out.printf("Would you like Player 1 to be CPU? [Yes/No] ");
answer = input.nextLine();
if(answer.toLowerCase().startsWith("y")) players.add(new ComputerPlayer(0, 3));
else players.add(new Player());
System.out.printf("Would you like Player 2 to be CPU? [Yes/No] ");
answer = input.nextLine();
if(answer.toLowerCase().startsWith("y")) players.add(new ComputerPlayer(1, 3));
else players.add(new Player());
}
public void start() {
Scanner input = new Scanner(System.in);
while(true) {
clearScreen();
Board board = new Board();
while(!board.isGameOver()) {
board = board.makeMove(players.get(board.getCurrentPlayer()).getMove(board));
}
board.display();
int winner = board.checkWinner();
if(winner >= 0) {
players.get(winner).addWin();
System.out.printf("Player %d wins. He has %d wins vs %d.\nRematch? [Yes/No] ", winner + 1, players.get(winner).getWins(), players.get(winner == 0 ? 1 : 0).getWins());
}
else {
System.out.printf("The game is a tie.\nRematch? [Yes/No] ");
}
String answer = input.nextLine();
if(answer.toLowerCase().startsWith("n")) break;
else {
Player temp = players.remove(0);
players.add(temp);
for(int i = 0; i < 2; i++) { // just to help the computer player track his own ID
players.get(i).flipID();
}
}
}
System.out.printf("Game aborted. Thank you for playing.");
}
public static void clearScreen() {
for(int i = 0; i < 30; i++) System.out.printf("\n");
}
}
class Board implements Cloneable {
private int[] board; // A two dimensional array for storing player X's and
// player O's moves separately. OR them together to get
// all moves made.
private final int[] map = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256}; // A simple
// way of mapping the digits 1 -> 9 (like on the numpad) to
// the bits of the board. You just do bitwise operations
// against map[n] - n being the digit.
// The numpad-like mapping looks like this:
// 7 8 9 // In memory the bits are stored thus:
// 4 5 6 // 987654321
// 1 2 3 //
private final int[] win = {7, 56, 73, 84, 146, 273, 292, 448}; // A mapping
// of all possible winning combinations translated to decimal
// numbers. Listed in order: 1,2,3; 4,5,6; 1,4,7; 3,5,7;
// 2,5,8; 1,5,9; 3,6,9; 7,8,9.
private int currentPlayer; // The player whose turn it is. 0 for X, 1 for O.
private int opponent; // The opponent. Will always be the opposite.
// The normal constructor. Takes as arguments the current state of the
// board, represented by a two dimensional integer, and the player whose
// turn it currently is, represtented by a 0 or 1
public Board(int[] theBoard, int player) {
board = theBoard;
currentPlayer = player;
opponent = player == 0 ? 1 : 0;
}
// If passed no arguments, construct the bord with default values,
// e.g. an empty board for both players and X's turn.
public Board() {
this(new int[2], 0);
}
// The usual suspects. Accesors for the attributes.
public int[] getBoard() {
return board;
}
public int getCurrentPlayer() {
return currentPlayer;
}
public int getOpponent() {
return opponent;
}
// First check against the win maps, for both players, to see if any of them
// got 3 symbols in a row. If not, check if the board is full.
public boolean isGameOver() {
for(int player = 0; player < 2; player++) {
for(int n: win) {
if((board[player] & n) == n) return true;
}
}
return (board[0] | board[1]) == 511;
}
// Returns -1 if nobody won, or returns 0 or 1 in case either of the
// players did.
public int checkWinner() {
for(int i = 0; i < 2; i++) {
for(int m: win) {
if((board[i] & m) == m) return i;
}
}
return -1;
}
// Find the possible moves on the board, returned in an array
public int[] getMoves() {
// Count the number of possible moves, prerequisite for initializing
// the array of moves that will later be returned.
int allMoves = (board[0] | board[1]);
int count = countBits(allMoves);
// Populate the array of possible moves and then return it
int[] moves = new int[9 - count];
int j = 0;
for(int i = 1; i < 10; i++) {
if((allMoves & map[i]) == 0) {
moves[j] = i;
j++;
}
}
return moves;
}
// Return the number of activated bits in an integer
// (in this case an 8 bit integer)
public static int countBits(int board) {
int count = 0;
for(int i = 1; i <= 256; i <<= 1) {
if((board & i) != 0) count++;
}
return count;
}
// The static evaluation function, used by the minmax algorithm.
// Returns 3 / -3 for victory, or the number of symbols the player
// has on any given line, if there's no opponent's symbol on it.
// Returns 0 otherwise
public int evaluate(int player) {
int allMoves = board[0] | board[1];
int ret = 0, max = 0, min = 0;
for(int p = 0; p < 2; p++) {
for(int w: win) {
int line = board[p] & w;
if(line == w) { // If victory condition found, return immediately
if(p == player) return 3;
else return -3;
}
if((line ^ allMoves) == 0) { // No moves on the line by the opp.
if(p == player) max = countBits(line) > max ? countBits(line) : max;
else min = -countBits(line) < min ? -countBits(line) : min;
}
}
}
if(Math.abs(min) != max) {
ret = Math.abs(min) > max ? min : max;
}
return ret;
}
// Now for the tricky part... this method returns a completely new
// board object. But when the minimax method calls it, it sure doesn't
// behave that way
public Board makeMove(int move) {
int[] newBoard = board;
newBoard[currentPlayer] |= map[move];
return new Board(newBoard, opponent);
}
// Tried to use something like this, at one point, but then I realized
// that it won't help me understand my problem. May use at a later time, tho
/*
public Board undoMove(int move) {
int[] newBoard = board;
newBoard[opponent] ^= map[move];
return new Board(newBoard, opponent);
}
*/
// The method to (very plainly) display the board
public void display() {
for(int i = 6; i >= 0; i -= 3) {
for(int j = 1; j <= 3; j++) {
if(((board[0] | board[1]) & map[i + j]) == 0) System.out.printf("%d", i + j);
else if((board[0] & map[i + j]) != 0) System.out.printf("X");
else System.out.printf("O");
}
System.out.printf("\n");
}
}
// Returns true/false whether a move is valid on the board
public boolean isValidMove(int move) {
if(move < 1 || move > 9) return false;
return ((board[0] | board[1]) & map[move]) == 0;
}
}
class Player {
int wins = 0; // Simple way of keeping track of the number of wins.
// Accessor for the win atr.
public int getWins() {
return wins;
}
// Add a win
public void addWin() {
wins++;
}
public void flipID() {
// To be overridden by the ComputerPlayer class
}
// Query the user for a valid move
public int getMove(Board board) {
Scanner input = new Scanner(System.in);
int move;
board.display();
do {
System.out.printf("Input a valid move: ");
move = input.nextInt();
} while(!board.isValidMove(move));
//Game.clearScreen();
return move;
}
}
class ComputerPlayer extends Player {
int self; // Keep track of his own place in the players array
int maxSearchDepth; // Seach depth setting for the minimax algorithm
public ComputerPlayer(int n, int m) { // Constructor
self = n;
maxSearchDepth = m;
}
#Override
public void flipID() {
self = self == 0 ? 1 : 0;
}
// The implementation of the minimax algorithm
#Override
public int getMove(Board board) {
int[] temp = minimax(board, 0, maxSearchDepth);
return temp[1];
}
public int[] minimax(Board mmBoard, int depth, int maxDepth) {
int[] ret = new int[2]; //ret[0] = bestScore, ret[1] = bestMove
int currentScore, bestScore, bestMove;
if(mmBoard.isGameOver() || depth == maxDepth) {
ret[0] = mmBoard.evaluate(mmBoard.getCurrentPlayer());
ret[1] = 0;
return ret;
}
bestMove = 0;
bestScore = mmBoard.getCurrentPlayer() == self ? -4 : 4;
for(int move: mmBoard.getMoves()) {
// System.out.printf("Board: %s, Depth: %d. Moves: %s. Trying: %d\n", Arrays.toString(mmBoard.getBoard()), depth, Arrays.toString(mmBoard.getMoves()), move);
Board newBoard = mmBoard.makeMove(move); // The problem call...
// System.out.printf("Original: %s New: %s", mmBoard, newBoard);
int[] temp = minimax(newBoard, depth + 1, maxDepth);
currentScore = temp[0];
if(mmBoard.getCurrentPlayer() == self) {
if(currentScore > bestScore) {
bestScore = currentScore;
bestMove = move;
}
}
else {
if(currentScore < bestScore) {
bestScore = currentScore;
bestMove = move;
}
}
}
ret[0] = bestScore;
ret[1] = bestMove;
return ret;
}
}
Note, I did not read through all the code, as there is no minimal example, but I saw an issue here:
public Board makeMove(int move) {
int[] newBoard = board;
// ^^^^^
newBoard[currentPlayer] |= map[move];
return new Board(newBoard, opponent);
}
You are in fact not making a new board here, the new Board(...) has a reference to the old board's int[].
by calling the statement int[] newBoard = board; you are assigning the reference of board to the new integer array and not actually making a copy, in other words: both board objects are now pointing to the same int[]
To make an actual copy, you will need to clone the array by using System.arraycopy();
So the new method would look like this:
public Board makeMove(int move) {
int[] newBoard = new int[board.length];
System.arraycopy(board, 0, newBoard, 0, board.length);
newBoard[currentPlayer] |= map[move];
return new Board(newBoard, opponent);
}
note that I have not read through all your code, but the assumption you made in that method is not correct
Try adding this to your makeMove() method:
int[] newBoard = Arrays.copyOf(board, board.length);
In your code you just point newBoard reference to an existing integer array also referenced by board.
The line above creates new integer array and copies the content of the array referneced by board across.
HTH
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 trying to write a program that can generate all the possible magic squares for a fixed N Dimension. I'm going about it by filling the diagonal cells with values and then filling in the rows with values.
I seem to be stuck in an infinite cycle when fillin in the rows, but can't seem to figure out how or why. I haven't implemented the sum check, to check whether the sum of the rows or columns is correct, but that is irrelevent here.
If anyone can help me out, i'd very greatful.
code bellow
public class Magic {
public static final int DIMENSION = 3;
public static final int DIMSQ = DIMENSION * DIMENSION;
public static int[][] array = new int[DIMENSION][DIMENSION];
public static boolean[] boolArray = new boolean[DIMENSION * DIMENSION];
public static final int sum = (DIMENSION * (DIMENSION * DIMENSION + 1)) / 2;
/*
* Inicializaljuk a matrixunkat, illetve a boolean matrixunkat
* Initializes the matrix and boolArray with values.
*/
public static void init() {
for (int e[] : array) {
for (int e2 : e) {
e2 = 0;
}
}
for (boolean e : boolArray) {
e = false;
}
}
/*
* Ki irassa a matrix jelenlegi allapotat konzolra
* Prints the array out to the console.
*/
public static void print() {
for (int i[] : array) {
for (int j : i) {
System.out.print(j + ",");
}
System.out.println();
}
System.out.println();
}
/*
* feltolti a foatlot adatokkal, majd meghivja a diagonal2-t
* fills diagonal cells with values
*/
public static void diagonal1(int x) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
boolArray[i] = true;
array[x][x] = i + 1;
if (x < DIMENSION - 1) {
diagonal1(x + 1);
} else
diagonal2(0);
boolArray[i] = false;
}
}
}
/*
* feltolti a mellekatlot adatokkal, majd meghivja a row(0,0,0)-t
* fills diagonal cells with values
*/
public static void diagonal2(int x) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[DIMENSION - 1 - x][x] == 0) {
boolArray[i] = true;
array[DIMENSION - 1 - x][x] = i + 1;
}
if (x < DIMENSION - 1) {
diagonal2(x + 1);
} else
row(0, 0);
boolArray[i] = false;
}
}
}
/*
* feltolti a sorokat adatokkal
* fills rows with values
*/
public static void row(int x, int y) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[x][y] == 0) {
boolArray[i] = true;
array[x][y] = i;
}
if (x < DIMENSION - 1) {
row(x + 1, y);
} else if(y < DIMENSION - 1) {
row(0,y+1);
} else print();
boolArray[i] = false;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
init();
print();
diagonal1(0);
}
}
My suspect is the row() method (see my comments below):
public static void row(int x, int y) {
for (int i = 0; i < DIMSQ; i++) {
if (!boolArray[i]) {
if (array[x][y] == 0) {
boolArray[i] = true; // <-- this one
array[x][y] = i;
}
if (x < DIMENSION - 1) {
row(x + 1, y);
} else if(y < DIMENSION - 1) {
row(0,y+1);
} else print();
boolArray[i] = false; // <-- would be OVERWRITTEN by this one
}
}
}
I dont think it's infinite, but very long:
9-step loop in diag1,
3-deep recrusion to diag1,
then 9-step loop in diag2
3-deep recursion in diag2,
then 9-step loop in row
~6-deep recursion in row.
Even though not all loops perform complicated operations on each iteration, this can easily add up to hours if you also take into consideration that you print the state of the square at every "resolution" of the square -- printing to console takes time.