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.
Related
I'm trying to get the diagonals of a matrix that is at least 5x5; trying to loop through new columns in multiple rows. The example I have is 8x8. I can't get it to trigger the middle section as recorded "hits" in a sequence diagonally. Here is my code. This is regarding button presses that then register a persons choice, based on a player color. It loops through fine on the edges like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int row = 0; row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
I am trying to solve it like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int loop = 0; loop < 5; loop++) {
for (int row = (0 + loop); row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
}
What I came up with is that I am always checking only from top to bottom and
Only to the right
Only to the bottom
Bottom down diagonally
Bottom left diagonally
So in my opinion this covers all possible wining configurations if the search is done field by field from the top.
I use 1D array to represent the gameboard and I always check if any of those checks can be performed or not, and skipping the one that.
Running example tries to randomly fill the board of given dimensions step by step and stops execution when winning condition is met (or it is a tie).
For checking winning conditions I just define the "field index progression" for the next field so I can do the ckeck as simple for loop.
public class TheGame {
static int side = 8;
static int fieldsCount = side * side;
static int[] fields = new int[fieldsCount];
static int toWin = 5;
public static void main(String[] args) {
//generate list of random picks
List<Integer> fieldsToBet = IntStream.range(0, fields.length).boxed().collect(Collectors.toList());
Collections.shuffle(fieldsToBet);
Iterator<Integer> it = fieldsToBet.iterator();
Arrays.fill(fields, ' ');
//place bets using list above
char nextBet = 0;
while (it.hasNext()) {
nextBet = nextBet == 'o' ? 'x' : 'o';
Integer idx = it.next();
fields[idx] = nextBet;
char winner=(char) gameOver(idx); //check the condition, if game is done, return the winner
if (winner > 0) {
System.out.println();
System.out.println("Winner is: " + nextBet);
break;
}
}
printout();
}
private static int gameOver(Integer i) {
if (fields[i] == ' ') {
return 0;
}
int column = i % side; //this can be ommited if using nested for loops
int row = i / side;
boolean canCheckRight = column + toWin <= side;
boolean canCheckDown = row <= side - toWin;
boolean canCheckLeft = column + 1 - toWin >= 0;
boolean canCheckRightDownDiagonal = canCheckDown && canCheckRight;
boolean canCheckLeftDownDiagonal = canCheckDown && canCheckLeft;
boolean itsEnd =
canCheckRight && check(i, toWin, idx -> idx += 1) || //every field to check is just "next field"
canCheckDown && check(i, toWin, idx -> idx += side) || // every field to check is in next row
canCheckRightDownDiagonal && check(i, toWin, idx -> idx += side + 1) || // next field to check is in next row skewed by 1 to the right
canCheckLeftDownDiagonal && check(i, toWin, idx -> idx += side - 1);// next field to check is in next row skewed by 1 to the left
return itsEnd ? fields[i]:0;
}
private static boolean check(int idx, int iterations, Function<Integer, Integer> nextCoordinateProducer) {
int figure = fields[idx];
if (figure == ' ') {
return false;
}
for (int i = 1; i < iterations; i++) {
idx = nextCoordinateProducer.apply(idx);
if (figure != fields[idx]) {
return false;
}
}
return true;
}
public static void printout() {
for (int i = 0; i < fieldsCount; i += side) {
System.out.println(Arrays.stream(fields)
.skip(i)
.limit(side)
.boxed()
.map(v -> Character.valueOf((char) v.intValue()).toString())
.collect(Collectors.joining("|")));
}
}
}
The purpose of this method is to read a grid of 1's and 0's that I created previously, and apply depth first search to find a path of components. In the main method, I scan the grid for a starting component, or "seed," then call labelUsingDFS once a seed is found. 0's are background, 1's are components.
public static boolean labelUsingDFS(Pixel [][] maze, int row, int col, int
componentNumber, ArrayStack stack){
ArrayStack path = new ArrayStack();
//initialize offsets
Position [] offset = new Position[4];
offset[0] = new Position(0,1); //right
offset[1] = new Position(1,0); //down
offset[2] = new Position(0,-1); //left
offset[3] = new Position(-1,0); //up
Position here = new Position(row, col);
maze[row][col].label = 0; //prevent return
int option = 0;
int lastOption = 3;
//search for a path
while(here.row != gridSize || here.col != gridSize) {
int r = 0, c = 0; //row and column of neighbor
while(option <= lastOption) {
r = here.row + offset[option].row;
c = here.col + offset[option].col;
if(maze[r][c].label == 0) break;
option++;
}
//was a neighbor found??
if(option <= lastOption){ //yes
path.push(here);
**here = new Position(r, c);**
maze[r][c].label = 0; //set to 0 to prevent revisit
option = 0;
} else { //no neighbor to move to, back up
if(path.empty()) {
return false;
}
Position next = (Position) path.pop();
if(next.row == here.row) {
option = 2 +next.col - here.col;
} else {
option = 3 + next.row - here.row;
here = next;
}
}
}
}
I am creating a puzzle game hashi, basically the purpose of the game is to solve a puzzle using connection (Bridges) and Nodes(Islands).
To solve the puzzle, the user has to connect the same amount of bridges to a corresponding nodes weight.
The user is allowed to join the islands using 2 bridges from one island, but after s/he tries to connect the 3 bridges from the same node the connection should disappear and the user should be allowed to create a new one from that node.
Problem:
The user can draw 2 bridges from the same node but once he tries to do a third one the connection disappear JUST VISUALLY meaning the nodes are still connected and then the user cannot draw a bridge again.
And in here the bridges are just deleted visually.
I tried to debug it, but unfortunately I cannot seem to figure out where the problem is.
Here is the code to my problem.
public class BoardCreation {
// This class member is used for random initialization purposes.
static private final Random random = new Random();
// The difficulty levels.
private static final int EASY = 0;
static public final int MEDIUM = 1;
static public final int HARD = 2;
static public final int EMPTY = 0;
private static int ConnectionFingerprint(BoardElement start, BoardElement end) {
int x = start.row * 100 + start.col;
int y = end.row * 100 + end.col;
// Swap to get always the same fingerprint independent whether we are called
// start-end or end-start
if (x > y ) {
int temp = x;
x = y;
y = temp;
}
Log.d("", String.format("%d %d" , x ,y));
return x ^ y;
}
public class State {
// The elements of the board are stored in this array.
// A value defined by "EMPTY" means that its not set yet.
public BoardElement [][] board_elements = null;
public int [][] cell_occupied = null;
// The width of the board. We only assume squared boards.
public int board_width=0;
public State(int width) {
board_width = width;
board_elements = new BoardElement[width][width];
cell_occupied = new int[width][width];
}
public State CloneWithoutConnections() {
State newstate = new State(board_width);
if (board_elements != null) {
newstate.board_elements = new BoardElement[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
for (int j = 0; j < board_elements.length; ++j) {
if (board_elements[i][j] == null)
continue;
newstate.board_elements[i][j] = board_elements[i][j].clone();
}
}
}
if (cell_occupied != null) {
assert board_elements != null;
newstate.cell_occupied = new int[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
System.arraycopy(cell_occupied[i], 0, newstate.cell_occupied[i], 0, board_elements.length);
}
}
return newstate;
}
public void AddToBridgeCache(BoardElement first, BoardElement second) {
if (first == null || second == null) { return; }
final int fingerprint = ConnectionFingerprint(first, second);
Log.d(getClass().getName(),
String.format("Fingerprint of this bridge %d", fingerprint));
// mark the end points as occupied.
cell_occupied[first.row][first.col] = fingerprint;
cell_occupied[second.row][second.col] = fingerprint;
int dcol = second.col - first.col;
int drow = second.row - first.row;
if (first.row == second.row) {
for (int i = (int) (first.col + Math.signum(dcol)); i != second.col; i += Math.signum(dcol)) {
cell_occupied[first.row][i] = fingerprint;
String.format("deleting bridge");
}
} else {
assert first.col == second.col;
for (int i = (int) (first.row + Math.signum(drow)); i != second.row; i+= Math.signum(drow)) {
cell_occupied[i][first.col] = fingerprint;
String.format("deleting bridge", fingerprint);
}
}
}
} // end of state
}
I am trying to create a method/ while loop which checks to see which player moves. For now I check to see which play moves in a for loop. I am trying to make my code a bit more clear and concise. Here is the main method:
public static void main(String[] args) {
try (Scanner input = new Scanner(System.in)) {
int height = 6, width = 8, moves = height * width;
ConnectFour board = new ConnectFour(width, height);
System.out.println("Use 0-" + (width - 1) + " to choose a column.");
System.out.println(board);
for (int player = 0; moves-- > 0; player = 1 - player) { // Here is where i check to see who plays
char symbol = players[player];
board.chooseAndDrop(symbol, input);
System.out.println(board);
if (board.isWinningPlay()) {
System.out.println("Player " + symbol + " wins!");
return;
}
}
System.out.println("Game over, no winner.");
}
}
I was thinking more along the lines of:
int playerNb = 0;
while (thegamestarted)
{
if (playerNb == 0)
// Get user input
else
// Get second player input
// Process the input
// Change playerNb to 1 or 0
}
Below is the full code:
import java.util.Arrays;
import java.util.Scanner;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ConnectFour {
private static final char[] players = new char[] { 'X', 'O' };
private final int width, height;
private final char[][] grid;
private int lastCol = -1, lastTop = -1;
public ConnectFour(int width, int height) {
this.width = width;
this.height = height;
this.grid = new char[height][];
for (int h = 0; h < height; h++) {
Arrays.fill(this.grid[h] = new char[width], '.');
}
}
public String toString() {
return IntStream.range(0, this.width)
.mapToObj(Integer::toString)
.collect(Collectors.joining()) + "\n" +
Arrays.stream(this.grid)
.map(String::new)
.collect(Collectors.joining("\n"));
}
/**
* Prompts the user for a column, repeating until a valid
* choice is made.
*/
public void chooseAndDrop(char symbol, Scanner input) {
do {
System.out.print("\nPlayer " + symbol + " turn: ");
int col = input.nextInt();
if (! (0 <= col && col < this.width)) {
System.out.println("Column must be between 0 and " +
(this.width - 1));
continue;
}
for (int h = this.height - 1; h >= 0; h--) {
if (this.grid[h][col] == '.') {
this.grid[this.lastTop=h][this.lastCol=col] = symbol;
return;
}
}
System.out.println("Column " + col + " is full.");
} while (true);
}
/**
* Detects whether the last chip played was a winning move.
*/
public boolean isWinningPlay() {
if (this.lastCol == -1) {
throw new IllegalStateException("No move has been made yet");
}
char sym = this.grid[this.lastTop][this.lastCol];
String streak = String.format("%c%c%c%c", sym, sym, sym, sym);
return contains(this.horizontal(), streak) ||
contains(this.vertical(), streak) ||
contains(this.slashDiagonal(), streak) ||
contains(this.backslashDiagonal(), streak);
}
/**
* The contents of the row containing the last played chip.
*/
private String horizontal() {
return new String(this.grid[this.lastTop]);
}
/**
* The contents of the column containing the last played chip.
*/
private String vertical() {
StringBuilder sb = new StringBuilder(this.height);
for (int h = 0; h < this.height; h++) {
sb.append(this.grid[h][this.lastCol]);
}
return sb.toString();
}
/**
* The contents of the "/" diagonal containing the last played chip
* (coordinates have a constant sum).
*/
private String slashDiagonal() {
StringBuilder sb = new StringBuilder(this.height);
for (int h = 0; h < this.height; h++) {
int w = this.lastCol + this.lastTop - h;
if (0 <= w && w < this.width) {
sb.append(this.grid[h][w]);
}
}
return sb.toString();
}
/**
* The contents of the "\" diagonal containing the last played chip
* (coordinates have a constant difference).
*/
private String backslashDiagonal() {
StringBuilder sb = new StringBuilder(this.height);
for (int h = 0; h < this.height; h++) {
int w = this.lastCol - this.lastTop + h;
if (0 <= w && w < this.width) {
sb.append(this.grid[h][w]);
}
}
return sb.toString();
}
private static boolean contains(String haystack, String needle) {
return haystack.indexOf(needle) >= 0;
}
public static void main(String[] args) {
try (Scanner input = new Scanner(System.in)) {
int height = 6, width = 8, moves = height * width;
ConnectFour board = new ConnectFour(width, height);
System.out.println("Use 0-" + (width - 1) + " to choose a column.");
System.out.println(board);
for (int player = 0; moves-- > 0; player = 1 - player) {
char symbol = players[player];
board.chooseAndDrop(symbol, input);
System.out.println(board);
if (board.isWinningPlay()) {
System.out.println("Player " + symbol + " wins!");
return;
}
}
System.out.println("Game over, no winner.");
}
}
}
Its a bit difficult to tell what you want from your code, but the absolute simplest way to keep track of what player it is, is to keep track of the turn number, and check if it is even or odd with the modulus function
This is just a brief bit of psuedocode to show you how you can tell what the turn is with simple math. You will have to adapt it to your own needs. You can see that it will only be "Player 2"'s turn on an even turn number where the turn number divided by 2 has no remainder. Just remember to increment the turn after every move.
There's no "good" answer. You're the one writing the code, you can decide whose turn it is, you just have to keep track of it.
int turn = 1;
for ( ) {
if (turn % 2 == 0) {
System.out.println("Player 2");
} else {
System.out.println("Player 1");
}
turn++;
}
Going through some past Java exercises so I can improve my programming in general and I've been stuck on this for quite a while. I'm going to post all of the source that's required, since this project is quite large and there's a lot of interfaces and subclasses that just offer to confuse.
public class Board {
public final static char NOUGHT = 'O';
public final static char CROSS = 'X';
public final static char EMPTY = ' ';
// Each cell is indexed as follows:
// 1 2 3
// 4 5 6
// 7 8 9
private char[][] grid; // a matrix to store the positions of the board
private int numOfMarks; // number of moves made on the board
private int lastMarkPosition; //position of last move maode in the board
public Board() {
grid = new char[3][3];
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
grid[row][col] = EMPTY;
}
}
numOfMarks = 0;
lastMarkPosition = 0;
}
//post: Returns true if the board is finished.
// A board is finished because either there is a winner or the board is full.
public boolean isFinished() {
return numOfMarks == 9 || getWinnerMark() != EMPTY;
}
//post: Records the position of the last mark made on the board.
public void setLastMarkPosition(int lastPosition){
lastMarkPosition = lastPosition;
}
//post: Returns the position of the last mark
public int getLastMarkPosition(){
return lastMarkPosition;
}
//post: Returns the mark ('X' or 'O') of the winner player if a winning condition exists in the board,
// returns EMPTY otherwise.
public char getWinnerMark() {
for (int i = 0; i < 3; i++) {
// check if there are three in a horizontal row
if (grid[i][0] != EMPTY && grid[i][0] == grid[i][1]
&& grid[i][1] == grid[i][2]) {
return grid[i][0];
}
// check if there are three in a vertical row
if (grid[0][i] != EMPTY && grid[0][i] == grid[1][i]
&& grid[1][i] == grid[2][i]) {
return grid[0][i];
}
}
// check if there are three in a diagonal row
if (grid[1][1] != EMPTY
&& (grid[1][1] == grid[0][0] && grid[1][1] == grid[2][2] || grid[1][1] == grid[0][2]
&& grid[1][1] == grid[2][0])) {
return grid[1][1];
}
// otherwise, return EMPTY as there is no winner
return EMPTY;
}
//post: Sets the given mark at the given position in the board
public void setMark(int pos, char mark) throws GameException {
if (numOfMarks == 9) {
throw new GameException("attempted to set mark on a full board.");
}
if (pos < 1 || pos > 9) {
throw new GameException(
"attempted to set mark in a wrong position: " + pos);
}
if (mark != NOUGHT && mark != CROSS) {
throw new GameException("attempted to set an invalid mark: "
+ String.valueOf(mark));
}
// perform board update
int row = (pos - 1) / 3;
int col = (pos - 1) % 3;
if (grid[row][col] != EMPTY) {
throw new GameException(
"attempted to set mark on a non-empty position: "
+ pos);
} else {
grid[row][col] = mark;
numOfMarks++;
}
}
//post: Returns the mark that is at a given position in the board
public char getMark(int pos) {
return grid[(pos-1)/3][(pos-1)%3];
}
//post: If the grid is not full, calculates whose turn is, based on the marks in the grid.
// Returns EMPTY if the board is already full.
public char getTurn() {
if (numOfMarks == 9) {
return EMPTY;
} else if (numOfMarks % 2 == 0) {
// by default, CROSS should go first
return CROSS;
} else {
return NOUGHT;
}
}
//post: Copy the board and returns it
public Board makeCopy() {
Board copy = new Board();
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
copy.grid[row][col] = this.grid[row][col];
}
}
copy.numOfMarks = this.numOfMarks;
return copy;
}
//post: Prints the given board
public static void display(Board board) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
System.out.print(" ");
char mark = board.grid[row][col];
if (mark != EMPTY) {
System.out.print(mark);
} else {
System.out.print((row)*3+(col+1));
}
System.out.print(" ");
if (col < 2) {
System.out.print("|");
}
}
System.out.println();
if (row < 2) {
System.out.println("-----------");
}
}
}
GameTreeInterface
public interface GameTreeInterface {
//post: Returns the board at the root of the game tree.
public Board getRootItem();
//post: Expands the game tree fully by adding all possible boards in the game.
// It uses a recursive auxiliary method.
public void expand();
//pre: The game tree is fully expanded.
//post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores();
//pre: Each board in the game tree has a score.
//post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have the highest score.
public int[] BestMoves();
//post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size();
}
GameTree
public class GameTree implements GameTreeInterface {
private GameTreeNode root; // reference to the root board of a game tree
public GameTree(Board board) {
this.root = new GameTreeNode(board);
}
// post: Returns the board at the root of a game tree.
public Board getRootItem() {
return root.getBoard();
}
// post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size() {
return sizeTree(root) + 1;
}
// post: Returns the number of boards stored in a game tree, excluded
// the root.
private int sizeTree(GameTreeNode node) {
int total = 0;
for (int i = 1; i < node.numberOfChildren(); i++) {
if (node.getChild(i).getBoard() != null)
total++;
}
return total;
}
// post: Expands the game tree fully by adding all possible boards in
// the game.
// It uses a recursive auxiliary method.
public void expand() {
expandTree(root);
}
// post: Expands the game tree from the given node by adding
// all the possible moves that the computer and the user player
// can make, until the game is finished, from the given node onwards.
private void expandTree(GameTreeNode node) {
if (!node.getBoard().isFinished()) {
char c = node.getBoard().getTurn();
for (int i = 1; i < 9; i++) {
if (node.getBoard().getMark(i) == Board.EMPTY) {
GameTreeNode n = new GameTreeNode(node.getBoard());
n.getBoard().setMark(i, c);
n.getBoard().setLastMarkPosition(i);
node.getChildren().add(2, n);
expandTree(n);
}
}
}
}
// pre: The game tree is fully expanded.
// post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores() {
char player = (root.getBoard()).getTurn();
assignScoresTree(root, player);
}
// post: Assigns scores to each board in a game tree for the computer
// player.
private void assignScoresTree(GameTreeNode node, char player) {
Board board = node.getBoard();
if (board.isFinished()) {
// base case of recursion
// score 3 for a winning board for the given player,
// score 2 for a draw baord,
// score 1 for a losing board for the given player
char winner = board.getWinnerMark();
if (winner == Board.EMPTY) {
// this is a draw!
node.setScore(2);
} else {
node.setScore(winner == player ? 3 : 1);
}
}
else {
// tries to assign the scores to all the children boards
// first, recursively
int minScore = Integer.MAX_VALUE;
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> children = node.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode child = it.next();
assignScoresTree(child, player);
// keeps track of the maximum and minimum scores
// of the children boards so far
int childScore = child.getScore();
if (childScore > maxScore)
maxScore = childScore;
if (childScore < minScore)
minScore = childScore;
}
// Assigns score to the current board in the recursion
// according to the player's turn
if (board.getTurn() == player) {
// get the maximum score as the player wants to
// win
node.setScore(maxScore);
} else {
// get the minimum score (as the player wants
// the opponent to lose;)
node.setScore(minScore);
}
}
}
// pre: Each board in the game tree has a score.
// post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have
// the highest score.
public int[] BestMoves() {
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> highestScoreBoards = new GenericList<GameTreeNode>();
GenericList<GameTreeNode> children = root.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode nextBoard = it.next();
int curScore = nextBoard.getScore();
if (maxScore < curScore) {
maxScore = curScore;
highestScoreBoards.clear();
highestScoreBoards.add(1, nextBoard);
} else if (maxScore == curScore) {
highestScoreBoards.add(1, nextBoard);
}
}
int[] moves = new int[highestScoreBoards.size()];
for (int i = 0; i < moves.length; i++) {
Board board = (highestScoreBoards.get(i + 1))
.getBoard();
moves[i] = board.getLastMarkPosition();
}
return moves;
}
}
GameTreeNode
public class GameTreeNode{
private GameTreeItem item; // includes the board object and the score
private GenericList<GameTreeNode> children; //list of gameTreeNodes of possible next moves.
public GameTreeNode(Board newBoard) {
this.item = new GameTreeItem(newBoard);
this.children = new GenericList<GameTreeNode>();
}
//post: Returns the board stored in a GameTreeNode
public Board getBoard() {
return item.board;
}
//post: Returns the children stored in a GameTreeNode, as a list of GameTreeNodes
public GenericList<GameTreeNode> getChildren(){
return children;
}
//post: Returns the score of the board
public int getScore() {
return item.score;
}
//post: Sets the score of the board to be equal to the given score
public void setScore(int score) {
item.score = score;
}
//post: Removes all the children
public void removeChildren(){
children = null;
}
//post: Returns the number of children
public int numberOfChildren(){
return children.size();
}
//post: Returns the child at a given position in the list of children, as a
GameTreeNode
public GameTreeNode getChild(int i){
return children.get(i);
}
//Inner class for storing a board and the score
private class GameTreeItem{
private Board board;
private int score;
public GameTreeItem(Board newBoard) {
this.board = newBoard;
score = 0;
}
}
Apologies for the rather large amount of code but I don't think I could explain the problem without having everything here. There's still a lot of extra code for the GenericList but it's a pretty straightforward implementation for a generic linked list.
If anyone does go through this code, my problem is in the GameTree class with the sizeTree() method. I offered a solution but I think it's far too simple to be correct. As I understand it, a GameTreeNode includes a GameTreeItem containing the current state of play and a reference to a linked list of GameTreeNode. However, my specification says that I have to use recursion to implement this method but I'm not sure how. Would my method work since it goes through every single child of the root and checks for the boards?
I know it's a long shot but if anyone can offer any help I'd really appreciate it!