package com.letsgo;
import java.util.ArrayList;
/**
* Class used to represent the Board of a game
*/
public class Board {
private int width ;
private int height ;
private BoardNode[][] grid ;
private boolean nextPlayerWhite = true;
/**
* Board constructor which creates a grid with the given width and height
* @param width
* @param height
*/
public Board(int width, int height)
{
this.width = width ;
this.height = height ;
this.grid = new BoardNode[this.width][this.height];
for(int i=0 ; i<this.width ; i++)
{
for(int j=0 ; j<this.height ; j++)
{
this.grid[i][j] = new BoardNode(i,j);
}
}
}
/**
* Basic constructor which creates a grid 9x9
*/
public Board()
{
this(9,9);
}
/**
* Return a String filled with the coordinates of each BoardNode and its type
* ex : 3.4-BLACK
*/
public String toString()
{
String s = "";
if(this.grid != null)
{
for(int i=0 ; i<this.width ; i++)
{
for(int j=0 ; j<this.height ; j++)
{
s += " "+this.grid[i][j].getRow()+"."+this.grid[i][j].getCol()+"-"+this.grid[i][j].getType();
}
}
}
return s ;
}
/**
* Fills the board from an ArrayList of BoardMovement (recalculates the game)
* @param bms ArrayList of BoardMovement, it must start from seq 1
* @throws BoardBadHistory if the BoardMovement's sequence number are not following each other or if the first is not 1
*/
public void fillFromHistory(ArrayList<BoardMovement> bms) throws BoardBadHistory
{
if(bms == null)
return ;
if(bms.get(0).getSeq() > 1)
throw new BoardBadHistory();
int seq = 1 ;
BNT currentType ;
for(BoardMovement bm : bms)
{
// If the sequence number are not in order throw an error
if(bm.getSeq() != seq)
throw new BoardBadHistory();
// Setting the current colors for the turn
if(bm.getSeq()%2 == 0)
{
currentType = BNT.WHITE;
}
else
{
currentType = BNT.BLACK;
}
// If the node is already filled an error is thrown
if(this.grid[bm.getRow()][bm.getColumn()].getType().equals(BNT.BLACK) || this.grid[bm.getRow()][bm.getColumn()].getType().equals(BNT.WHITE))
throw new BoardBadHistory();
else
{
// Setting the new color of the node
this.grid[bm.getRow()][bm.getColumn()].setType(currentType);
}
// Check if some tokens are deleted due to the last movement
checkArea(bm.getRow(), bm.getColumn());
seq++ ;
}
this.nextPlayerWhite = (seq % 2 == 1);
}
/**
* Start a check around the node given, this function should be called after each movement
* it will check if some opponent's tokens need to be removed
* @param row of the last movement
* @param col of the last movement
* @see checkFleeWay
*/
private void checkArea(int row, int col)
{
if(row < 0 || col < 0 || row > width || col > height)
return ;
BNT currentType = this.grid[row][col].getType() ;
BNT currentOpponentType ;
if(currentType.equals(BNT.BLACK))
currentOpponentType = BNT.WHITE ;
else if(currentType.equals(BNT.WHITE))
currentOpponentType = BNT.BLACK ;
else
return ;
// CHECK TOP
if(row > 0 && this.grid[row-1][col].getType().equals(currentOpponentType))
{
checkFleeWay(row-1, col, currentType, currentOpponentType);
}
// CHECK RIGHT
if(col < this.width-1 && this.grid[row][col+1].getType().equals(currentOpponentType))
{
checkFleeWay(row, col+1, currentType, currentOpponentType);
}
// CHECK DOWN
if(row < this.height-1 && this.grid[row+1][col].getType().equals(currentOpponentType))
{
checkFleeWay(row+1, col, currentType, currentOpponentType);
}
// CHECK LEFT
if(col > 0 && this.grid[row][col-1].getType().equals(currentOpponentType))
{
checkFleeWay(row, col-1, currentType, currentOpponentType);
}
}
/**
* Checks the Area around the given node, if the node has a color it searchs if a flee way is available
* if there is no flee way available, the funcion removes all the nodes from the chain linked to the given coordinates
* @param row of the node to check
* @param col of the node to check
* @param currentType color of the last movement
* @param currentOpponentType color of the opponent
* @see checkArea
*/
private void checkFleeWay(int row, int col, BNT currentType, BNT currentOpponentType)
{
// This ArrayList is used to know which nodes were visited during the search
// if the search finished without finding an empty node around the chain,
// all the visited nodes will be deleted
ArrayList<BoardNode> visited = new ArrayList<BoardNode>();
// This ArrayList is used to know which nodes are part of the chain, and need to be visited
// the search finish when this list is empty
ArrayList<BoardNode> unvisited = new ArrayList<BoardNode>() ;
BoardNode current ;
int cRow ;
int cCol ;
boolean fleeWay = false ;
unvisited.add(this.grid[row][col]);
while(!unvisited.isEmpty() && !fleeWay)
{
current = unvisited.remove(0);
visited.add(current);
cRow = current.getRow();
cCol = current.getCol();
if(cRow > 0) // Top
{
if(this.grid[cRow-1][cCol].getType().equals(BNT.EMPTY))
fleeWay = true ; // An empty node was found
else if(this.grid[cRow-1][cCol].getType().equals(currentOpponentType))
unvisited.add(this.grid[cRow-1][cCol]); // An opponent token was found, so we add it to the unvisited list
}
if(cCol < this.width-1) // Right
{
if(this.grid[cRow][cCol+1].getType().equals(BNT.EMPTY))
fleeWay = true ; // An empty node was found
else if(this.grid[cRow][cCol+1].getType().equals(currentOpponentType))
unvisited.add(this.grid[cRow][cCol+1]); // An opponent token was found, so we add it to the unvisited list
}
if(cRow < this.height-1) // Down
{
if(this.grid[cRow+1][cCol].getType().equals(BNT.EMPTY))
fleeWay = true ; // An empty node was found
else if(this.grid[cRow+1][cCol].getType().equals(currentOpponentType))
unvisited.add(this.grid[cRow+1][cCol]); // An opponent token was found, so we add it to the unvisited list
}
if(cCol > 0) // Left
{
if(this.grid[cRow][cCol-1].getType().equals(BNT.EMPTY))
fleeWay = true ; // An empty node was found
else if(this.grid[cRow][cCol-1].getType().equals(currentOpponentType))
unvisited.add(this.grid[cRow][cCol-1]); // An opponent token was found, so we add it to the unvisited list
}
}
// If there is no fleeWay at the end of the search, all the visited nodes are deleted
if(!fleeWay)
{
// Remove BoardNode from the grid
for(BoardNode b : visited)
{
this.grid[b.getRow()][b.getCol()] = new BoardNode(b.getRow(), b.getCol(), this.grid[b.getRow()][b.getCol()].getValue(), BNT.EMPTY);
}
}
}
/**
* Retrieve the BoardNode at the given position
* @param x position of the node to modify
* @param y position of the node to modify
* @return the BoardNode at the given position
* @throws BoardException
*/
public BoardNode get(int x, int y) throws BoardException
{
if(x<0 || x>=this.width || y<0 || y>=this.height)
throw new BoardException();
return grid[x][y];
}
/**
* Change the type of a BoardNode in the grid
* @param x position of the node to modify
* @param y position of the node to modify
* @param type new type of the node : BNT {EMPTY, BLACK, WHITE}
* @throws BoardException if x or y are out of bound
*/
public void set(int x, int y, BNT type) throws BoardException
{
if(x<0 || x>=this.width || y<0 || y>=this.height)
throw new BoardException();
this.grid[x][y].setType(type) ;
}
/**
* Plays a move
* @param x position of the new rock
* @param y position of the new rock
* @throws BoardException if x or y are out of bound
*/
public void playMove(int x, int y) throws BoardException
{
this.set(x, y, nextPlayerWhite ? BNT.WHITE : BNT.BLACK);
nextPlayerWhite = !nextPlayerWhite;
}
/**
* @return the width of the grid
*/
public int getWidth() {
return width;
}
/**
* @param width the width of the grid to set
*/
public void setWidth(int width) {
this.width = width;
}
/**
* @return the height of the grid
*/
public int getHeight() {
return height;
}
/**
* @param height the height of the grid to set
*/
public void setHeight(int height) {
this.height = height;
}
/**
* @return the grid
*/
public BoardNode[][] getGrid() {
return grid;
}
/**
* @param grid the grid to set
*/
public void setGrid(BoardNode[][] grid) {
this.grid = grid;
}
/**
* @return the nextPlayerWhite
*/
public boolean isNextPlayerWhite() {
return nextPlayerWhite;
}
}
|