|
|
@@ -0,0 +1,451 @@ |
|
|
|
package SnakePkg;
|
|
|
|
|
|
|
|
import java.util.Random;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The game's board representation
|
|
|
|
* @author Christos Choutouridis 8997
|
|
|
|
*/
|
|
|
|
public class Board {
|
|
|
|
/** @name Constructors */
|
|
|
|
/** @{ */
|
|
|
|
/** Default ctor */
|
|
|
|
Board () {
|
|
|
|
N = M =0;
|
|
|
|
tiles = null;
|
|
|
|
snakes = null;
|
|
|
|
ladders = null;
|
|
|
|
apples = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main constructor
|
|
|
|
* We make some simple input checking before memory allocation.
|
|
|
|
* @note
|
|
|
|
* May throw BadBoardInput
|
|
|
|
*/
|
|
|
|
Board (int N, int M, int numOfSnakes, int numOfLadders, int numOfApples)
|
|
|
|
throws BadBoardInput {
|
|
|
|
// Input checking
|
|
|
|
if (numOfApples + numOfLadders*2 + numOfSnakes*2 >= N*M/2)
|
|
|
|
throw new BadBoardInput("Too many Apples, Snakes and ladders");
|
|
|
|
// Init the board object
|
|
|
|
setN (N); // Input checked version (may throw)
|
|
|
|
setM (M); // Input checked version (may throw)
|
|
|
|
tiles = new int[N][M];
|
|
|
|
snakes = new Snake[numOfSnakes];
|
|
|
|
ladders = new Ladder[numOfLadders];
|
|
|
|
apples = new Apple[numOfApples];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy constructor. We make a deep copy of B and we trust B's
|
|
|
|
* data to be valid
|
|
|
|
* @note We don't use clone as long as we don't inherit Cloneable iface
|
|
|
|
*/
|
|
|
|
Board (Board B) {
|
|
|
|
this.N = B.getN();
|
|
|
|
this.M = B.getM();
|
|
|
|
tiles = new int[N][M];
|
|
|
|
snakes = new Snake[B.getSnakes().length];
|
|
|
|
ladders = new Ladder[B.getLadders().length];
|
|
|
|
apples = new Apple[B.getApples().length];
|
|
|
|
// Copy B's guts
|
|
|
|
for (int i =0 ; i<N ; ++i)
|
|
|
|
for (int j =0 ; j<M ; ++j)
|
|
|
|
tiles[i][j] = B.getTiles()[i][j];
|
|
|
|
for (int i =0 ; i<B.getSnakes().length ; ++i)
|
|
|
|
snakes[i] = B.getSnakes()[i];
|
|
|
|
for (int i =0 ; i<B.getLadders().length ; ++i)
|
|
|
|
ladders[i] = B.getLadders()[i];
|
|
|
|
for (int i =0 ; i<B.getApples().length ; ++i)
|
|
|
|
apples[i] = B.getApples()[i];
|
|
|
|
}
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
/** @name Get/Set interface */
|
|
|
|
/** @{ */
|
|
|
|
|
|
|
|
/** Get value N */
|
|
|
|
int getN () { return N; }
|
|
|
|
/**
|
|
|
|
* Set value N
|
|
|
|
* @throws BadBoardInput
|
|
|
|
*/
|
|
|
|
void setN (int N) throws BadBoardInput {
|
|
|
|
// Input checking
|
|
|
|
if (N<=0)
|
|
|
|
throw new BadBoardInput("N is zero or Less");
|
|
|
|
this.N = N;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get value M */
|
|
|
|
int getM () { return M; }
|
|
|
|
/**
|
|
|
|
* Set value M
|
|
|
|
* @throws BadBoardInput
|
|
|
|
*/
|
|
|
|
void setM (int M) throws BadBoardInput {
|
|
|
|
// Input checking
|
|
|
|
if (M<=0)
|
|
|
|
throw new BadBoardInput("M is zero or Less");
|
|
|
|
this.M = M;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get reference to tiles */
|
|
|
|
int[][] getTiles () { return tiles; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set tiles (deep copy)
|
|
|
|
* @param tiles Source of tiles to use
|
|
|
|
* @throws BadBoardInput
|
|
|
|
*/
|
|
|
|
void setTiles (int[][] tiles) throws BadBoardInput {
|
|
|
|
// Input checking
|
|
|
|
if (tiles.length != N && tiles[0].length != M)
|
|
|
|
throw new BadBoardInput("tiles size missmatch");
|
|
|
|
// Check if we need allocation
|
|
|
|
if (this.tiles == null)
|
|
|
|
this.tiles = new int[N][M];
|
|
|
|
// Assign values (deep copy)
|
|
|
|
for (int i =0 ; i<N ; ++i)
|
|
|
|
for (int j =0 ; j<M ; ++j)
|
|
|
|
this.tiles[i][j] = tiles[i][j];
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get reference to snakes */
|
|
|
|
Snake[] getSnakes() { return snakes; }
|
|
|
|
/**
|
|
|
|
* Set snakes (deep copy)
|
|
|
|
* @param snakes Source of snakes to use
|
|
|
|
* @throws BadBoardInput
|
|
|
|
* @note Requires Snake copy contructor
|
|
|
|
*/
|
|
|
|
void setSnakes(Snake[] snakes) throws BadBoardInput {
|
|
|
|
// Check if we need allocation
|
|
|
|
if (this.snakes == null)
|
|
|
|
this.snakes = new Snake[snakes.length];
|
|
|
|
// Input checking
|
|
|
|
if (this.snakes.length != snakes.length)
|
|
|
|
throw new BadBoardInput("snakes size missmatch");
|
|
|
|
// Assign values (deep copy)
|
|
|
|
for (int i =0 ; i<this.snakes.length ; ++i)
|
|
|
|
this.snakes[i] = new Snake(snakes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get reference to ladders */
|
|
|
|
Ladder[] getLadders() { return ladders; }
|
|
|
|
/**
|
|
|
|
* Set ladders (deep copy)
|
|
|
|
* @param ladders Source of snakes to use
|
|
|
|
* @throws BadBoardInput
|
|
|
|
* @note Requires Ladder copy contructor
|
|
|
|
*/
|
|
|
|
void setLadders (Ladder[] ladders) throws BadBoardInput {
|
|
|
|
// Check if we need allocation
|
|
|
|
if (this.ladders == null)
|
|
|
|
this.ladders = new Ladder[ladders.length];
|
|
|
|
// Input checking
|
|
|
|
if (this.ladders.length != ladders.length)
|
|
|
|
throw new BadBoardInput("ladders size missmatch");
|
|
|
|
// Assign values (deep copy)
|
|
|
|
for (int i =0 ; i<this.ladders.length ; ++i)
|
|
|
|
this.ladders[i] = new Ladder(ladders[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get reference to apples */
|
|
|
|
Apple[] getApples() { return apples; }
|
|
|
|
/**
|
|
|
|
* Set apples (deep copy)
|
|
|
|
* @param apples Source of snakes to use
|
|
|
|
* @throws BadBoardInput
|
|
|
|
* @note Requires Apple copy contructor
|
|
|
|
*/
|
|
|
|
void setApples (Apple[] apples) throws BadBoardInput {
|
|
|
|
// Check if we need allocation
|
|
|
|
if (this.apples == null)
|
|
|
|
this.apples = new Apple[apples.length];
|
|
|
|
// Input checking
|
|
|
|
if (this.apples.length != apples.length)
|
|
|
|
throw new BadBoardInput("ladders size missmatch");
|
|
|
|
// Assign values (deep copy)
|
|
|
|
for (int i =0 ; i<this.apples.length ; ++i)
|
|
|
|
this.apples[i] = new Apple(apples[i]);
|
|
|
|
}
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
/** @name Exposed API members */
|
|
|
|
/** @{ */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a playable board for the game
|
|
|
|
*/
|
|
|
|
void createBoard () {
|
|
|
|
_tile_numbering (); // Create tile numbering
|
|
|
|
_place_snakes (); // Place snakes
|
|
|
|
_place_apples (); // Place Apples
|
|
|
|
_place_ladders (); // place ladders
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* make and PRINT element boards
|
|
|
|
*/
|
|
|
|
void createElementBoard () {
|
|
|
|
String[][] elementBoardSnakes = new String[N][M];
|
|
|
|
String[][] elementBoardLadders = new String[N][M];
|
|
|
|
String[][] elementBoardApples = new String[N][M];
|
|
|
|
|
|
|
|
_makeElementSnakes (elementBoardSnakes);
|
|
|
|
_makeElementLadders (elementBoardLadders);
|
|
|
|
_makeElementApples (elementBoardApples);
|
|
|
|
|
|
|
|
_printElement (elementBoardSnakes, "elementBoardSnakes");
|
|
|
|
_printElement (elementBoardLadders, "elementBoardLadders");
|
|
|
|
_printElement (elementBoardApples, "elementBoardApples");
|
|
|
|
}
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
|
|
|
|
/** @name Private api */
|
|
|
|
/**@{ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Create the tile numbering
|
|
|
|
* We use a starting point the tile[0][0] and as finish point
|
|
|
|
* the tile[N-1][M-1]
|
|
|
|
*/
|
|
|
|
private void _tile_numbering () {
|
|
|
|
for (int i=0, tile =1 ; i<N ; ++i) {
|
|
|
|
if (i%2 == 0) {
|
|
|
|
// Even row, go right
|
|
|
|
for (int j=0 ; j<M ; ++j)
|
|
|
|
tiles[i][j] = tile++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Odd row, go left
|
|
|
|
for (int j=M-1 ; j>=0 ; --j)
|
|
|
|
tiles[i][j] = tile++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Place the snakes on the board
|
|
|
|
* The only constrain at this point is that snake tails must be
|
|
|
|
* below heads and in different tiles
|
|
|
|
*/
|
|
|
|
private void _place_snakes () {
|
|
|
|
int [] head = new int [snakes.length]; // temporary place holder for heads
|
|
|
|
int [] tail = new int [snakes.length]; // temporary place holder for tails
|
|
|
|
for (int i =0, tile =0 ; i<snakes.length ; ++i) {
|
|
|
|
// Keep getting heads until they are different from the previous
|
|
|
|
do
|
|
|
|
tile = (int)(M + Math.random() * (N-1) * M); // Don't use first row for heads
|
|
|
|
while (_search (head, tile) >= 0);
|
|
|
|
head[i] = tile;
|
|
|
|
tail[i] = (int)(Math.random() * (head[i] - head[i]%M)); // Don't use heads row and up for tail
|
|
|
|
snakes[i] = new Snake(i, head[i], tail[i]); // Allocate snake
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Place apples on the board
|
|
|
|
* At this point we have snakes. The constrains here are
|
|
|
|
* that apples have to lie on different tiles and not in some
|
|
|
|
* snake's head
|
|
|
|
*/
|
|
|
|
private void _place_apples () {
|
|
|
|
int [] apple_tiles = new int [apples.length]; // temporary placeholder for heads
|
|
|
|
int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
|
|
|
|
for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
|
|
|
|
snake_tiles[i] = snakes[i].getHeadId();
|
|
|
|
|
|
|
|
for (int i =0, tile =0 ; i<apples.length ; ++i) {
|
|
|
|
// Keep getting tiles until they are different from the previous
|
|
|
|
// and not in some snake's head
|
|
|
|
do
|
|
|
|
tile = (int) (Math.random() * (N * M));
|
|
|
|
while ((_search (apple_tiles, tile) >= 0) ||
|
|
|
|
(_search (snake_tiles, tile) >= 0));
|
|
|
|
apple_tiles[i] = tile;
|
|
|
|
int points = (int)(Math.random() *10) * 5; // get points
|
|
|
|
boolean red = (boolean)(Math.random() >=0.5); // get color
|
|
|
|
// Allocate apple
|
|
|
|
if (red)
|
|
|
|
apples[i] = new Apple(i, tile, "red", points);
|
|
|
|
else
|
|
|
|
apples[i] = new Apple(i, tile, "black", -points);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Place ladders on board
|
|
|
|
* At this point we have snakes and apples.
|
|
|
|
* @note
|
|
|
|
* We add a constrain for ladder so its up-setp has to be different from a
|
|
|
|
* snake's head tile. This ensures the each ladder and snake are independent
|
|
|
|
*/
|
|
|
|
private void _place_ladders () {
|
|
|
|
int [] upstep = new int [ladders.length]; // temporary place holder for up-steps
|
|
|
|
int [] downstep = new int [ladders.length]; // temporary place holder for down-step
|
|
|
|
int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
|
|
|
|
for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
|
|
|
|
snake_tiles[i] = snakes[i].getHeadId();
|
|
|
|
|
|
|
|
for (int i =0, tile =0 ; i<ladders.length ; ++i) {
|
|
|
|
// Keep getting up-steps until they are different from the previous
|
|
|
|
// and not in some snake's head
|
|
|
|
do
|
|
|
|
tile = (int)(M + Math.random() * (N-1) * M); // Don't use first row for heads
|
|
|
|
while ((_search (upstep, tile) >= 0) ||
|
|
|
|
(_search (snake_tiles, tile) >= 0));
|
|
|
|
upstep[i] = tile;
|
|
|
|
downstep[i] = (int)(Math.random() * (upstep[i] - upstep[i]%M));
|
|
|
|
//^ Don't use up-step row and up for down-step
|
|
|
|
ladders[i] = new Ladder (i, upstep[i], downstep[i]); // Allocate ladder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make element array of snakes
|
|
|
|
* @param elemSnakes
|
|
|
|
*/
|
|
|
|
private void _makeElementSnakes (String[][] elemSnakes) {
|
|
|
|
int [] head_tiles = new int [snakes.length]; // array with snake head tiles
|
|
|
|
int [] tail_tiles = new int [snakes.length]; // array with snake head tiles
|
|
|
|
int sn =-1;
|
|
|
|
// Load snake head tiles
|
|
|
|
for (int i =0 ; i<snakes.length ; ++i) {
|
|
|
|
head_tiles[i] = snakes[i].getHeadId();
|
|
|
|
tail_tiles[i] = snakes[i].getTailId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search all tiles for snake heads and tails
|
|
|
|
for (int i =0; i<N ; ++i) {
|
|
|
|
for (int j =0 ; j<M ; ++j) {
|
|
|
|
if ((sn = _search (head_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemSnakes[i][j] = "SH" + sn;
|
|
|
|
else if ((sn = _search (tail_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemSnakes[i][j] = "ST" + sn;
|
|
|
|
else
|
|
|
|
elemSnakes[i][j] = "___";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make element array of ladders
|
|
|
|
* @param elemLadders
|
|
|
|
*/
|
|
|
|
private void _makeElementLadders (String[][] elemLadders) {
|
|
|
|
int [] up_tiles = new int [ladders.length]; // array with ladder up-step tiles
|
|
|
|
int [] down_tiles = new int [ladders.length]; // array with ladder down-step tiles
|
|
|
|
int sn =-1;
|
|
|
|
// Load ladder tiles
|
|
|
|
for (int i =0 ; i<ladders.length ; ++i) {
|
|
|
|
up_tiles[i] = ladders[i].getUpStepId();
|
|
|
|
down_tiles[i] = ladders[i].getDownStepId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search all tiles for snake heads and tails
|
|
|
|
for (int i =0; i<N ; ++i) {
|
|
|
|
for (int j =0 ; j<M ; ++j) {
|
|
|
|
if ((sn = _search (up_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemLadders[i][j] = "LU" + sn;
|
|
|
|
else if ((sn = _search (down_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemLadders[i][j] = "LD" + sn;
|
|
|
|
else
|
|
|
|
elemLadders[i][j] = "___";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make element array of apples
|
|
|
|
* @param elemApples
|
|
|
|
*/
|
|
|
|
private void _makeElementApples (String[][] elemApples) {
|
|
|
|
int [] red_tiles = new int [apples.length]; // array with red apple tiles
|
|
|
|
int [] black_tiles = new int [apples.length]; // array with black apple tiles
|
|
|
|
int sn =-1;
|
|
|
|
// Load apple tiles
|
|
|
|
for (int i =0 ; i<apples.length ; ++i) {
|
|
|
|
if (apples[i].getColor() == "red")
|
|
|
|
red_tiles[i] = apples[i].getAppleTileId();
|
|
|
|
else
|
|
|
|
black_tiles[i] = apples[i].getAppleTileId();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search all tiles for snake heads and tails
|
|
|
|
for (int i =0; i<N ; ++i) {
|
|
|
|
for (int j =0 ; j<M ; ++j) {
|
|
|
|
if ((sn = _search (red_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemApples[i][j] = "AR" + sn;
|
|
|
|
else if ((sn = _search (black_tiles, tiles[i][j])) >= 0)
|
|
|
|
elemApples[i][j] = "AB" + sn;
|
|
|
|
else
|
|
|
|
elemApples[i][j] = "___";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Print element array
|
|
|
|
* @param elem The element array to print
|
|
|
|
* @param caption The caption
|
|
|
|
* @note
|
|
|
|
* As long as we use tiles[0][0] for first tile, this method
|
|
|
|
* has to print in reverse Y-axis order. For ex:
|
|
|
|
* 16 15 14 13
|
|
|
|
* 09 10 11 12
|
|
|
|
* 08 07 06 05
|
|
|
|
* 01 02 03 04
|
|
|
|
*/
|
|
|
|
private void _printElement (String[][] elem, String caption) {
|
|
|
|
System.out.print(caption);
|
|
|
|
for (int i=N-1 ; i>=0 ; --i) {
|
|
|
|
System.out.println("");
|
|
|
|
System.out.print(" ");
|
|
|
|
for (int j =0 ; j<M ; ++j)
|
|
|
|
System.out.print(elem[i][j] + " ");
|
|
|
|
}
|
|
|
|
System.out.println("");
|
|
|
|
System.out.println("");
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Search algorithm
|
|
|
|
* @param array Array to search
|
|
|
|
* @param elem Element of type T to find inside of array
|
|
|
|
* @return The status of the operation
|
|
|
|
* @arg -1 Element not found
|
|
|
|
* @arg >=0 Element found
|
|
|
|
*/
|
|
|
|
private int _search (int[] array, int elem) {
|
|
|
|
for (int i=0 ; i<array.length ; ++i)
|
|
|
|
if (elem == array[i])
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/**@} */
|
|
|
|
|
|
|
|
/** @name Data members (private) */
|
|
|
|
/** @{ */
|
|
|
|
private int N; //!< Board's row count
|
|
|
|
private int M; //!< Board's Column count
|
|
|
|
private int[][] tiles; //!< Board's tiles
|
|
|
|
private Snake[] snakes; //!< Board's snakes
|
|
|
|
private Ladder[] ladders; //!< Board's ladders
|
|
|
|
private Apple[] apples; //!< Board's apples
|
|
|
|
/** @} */
|
|
|
|
}
|
|
|
|
|
|
|
|
class BadBoardInput extends Exception {
|
|
|
|
BadBoardInput() {}
|
|
|
|
BadBoardInput(String str) { super(str); }
|
|
|
|
//static final long SerialVersionUIDAdder = 0;
|
|
|
|
}
|