I'm creating a simple engine that plays Othello, using minimax with alpha beta cuts.
It's playing well, but sometimes i get a weird index out of bounds exception (near the
endgame, always).
Here' my algorithm
private float minimax(OthelloBoard board, OthelloMove best, float alpha, float beta, int depth)
{
calls++;
float bestResult = -Float.MAX_VALUE;
OthelloMove garbage = new OthelloMove();
int state = board.getState();
int currentPlayer = board.getCurrentPlayer();
if (state == OthelloBoard.STATE_DRAW)
return 0.0f;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.BLACK))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.WHITE))
return Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_BLACK_WINS) && (currentPlayer == OthelloBoard.WHITE))
return -Float.MAX_VALUE;
if ((state == OthelloBoard.STATE_WHITE_WINS) && (currentPlayer == OthelloBoard.BLACK))
return -Float.MAX_VALUE;
if (depth == maxDepth)
return OthelloHeuristics.eval(currentPlayer, board);
ArrayList<OthelloMove> moves = board.getAllMoves(currentPlayer);
for (OthelloMove mv : moves)
{
board.makeMove(mv);
alpha = - minimax(board, garbage, -beta, -alpha, depth + 1);
board.undoMove(mv);
if (beta <= alpha)
return alpha;
if (alpha > bestResult)
{
best.setFlipSquares(mv.getFlipSquares());
best.setIdx(mv.getIdx());
best.setPlayer(mv.getPlayer());
bestResult = alpha;
}
}
return bestResult;
}
Inside makeMove and undoMove i update the game state(black wins, white wins, draw).
I also toggle the players inside these methods. When a player has no moves i make a dummy
move without changing the board, and toggle the players.
There's a lot more of code, but i think the problem happens when the algorithm hits the
game over position. This problem doesn't happen when i set the engine to play random moves, so the problem should be the alpha beta algorithm.
Here is getAllMoves, this call getFlips:
public ArrayList<OthelloMove> getAllMoves(int player)
{
ArrayList<OthelloMove> moves = new ArrayList<OthelloMove>();
for (int i = 10; i < 90; i++)
{
int col = i % 10;
if (col != 0 && col != 9)
{
if (cells[i] == EMPTY)
{
ArrayList<Integer> flips = getFlips(i, player);
if (flips.size() > 0)
{
OthelloMove mv = new OthelloMove();
mv.setFlipSquares(flips);
mv.setIdx(i);
mv.setPlayer(player);
moves.add(mv);
}
}
}
}
return moves;
}
Here is getFlips.
public ArrayList<Integer> getFlips(int idx, int player)
{
int opponent = getOpponent(player);
ArrayList<Integer> flips = new ArrayList<Integer>();
if (cells[idx] != EMPTY)
return flips;
for (Integer dir : DIRECTIONS)
{
int distance = 1;
int tempIdx = idx;
while (cells[tempIdx += dir] == opponent)
distance++;
if ((cells[tempIdx] == player) && (distance > 1))
{
while (distance-- > 1)
{
tempIdx -= dir;
flips.add(tempIdx);
}
}
}
return flips;
}
Here is updateState:
public void updateState()
{
int opponent = getOpponent(currentPlayer);
int playerMoves = getAllMoves(currentPlayer).size();
int opponentMoves = getAllMoves(opponent).size();
if ( ((playerMoves == 0) && (opponentMoves == 0)) || (emptyCells == 0))
{
int blackDiscs = countDiscs(BLACK);
int whiteDiscs = countDiscs(WHITE);
if (blackDiscs > whiteDiscs)
state = STATE_BLACK_WINS;
else if (blackDiscs < whiteDiscs)
state = STATE_WHITE_WINS;
else
state = STATE_DRAW;
}
}
Thanks!
I am not familiar with the game specifically, but I believe it has something to do with the fact hat in the line:
while (cells[tempIdx += dir] == opponent)
You should also check you are not out of bound, otherwise - if there is still an opponent on the end of the board, you will keep increasing dir
Try changing this line to:
while (tempIdx + dir >= 0 && tempIdx + dir < cells.length && cells[tempIdx += dir] == opponent)
As a rule of thumb, usually it is a good practice in array accesses, especially in loops, to guard against going out of bound by checking the length explicitly.
Found the problem, thanks anyway.
The bug was a situation where a player can't move, and must pass the turn.
The tricky is to play a 'ghost move' (i.e. a move that doesn't change the board), and
toggle the players turn, so that the Minimax doesn't even notice this situation.
I was doing this, but in the wrong place! The code is like:
public void makeMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = player;
for (Integer flip : flips)
cells[flip] = player;
emptyCells--;
this.updatePhase();
}
this.toogleCurrentPlayer();
}
public void undoMove (OthelloMove move)
{
int player = move.getPlayer();
ArrayList<Integer> flips = move.getFlipSquares();
int opponent = getOpponent(player);
if (flips != null)
{
int idx = move.getIdx();
cells[idx] = EMPTY;
for (Integer flip : flips)
cells[flip] = opponent;
emptyCells++;
this.updatePhase();
}
this.toogleCurrentPlayer();
}
Related
I'm developing a minimax alogorithm for use in a modified checkers game. In my evaluation function every score is multiplied by 10 and then a random number between 1 and 10 is added/subtracted from it (max or min node depending). However, when running the program it always executes the same sequence of moves. I've checked the evaluation function and it definitely returns randomised values for nodes that would be of equal value so I can only assume the problem lies in the minimax function itself, any ideas? The other functions, generateMoves and simulateMove also work correctly.
private int minimax(State state, int depth, int min, int max) {
ArrayList<Move> moves = generateMoves(state.board, state.colour);
char opponent = (state.colour == DraughtBoard.WHITE) ? DraughtBoard.BLACK : DraughtBoard.WHITE;
if (moves.size() == 1)
nextMove = moves.get(0);
int bestScore;
Move bestMove = new Move();
int score = 0;
if (depth == 0 || moves.size() == 0) {
return evaluateBoard(state);
}
if (colour == DraughtBoard.WHITE) {
bestScore = min;
for (Move move : moves) {
char[][] temp = state.board.clone();
boolean scored = simulateMove(move, temp);
State nextState = new State(temp, opponent, state.whiteScore, state.blackScore);
if (scored) state.whiteScore++;
score = minimax(state, depth-1, bestScore, max);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
if (bestScore > max) return max;
}
nextMove = bestMove;
return bestScore;
} else {
bestScore = max;
for (Move move : moves) {
char[][] temp = state.board.clone();
boolean scored = simulateMove(move, temp);
State nextState = new State(temp, opponent, state.whiteScore, state.blackScore);
if (scored) state.blackScore++;
score = minimax(state, depth-1, min, bestScore);
if (score < bestScore) {
bestScore = score;
bestMove = move;
}
if (bestScore < min) return min;
}
nextMove = bestMove;
return bestScore;
}
}
char[][] temp = state.board.clone(); will only do a swallow copy (except you wrote your own clone() method)
Which means temp has the same references as board therefor you will change board while calling siumlateMove.
This may causes your problem.
deep copy of a 2d array
I am a beginner and I am trying to develop Connect4 game by applying minimax algorithm, I am stuck at the condition that determines whether it's min player turn or max player turn. I've got the feeling it's something reduculs but I've been thinking for two days trying to figure it out.
Any help?
private int evaluatePlayerMove(int depth, int maxDepth, int col, int alpha, int beta) {
boardsAnalyzed++;
int evaluatedMove=0; // For evaluating min player move or max player move
int min = Integer.MAX_VALUE, minScore = 0; // For min player
int max = Integer.MIN_VALUE, maxScore = 0; // For max player
if (col != -1) {
// Check whether it's min player turn or max player turn
// If it's min player turn then evaluate min move:
if(//it's min player turn){
minScore = board.getHeuristicScore(Board.MARK_BLACK, col, depth, maxDepth);
if(board.blackWinFound()) {
blackWinFound = true;
return minScore;
}
if (depth == maxDepth) {
return minScore;
}
for (int c = 0; c < Board.COLUMNS; c++) {
if (board.isColumnAvailable(c)) {
board.mark(c, Board.MARK_RED);
int value = evaluatePlayerMove(depth + 1, maxDepth, c, alpha, beta);
board.unset(c);
if (value < min) {
min = value;
if (depth == 0) {
column = c;
}
}
if (value < beta) {
beta = value;
}
if (alpha >= beta) {
return beta;
}
}
}
if (min == Integer.MAX_VALUE) {
return 0;
}
evaluatedMove = min;
}
// If it's max player turn then evaluate max move:
if(//it's max player turn) {
maxScore = board.getHeuristicScore(Board.MARK_RED, col, depth, maxDepth);
if (board.redWinFound()) {
redWinFound = true;
return maxScore;
}
if (depth == maxDepth) {
return maxScore;
}
for (int c = 0; c < Board.COLUMNS; c++) {
if (board.isColumnAvailable(c)) {
board.mark(c, Board.MARK_BLACK);
int value = evaluatePlayerMove(depth + 1, maxDepth, c, alpha, beta);
board.unset(c);
if (value > max) {
max = value;
if (depth == 0) {
column = c;
}
}
if (value > alpha) {
alpha = value;
}
if (alpha >= beta) {
return alpha;
}
}
}
if (max == Integer.MIN_VALUE) {
return 0;
}
evaluatedMove= max;
}
}
return evaluatedMove;
}
In most real-time AI situations, it is your AI program versus a human player. So, usually, if you are building a min-max tree, the AI program will only be either min or max, and the same will be the root of the tree. For e.g. if you try at the AI program as max, the root of the tree will always remain max and you need only compute moves for max (min moves will be user inputs). For such situation, i would recommend using the depth of the tree as your checking condition.
if(root == max){
for any node n:
if(n.depth%2 == 0){
n is max
}
else{
n is min
}
}
Because depth is usually used in almost all problems, it would be an efficient way.
However, if it is an homework problem and you indeed need to compute moves for both min and max, I would recommend using a instance static boolean variable isMax which should be flipped after every move.
I am trying to write the code to Conway's game of life but it just keeps dying out. I do not know what i should write so instead of making all the calculations as the code going forward it should just store what the code says and then make the changes. And to clarify i do not want my code to make one decision about one cell and the change it and then move on to the next cell but I want it to make a decision move on to the next cell and in the end of my code i want it to change all the cell att once.
This is the button to create the world:
private void btnSVActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
for (rad = 0; rad < 15; rad++){
for (kolumn = 0; kolumn < 15; kolumn++){
int x = 10 + 20 * kolumn;
int y = 10 + 20 * rad;
int diameter = 20;
int liv = (int) (Math.random()+0.5);
Cirkel cirkel = new Cirkel(x, y, diameter, liv);
cirklar.add(cirkel);
}
}
repaint();
}
This is a button to make the next generation:
private void btnNGActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
for (i=0; i<=225; i++){
rad=(i/15+1);
kolumn=(i%15+1);
if (rad==1 && kolumn==1) {
int levandeGrannarH1=cirklar.get(i+1).kollaLevande()
+cirklar.get(i+15).kollaLevande()
+cirklar.get(i+16).kollaLevande();
if ((levandeGrannarH1!=2 && levandeGrannarH1!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarH1==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad==1 && kolumn==15 ) {
int levandeGrannarH2=cirklar.get(i-1).kollaLevande()+cirklar.get(i+14).kollaLevande()+cirklar.get(i+15).kollaLevande();
if ((levandeGrannarH2!=2 && levandeGrannarH2!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarH2==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad==15 && kolumn==1 ) {
int levandeGrannarH3=cirklar.get(i+1).kollaLevande()+cirklar.get(i-14).kollaLevande()+cirklar.get(i-15).kollaLevande();
if ((levandeGrannarH3!=2 && levandeGrannarH3!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarH3==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad==15 && kolumn==15 ) {
int levandeGrannarH4=cirklar.get(i-1).kollaLevande()+cirklar.get(i-15).kollaLevande()+cirklar.get(i-16).kollaLevande();
if ((levandeGrannarH4!=2 && levandeGrannarH4!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarH4==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad==1 && kolumn>1 && kolumn<15) {
int levandeGrannarR1=cirklar.get(i-1).kollaLevande()+cirklar.get(i+1).kollaLevande()+cirklar.get(i+14).kollaLevande()+cirklar.get(i+15).kollaLevande()+cirklar.get(i+16).kollaLevande();
if ((levandeGrannarR1!=2 && levandeGrannarR1!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarR1==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad>1 && kolumn==15 && rad<15) {
int levandeGrannarR2=cirklar.get(i-15).kollaLevande()+cirklar.get(i-16).kollaLevande()+cirklar.get(i-1).kollaLevande()+cirklar.get(i+14).kollaLevande()+cirklar.get(i+15).kollaLevande();
if ((levandeGrannarR2!=2 && levandeGrannarR2!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarR2==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad==15 && kolumn>1 && kolumn<15) {
int levandeGrannarR3=cirklar.get(i-1).kollaLevande()+cirklar.get(i+1).kollaLevande()+cirklar.get(i-14).kollaLevande()+cirklar.get(i-15).kollaLevande()+cirklar.get(i-16).kollaLevande();
if ((levandeGrannarR3!=2 && levandeGrannarR3!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarR3==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad>1 && kolumn==1 && rad<15 ) {
int levandeGrannarR4=cirklar.get(i-15).kollaLevande()+cirklar.get(i-14).kollaLevande()+cirklar.get(i+1).kollaLevande()+cirklar.get(i+15).kollaLevande()+cirklar.get(i+16).kollaLevande();
if ((levandeGrannarR4!=2 && levandeGrannarR4!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannarR4==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
if (rad>1 && kolumn>1 && rad<15 && kolumn<15) {
int levandeGrannar = cirklar.get(i-16).kollaLevande()+cirklar.get(i-15).kollaLevande()+cirklar.get(i-14).kollaLevande()+cirklar.get(i-1).kollaLevande()+cirklar.get(i+1).kollaLevande()+cirklar.get(i+14).kollaLevande()+cirklar.get(i+15).kollaLevande()+cirklar.get(i+16).kollaLevande();
if ((levandeGrannar!=2 && levandeGrannar!=4) && cirklar.get(i).kollaLevande()==1)
cirklar.get(i).gorDod();
else if ((levandeGrannar==3) && cirklar.get(i).kollaLevande()==0 )
cirklar.get(i).gorLevande();
}
repaint();
}
}
This is the explanation of how the cells should look like and if they are alive or not.
public class Cirkel {
private int x = 0;
private int y = 0;
private int diameter = 10;
private int liv = 1;
public Cirkel(int x, int y, int diameter, int liv){
this.x = x;
this.y = y;
this.diameter = diameter;
this.liv = liv;
}
public void rita(Graphics g){
if (this.liv==1)
g.setColor(Color.green);
else
g.setColor(Color.white);
g.fillOval(this.x, this.y, this.diameter, this.diameter);
}
public int kollaLevande(){
return this.liv;
}
public void gorLevande(){
this.liv=1;
}
public void gorDod(){
this.liv=0;
}
}
I would be grateful for any kind of help and if there is some problem whit my question please just ask and I will try to explain in an other way.
When you calculate your next generation, you appear to be trying to update the state of your current grid cells, in other words, you're setting the state of the cells in your cirklar ArrayList, while you're still using cirklar to calculate the state of the next generation of cells. But doing this before the entire grid has been checked will effect calculations of the grid cell neighbors down-stream. And in fact you already know that this is a problem, since you state:
And to clarify i do not want my code to make one decision about one cell and the change it and then move on to the next cell but I want it to make a decision move on to the next cell and in the end of my code i want it to change all the cell att once.
To fix this, you need to create a new grid, and then when doing your calculations, set the state of the new grid cells as dead or alive. Then when you've completed your iteration through the entire grid, swap the new grid in for the old grid.
Also note that you have a lot of unneeded redundancy in your code, and I think that you're doing this in order to handle the edges of the grid. This can be cleaned up greatly and greatly simplified by thinking through your coding a bit. Do something like:
// pseudo-code:
SomeArray newGrid = new SomeArray....
for (int currentRow = 0; currentRow <= NUMBER_OF_ROWS; currentRow++) {
for (int currentCol = 0; currentCol <= NUMBER_OF_COLS; currentCol++) {
int minumumRow = Math.max(currentRow - 1, 0); // *** check for edge!!!
int maximumRow = Math.min(currentRow + 1, NUMBER_OF_ROWS); // *** check for edge!!!
int minumumCol = Math.max(currentCol - 1, 0); // *** check for edge!!!
int maximumCol = Math.MIN(currentCol + 1, NUMBER_OF_COLS); // *** check for edge!!!
int neighborCount = 0;
for (row = minumumRow; row <= maximumRow; row++) {
for (col = minumumCol; col <= maximumCol; col++) {
if (row != currentRow || col != currentCol) {
// check if neighbors alive and increment neighborCount
}
}
}
// use neighbor count to set the state of the grid cell in a **new** grid
// silly pseudo-code:
newGrid.getCell(currentRow, currentCol).setState(....)
}
}
// now you've checked all the grid and set the new grid states.
// Time to swap grids
currentGrid = newGrid;
I'm making Tetris in Java and am working on rotating a piece.
To start, I was just rotating the bar-shaped piece.
I feel the way I'm doing it now is not only buggy, but complete overkill. But I'm not sure how else to go about it.
Firstly, I have a keylistener that sets int[] rotatedCoords equal to calcRotation("right")... If rotate Left, the rotationsCounter+=1; will be decremented instead.
if (keycode == KeyEvent.VK_D) {
int[] rotatedCoords = calcRotation("right");
rotationsCounter+=1;
clearCurrPosition();
rotate(rotatedCoords);
System.out.println(rotationsCounter);
if (rotationsCounter == 4) {
rotationsCounter = 0;
}
System.out.println(rotationsCounter);
}
calcRotation(String right or left) gets the current coordinates of all 4 Tiles in a Piece and sends them to int[] getRotation(String shape, String direction, int[] current X coords, int[] current Y Coords)
public int[] calcRotation(String direction) {
for (int i = 0; i < tile.length; i++) {
currPositionX[i] = tile[i].getX();
currPositionY[i] = tile[i].getY();
System.out.println(currPositionX[i] + ", " + currPositionY[i]);
}
return getRotation("Bar", direction, currPositionX, currPositionY);
}
then getRotation[] sets the new coordinates based on which rotation direction was chosen (right or left), which shape it is, and which rotation counter it's on (0 degrees, 90 degrees, 180 or 270...)
if (direction == "right") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
currXs[0] += 1;
currYs[0] += -1;
currXs[1] += 0;
currYs[1] += 0;
currXs[2] += -1;
currYs[2] += 1;
currXs[3] += -2;
currYs[3] += 2;
rightRotate1 = new int[] {currXs[0], currYs[0], currXs[1], currYs[1], currXs[2], currYs[2], currXs[3], currYs[3]};
}
if (rotationsCounter == 1) {
... etc
Then the coordinates (pieceRotations) would be set appropriately:
//handle on left rotations
if (direction == "right") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
pieceRotations = rightRotate1;
}
if (rotationsCounter == 1) {
pieceRotations = rightRotate2;
}
if (rotationsCounter == 2) {
pieceRotations = rightRotate3;
}
if (rotationsCounter == 3) {
pieceRotations = rightRotate0;
}
}
}
if (direction == "left") {
if (shape == "Bar") {
if (rotationsCounter == 0) {
pieceRotations = rightRotate3;
}
if (rotationsCounter == 1) {
pieceRotations = rightRotate0;
}
if (rotationsCounter == 2) {
pieceRotations = rightRotate1;
}
if (rotationsCounter == 3) {
pieceRotations = rightRotate2;
}
}
}
return pieceRotations;
}
Then finally, rotate(rotatedCoords) would be called with the correct coordinates to rotate all the tiles too...
public void rotate(int[] rotatedCoordinates) {
int counterX = 0, counterY = 1;
if (movePieceValid()) {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(rotatedCoordinates[counterX], rotatedCoordinates[counterY]);
counterX+=2;
counterY+=2;
}
} else {
for (int i = 0; i < tile.length; i++) {
tile[i].setLocation(currPositionX[i], currPositionY[i]);
}
}
}
So, my current way of calculating the new coordinates based on current position of each shape for left or right is clearly overkill. Is there a general guide I can follow to greatly simplify that? I can't think of another way to get the positions for each shape?
There are only so many pieces. Try having this:
public abstract class Piece {
public abstract void rotate(Direction dir);
}
public class BarPiece extends Piece {
public void rotate(Direction dir) {
// implement
}
}
public class TPiece extends Piece {
// ...
}
public class LeftSPiece extends Piece {
// ...
}
It seems a bit dirty to special but doing math all the time will be slow, and since there are only so many possible pieces...
I have a question that I have been trying to figure out, but I am stuck. Basically I have been trying to implement the logic of a rook's movement in a game that's not chess, but I'm stuck on it. I'll give you the details:
The Board is a 5x5 multidimensional array where there are only pawns and rooks for each player
The goal is to capture all your opponent's pieces and the one that captures them all will win the game.
Rooks can move as far as they want in one direction, until they hit something blocking their path.
The thing my rook does right now is that it can go one direction but it can go anywhere in that line. I need help on trying to figure out how to add more logic to make sure it can only go as long as the path is clear. Here is an example:
The small "p" and "r" are player 2's pieces and the big "P" and "R" are player one's pieces. Right now the top-right R (rook) can only move right, but if you do that it will go beyond the pawns, and then can go as far down as it wants.
* R R R *
* P P P *
* * * * *
* p p p *
* r r r *
Here is the code for what I have for the rook:
public boolean isLegalMove(Location from,Location to)
{
// Row is XPosition (Up/Down)
// Column is YPosition(Left/Right)
int fromRow = from.getXPosition();
int fromColumn = from.getYPosition();
int toRow = to.getXPosition();
int toColumn = to.getYPosition();
// higher row or column or both
if(((fromColumn >= toColumn) || (fromColumn <= toColumn)) && ((fromRow == toRow))) {
return true;
}
if(((fromRow >= toRow) || (fromRow <= toRow)) && ((fromColumn == toColumn))) {
return true;
}
return false;
}
I guess I'm going to make another method to check the logics if there's any thing in the path, calling it isPathClear()
EDIT:
Here's the rest of the code:
public class Board
{
// The depth and width of the field.
public static final int ROW = 5;
public static final int COLUMN = 5;
public static final String EMPTYPIECE = " * ";
//Storage for the game pieces
private GamePiece [] [] gameBoard;
//Makes the balls and torches for player1
private Pawn1 p1Pawn1,p1Pawn2,p1Pawn3;
private Rook1 p1Rook1,p1Rook2,p1Rook3;
//Makes the ball and torchers for player2
private Pawn2 p2Pawn1,p2Pawn2,p2Pawn3;
private Rook2 p2Rook1,p2Rook2,p1Rook3;
/**
* Makes a 5x5 Gameboard
*/
public Board()
{
// initialise instance variables
gameBoard = new GamePiece [ROW][COLUMN];
//Makes pieces for player1
p1Pawn1 = new Pawn1();
p1Pawn2 = new Pawn1();
p1Pawn3 = new Pawn1();
p1Rook1 = new Rook1();
p1Rook2 = new Rook1();
p1Rook3 = new Rook1();
//Makes pieces for player2
p2Pawn1 = new Pawn2();
p2Pawn2 = new Pawn2();
p2Pawn3 = new Pawn2();
p2Rook1 = new Rook2();
p2Rook2 = new Rook2();
p2Rook3 = new Rook2();
}
/**
* Makes new games
*/
public void newGame()
{
// Assigns the piece of the board for player1
gameBoard[0][1] = p1Rook1;
gameBoard[0][2] = p1Rook2;
gameBoard[0][3] = p1Rook3;
gameBoard[1][1] = p1Pawn1;
gameBoard[1][2] = p1Pawn2;
gameBoard[1][3] = p1Pawn3;
// Assigns the pieces of the board for player2
gameBoard[4][1] = p2Rook1;
gameBoard[4][2] = p2Rook2;
gameBoard[4][3] = p2Rook3;
gameBoard[3][1] = p2Pawn1;
gameBoard[3][2] = p2Pawn2;
gameBoard[3][3] = p2Pawn3;
}
/**
* Displays the content of the board
*/
public void displayBoard()
{
System.out.println(" a b c d e");
int counter = 1;
for (int i = 0; i < gameBoard.length; i++){
System.out.print(counter);
for (int j = 0; j < gameBoard[i].length; j++) {
if (gameBoard[i][j] == null) {
System.out.print(EMPTYPIECE);
} else {
System.out.print(" " + gameBoard[i][j] + " ");
}
}
counter++;
System.out.println();
}
}
/**
* Moves the movepiece from one locatin to another
* #param from - where the location was from
* #param to - Where the location is going to
*/
public void movePiece(Location from,Location to) throws InvalidMoveException
{
int fromRow = from.getXPosition();
int fromColumn = from.getYPosition();
int toRow = to.getXPosition();
int toColumn = to.getYPosition();
if (gameBoard[fromRow][fromColumn] == null) {
throw new InvalidMoveException("Invalid input for source location.");
}
if (! checkBounds(from, to)) {
throw new InvalidMoveException("Invalid input for destination location.");
}
if (isSameLocation(from, to)){
throw new InvalidMoveException("Invalid move, source and destination cannot bethe same.");
}
if (! gameBoard[fromRow][fromColumn].isLegalMove(from, to)) {
throw new InvalidMoveException("Invalid move for this piece.");
}
gameBoard[toRow][toColumn] = gameBoard[fromRow][fromColumn];
gameBoard[fromRow][fromColumn] = null;
displayBoard();
}
/**
* Checks a proposed move to ensure it is within the bounds of the board.
* #param source location, destination location
* #return true if both source and destination are within bounds
*/
private boolean checkBounds(Location from, Location to)
{
int fromRow = from.getXPosition();
int fromColumn = from.getYPosition();
int toRow = to.getXPosition();
int toColumn = to.getYPosition();
boolean testFrom = (fromRow >= 0) && (fromColumn >= 0) && (fromRow < gameBoard.length) && (fromColumn < gameBoard[0].length);
boolean testTo = (toRow >= 0) && (toColumn >= 0) && (toRow < gameBoard.length) && (toColumn < gameBoard[0].length);
return testFrom && testTo;
}
/**
* Checks a proposed move to ensure source and destination are different.
* #param source location, destination location
* #return true if source and destination are the same
*/
private boolean isSameLocation(Location from, Location to)
{
int fromRow = from.getXPosition();
int fromColumn = from.getYPosition();
int toRow = to.getXPosition();
int toColumn = to.getYPosition();
return fromRow == toRow && fromColumn == toColumn;
}
You can't know if the path is clear without knowing what else is on the board. However, your method signature doesn't give this function access to the layout of the board. If you pass the entire board, you can use a loop to check all the squares in between for other pieces.
From Lord Torgamus:
You wouldn't check to see if the board is null. You'd have to check the individual spaces between the rook's source and destination locations.
Now that I know what board looks like, here's some code:
public boolean isLegalMove(Location from,Location to)
{
// Row is XPosition (Up/Down)
// Column is YPosition(Left/Right)
int fromRow = from.getXPosition();
int fromColumn = from.getYPosition();
int toRow = to.getXPosition();
int toColumn = to.getYPosition();
// Has to be same row or column
if(fromRow != toRow || fromColumn != toColumn) return false;
// Can't move to the same square
if(fromRow == toRow && fromColumn == toColumn) return false;
// Rows are the same
if(fromRow - toRow == 0) {
// this will hold the column of the we're going to check next
int newPos = fromColumn;
// Should we go up or down?
int amount = (toColumn - fromColumn < 0) ? -1 : 1;
while(newPos != toColumn) {
newPos += amount;
// if it's not null, we found a different piece
if(gameBoard[fromRow][newPos] != null) return false;
}
if(gameBoard[toRow][toColumn] != null) {
// return false if it's your own piece, true if it's not
}
// Columns are the same
} else {
// this will hold the row of the we're going to check next
int newPos = fromRow;
// Should we go up or down?
int amount = (toRow - fromRow < 0) ? -1 : 1;
while(newPos != toRow) {
newPos += amount;
// if it's not null, we found a different piece
if(gameBoard[newPos][fromColumn] != null) return false;
}
if(gameBoard[toRow][toColumn] != null) {
// return false if it's your own piece, true if it's not
}
}
return true;
}
Edited for the case where you want to be able to capture an opponent's piece... but I didn't put the last bit of code in because you have to change the method signature again. Look for my comment. Notice also it's a while loop now, not a do-while.