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.
Related
I am working on the following problem:
Write a method that, given a chessboard with one knight, rocks on some of the squares, and a target position, indicates whether or not the knight can reach the target without landing on any rocks, and if so, the smallest number of moves needed by the knight to reach the target. The method should return the minimum number of moves needed to do so; otherwise, the method should return the value -1. (If the initial position has a rock on it, the method should return -1; likewise, if the target position has a rock on it, the method should return -1.)
You can see the code I've implemented so far below. My approach to rocks is to change the "coordinates" on the chessboard that have rocks as visited, so the knight can't revisit them, hence blocking his path(?).
My program compiles but doesn't return either minimum moves or -1. Any tips/different approaches to the problem are much appreciated. Thanks!!
PS: I'm ridiculously new to Java so apologies in advance for the messy code :)
import java.util.*;
public class Knight {
public static int numMoves( int dim, int xstart, int ystart, int xtarget,
int ytarget, int[] xrock, int[] yrock )
{
int result = -1;
List<Integer> knightPos = new ArrayList<>(Arrays.asList(xstart, ystart));
int [] targetPos = {xtarget, ytarget};
int dis = 0;
// x and y direction, where a knight can move
int[] dx = { -2, -1, 1, 2, -2, -1, 1, 2 };
int[] dy = { -1, -2, -2, -1, 1, 2, 2, 1 };
// queue for storing states of knight in board
Vector<cell> q = new Vector<>();
// push starting position of knight with 0 distance
q.add(new cell(knightPos.get(xstart), knightPos.get(ystart), dis));
cell t;
int x, y;
boolean[][] visit = new boolean[dim + 1][dim + 1];
// make all cell unvisited
for (int i = 1; i <= dim; i++) {
for (int j = 1; j <= dim; j++) {
visit[i][j] = false;
}
}
// visit starting state
visit[knightPos.set(0,xstart)][knightPos.set(1,ystart)] = true;
// visit rock squares
for (int i = 0; i < xrock.length;) {
for (int j = 0; j < yrock.length; ++i, ++j) {
visit[knightPos.get(i)][knightPos.get(j)] = true;
}
}
// loop until we have one element in queue
while (!q.isEmpty()) {
t = q.firstElement();
q.remove(0);
// if current cell is equal to target cell,
// return its distance
if (t.x == targetPos[0] && t.y == targetPos[1])
return t.dis;
// loop for all reachable states
for (int i = 0; i < 8; i++) {
x = t.x + dx[i];
y = t.y + dy[i];
// If reachable state is not yet visited and
// inside board, push that state into queue
if (isInside(x, y, dim) && !visit[x][y]) {
visit[x][y] = true;
q.add(new cell(x, y,dis + 1));
}
}
}
return result;
}
public static boolean isInside (int x, int y, int dim)
{
return x >= 0 && x <= dim && y >= 0 && y <= dim;
}
static class cell {
int x, y;
int dis;
public cell(int x, int y, int dis) {
this.x = x;
this.y = y;
this.dis = dis;
}
}
public static void main( String[] args )
{
}
}
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
Ok so I've been following this tutorial on Connect4 in java and I've tried to modify it to fit what I already had and to fit into libgdx. After implementing it, i have a few odd problems.
Problem 1: After i make the first move, the computer fills the entire bottom row with his chips and then makes his first move.
Problem 2: The computer isn't displaying any A.I. and simply starts at the first column and first row available and places a chip there. The computer will keep following this pattern.
Problem 3: My winning checker no longer realizes if I have won the game but does realize when the computer has won. When I first designed the game I started by having the computer place chips at random (for testing) and my winning checker worked for the computer and for myself.
The article I followed was here: Connect4 in Java
Here is my code.
ConnectFour.java
package com.comp452.tme31;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class ConnectFour extends ApplicationAdapter implements InputProcessor {
// Create final ints for number of columns and rows in game.
protected final static int COLUMNS = 7;
protected final static int ROWS = 6;
protected final static int TILESIZE = 64;
// Create boolean to determine if player can take a turn.
protected boolean playersTurn = true;
// Create boolean for gameover.
protected boolean gameOver = false;
// Create boolean for winners.
private boolean winner = false;
// Sprite batch for texture drawing.
SpriteBatch batch;
// Create textures to represent board and player pieces.
Texture drawingTile, empty, player, computer;
// Create 2D array to hold game board.
private final static int[][] gameBoard = new int[COLUMNS][ROWS];
public static int[][] getGameBoard() {
return gameBoard;
}
// Create variables to display status message.
BitmapFont mainStatusDisplay;
public static String mainStatusString;
public static String winningString;
// Create and set max depth for tree search
private final int MAX_DEPTH = 4;
// Create win, loss and nothing for zero sum game.
private final float WIN = 1f;
private final float LOSE = -1f;
private final float TIE = 0f;
#Override
public void create () {
batch = new SpriteBatch();
empty = new Texture("empty.jpg");
player = new Texture("player.jpg");
computer = new Texture("computer.jpg");
Gdx.input.setInputProcessor(this);
// Initialize display for status messages.
mainStatusDisplay = new BitmapFont();
mainStatusString = "Player's Turn";
winningString = "";
}
#Override
public void render () {
update();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
drawBoard();
mainStatusDisplay.setColor(Color.YELLOW);
mainStatusDisplay.draw(batch, mainStatusString, 32, 416);
batch.end();
}
public void drawBoard() {
for (int i = 0; i < COLUMNS; i ++) {
for (int j = 0; j < ROWS; j++) {
if (gameBoard[i][j] == 0) {
drawingTile = empty;
}
else if (gameBoard[i][j] == 1) {
drawingTile = player;
}
else if (gameBoard[i][j] == 2) {
drawingTile = computer;
}
batch.draw(drawingTile, i * 64, j * 64);
}
}
}
// Method update handles updates to game logic.
public void update() {
// If it's gameover, end the game.
if (gameOver) {
// Set players turn to true to prevent computer from taking another turn.
playersTurn = true;
// Set status message to winning message;
mainStatusString = winningString;
// Disable input processor to prevent player from taking another turn.
Gdx.input.setInputProcessor(null);
}
// If it's not players turn, call computersTurn.
else if (!playersTurn) {
mainStatusString = "Computer's Turn";
computersTurn();
}
if (checkForWin(1) && checkForWin(2)) {
gameOver = true;
}
}
public void computersTurn() {
double maxScore = 2. * Integer.MIN_VALUE;
int xValue = 0;
// Search the gameboard and find the best move.
for (int x = 0; x < COLUMNS; x++) {
// If x column is a value move...
if (canMove(x)) {
// Set score of move from function.
double score = moveScore(x);
// If score is greater than max score...
if (score > maxScore) {
// Set score to max score and xValue to column.
maxScore = score;
xValue = x;
// If the score is a win, break from loop.
if (score == WIN) {
break;
}
}
}
}
// Set the piece for player at column as x.
setPiece(2, xValue);
// Set players turn and string status.
playersTurn = true;
mainStatusString = "Player's Turn";
}
// Method moveScore determines the value of a move and returns it.
public double moveScore(int xValue) {
// Set the piece in place.
setPiece(2, xValue);
// Get the score and check it's value with alpha beta pruning.
double score = alphaBetaPrune(MAX_DEPTH, Integer.MIN_VALUE, Integer.MAX_VALUE, 1);
// Remove the piece.
takeAwayPiece(xValue);
return score;
}
public double alphaBetaPrune(int depth, double alpha, double beta, int whoPlayed) {
winner = checkForWin(1) || checkForWin(2);
// If we've reached the max depth of the tree or there is a winner...
if (depth == 0 || winner) {
double score;
// If there is a winner...
if (winner) {
// If player is the winner...
if (checkForWin(1)) {
// Set a losing score (Computer does not want player to win).
score = LOSE;
}
// Else this is a win for the computer...
else {
// Set score to a win.
score = WIN;
}
}
// Otherwise there is no winner...
else {
// Set score to TIE (0).
score = TIE;
}
// Return score and remove depth level.
return score / (MAX_DEPTH - depth +1);
}
// If computer is making the move...
if (whoPlayed == 2) {
// Iterate through gameboard.
for (int x = 0; x < COLUMNS; x++) {
// Check and see if next move can be made.
if (canMove(x)) {
// Make move for computer to x.
setPiece(2, x);
// Set alpha equal to return from recursion step minus one depth level.
alpha = Math.max(alpha, alphaBetaPrune(depth - 1, alpha, beta, 1));
// Remove piece.
takeAwayPiece(x);
// Check returned alpha against beta and break from loop if beta is less than alpha.
if (beta <= alpha) {
break;
}
}
}
// We're here if alpha is larger and we didn't break from loop.
return alpha;
}
// Else if player is making move...
else {
// Iterate through gameboard.
for (int x = 0; x < COLUMNS; x++) {
// Check and see if next move can be made.
if (canMove(x)) {
// Make move for player to x.
setPiece(1, x);
// Set beta equal to return from recursion step minus one depth level for beta.
beta = Math.min(beta, alphaBetaPrune(depth - 1, alpha, beta, 2));
// Remove piece.
takeAwayPiece(x);
// Check returned alpha against beta and break from loop if beta is less than alpha.
if (beta <= alpha) {
break;
}
}
}
// We're here if alpha is larger than beta and we didn't break from loop.
return beta;
}
}
// Method setPiece takes two int values as parameters and places a piece on the game board.
public void setPiece(int whoPlayed, int xValue) {
// For loop to iterate through each row.
for (int i = 0; i < ROWS; i++) {
// If row is empty...
if (gameBoard[xValue][i] == 0) {
// Place piece on the board.
gameBoard[xValue][i] = whoPlayed;
break;
}
}
}
// Method takeAwayPiece takes two int values as parameters and removes a piece from the board.
public void takeAwayPiece(int xValue) {
// For loop to iterate through each row.
for (int i = ROWS - 1; i > 0; i--) {
// If row contains a piece..
if (gameBoard[xValue][i] != 0) {
// Remove piece.
gameBoard[xValue][i] = 0;
break;
}
}
}
// Method to determine if a move is valid
public boolean canMove(int xValue) {
// If the top spot in the given column is 0, return true.
return (gameBoard[xValue][ROWS-1] == 0);
}
// Method checkForWin takes a flag and checks to see if that player has won the game.
public boolean checkForWin(int whoPlayed) {
// Create counter to check for 4 in a row.
int win = 0;
// Iterate through gameboard and count pieces in a row.
for (int y = 0; y < ROWS; y++) {
for (int x = 0; x < COLUMNS; x++) {
// If piece is player who is checking, increment counter.
if (gameBoard[x][y] == whoPlayed) {
win++;
}
// Not in a row, set counter to 0.
else {
win = 0;
}
if (win == 4) {
break;
}
}
// If win counter is 4, winner.
if (win == 4) {
winningString = "Horizontal Win for Player " + whoPlayed;
return true;
}
// Else, reset win counter and check next column.
else {
win = 0;
}
}
// Iterate through gameboard and count pieces in a column.
for (int x = 0; x < COLUMNS; x++) {
for (int y = 0; y < ROWS; y++) {
// If piece is player who is checking, increment counter.
if (gameBoard[x][y] == whoPlayed) {
win++;
}
// Not in a row, set counter to 0.
else {
win = 0;
}
if (win == 4) {
break;
}
}
// If win counter is 4, player won.
if (win == 4) {
winningString = "Vertical Win for Player " + whoPlayed;
return true;
}
// Else, reset win counter and check next column.
else {
win = 0;
}
}
// Iterate through gameboard and count pieces in a diagonal row, left to right.
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 2; y++) {
// If piece is player who is checking, check next piece diagonally.
if (gameBoard[x][y] == whoPlayed) {
// Then check next diagonal piece.
if (gameBoard[x+1][y+1] == whoPlayed) {
// Then check next diagonal piece.
if (gameBoard[x+2][y+2] == whoPlayed) {
// Then check last diagonal piece.
if (gameBoard[x+3][y+3] == whoPlayed) {
// Set winning message to player won and set gameover flag.
winningString = "Diagonal Win (LR) for Player " + whoPlayed;
// Exit function.
return true;
}
}
}
}
}
}
// Iterate through gameboard and count pieces in a diagonal row, right to left.
for (int x = 3; x < COLUMNS; x++) {
for (int y = 0; y < 2; y++) {
// If piece is player who is checking, check next piece diagonally.
if (gameBoard[x][y] == whoPlayed) {
// Then check next diagonal piece.
if (gameBoard[x-1][y+1] == whoPlayed) {
// Then check next diagonal piece.
if (gameBoard[x-2][y+2] == whoPlayed) {
// Then check last diagonal piece.
if (gameBoard[x-3][y+3] == whoPlayed) {
// Set winning message to player won and set gameover flag.
winningString = "Diagonal Win (RL) for Player " + whoPlayed;
// Exit function.
return true;
}
}
}
}
}
}
// Iterate through gameboard and if no 0 slots remain, game is a tie.
for (int x = 0; x < COLUMNS; x++) {
for (int y = 0; y < ROWS; y++) {
// If a 0 slot remains, return.
if (gameBoard[x][y] == 0) {
return false;
}
}
}
// If we're here, then there was no winners and no slots left.
winningString = "Tie Game";
return false;
}
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
if (playersTurn) {
if (button == Input.Buttons.LEFT) {
if (canMove(x / TILESIZE)) {
setPiece(1, x / TILESIZE);
playersTurn = false;
return true;
}
}
}
return false;
}
#Override
public boolean touchUp(int i, int i1, int i2, int i3) {
return false;
}
#Override
public boolean touchDragged(int i, int i1, int i2) {
return false;
}
#Override
public boolean mouseMoved(int i, int i1) {
return false;
}
#Override
public boolean scrolled(int i) {
return false;
}
#Override
public boolean keyDown(int i) {
return false;
}
#Override
public boolean keyUp(int i) {
return false;
}
#Override
public boolean keyTyped(char c) {
return false;
}
}
Thanks!
So after the first fix, it was fairly easy to narrow down what the problem was for the other problem. In my takeAwayPiece method, it wasn't removing anything on the bottom row so when the A.I calculated it's next move, its testpieces it was placing at the bottom didn't work.
So in the takeAwayPiece method, i changed it to the following
// Method takeAwayPiece takes two int values as parameters and removes a piece from the board.
public void takeAwayPiece(int xValue) {
// For loop to iterate through each row.
for (int i = ROWS; i > 0; i--) {
System.out.println(i);
// If row contains a piece..
if (gameBoard[xValue][i-1] != 0) {
// Remove piece.
//System.out.println("Piece at column " + xValue + " " + i);
gameBoard[xValue][i-1] = 0;
break;
}
}
}
You might notice that the difference is that i starts = to ROWS instead of ROWS -1 and then i was checking against x-1 instead of x (Previously i just tried changing the condition from i > 0 to i = 0 but that had some crazy strange results).
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.