package net.hoo2.auth.dsproject.snake; import java.lang.Math; import java.util.*; /** * @class Board * @brief The game's board representation. * * The board is a square collection of tiles numbered in a * boustrophedon (zig-zag) way. A number of snakes, ladders * and apples which we called elements are placed on the board * for each game. * * @author Christos Choutouridis AEM:8997 * @email cchoutou@ece.auth.gr */ public class Board { /** @name Constants */ /**@{ */ static final int POINTS_MAX = 20; /**< The maximum absolute number of points for each apple */ static final int POINTS_STEP = 5; /**< The difference between different apple points */ /**@} */ /** @name Constructors */ /** @{ */ /** A doing nothing default constructor * @warining Avoid using this constructor as it requires all setters(or copy) * and @ref createBoard() to be called after. */ Board () { N = M =0; tiles = null; snakes = null; ladders = null; apples = null; players = null; } /** * @brief Creates a board for game * * This constructor allocates the memory for the board and elements and * creates a board by placing all required elements on the board. * * @param N The row for the board * @param M The columns of the board * @param numOfSnakes Number of snakes to place * @param numOfLadders Number of ladders to place * @param numOfApples Number of Apples to place * * @warning * We call @ref createBoard() inside this constructor in order for * the board to be in "playable condition". This is preferable by the author. * A constructor should(if possible) to leave the object in a usable condition. * In order to follow the project requirements we create this functionality in a * separate function @ref createBoard(). We believe that if a user can make a * mistake he eventually will do it sometime. Here, if we leave the createBoard() * call to user we are enabling him to make it. */ Board (int N, int M, int numOfSnakes, int numOfLadders, int numOfApples) { // 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]; players = null; createBoard (); // Complete board preparation and make all the element memory allocations } /** * @brief Copy constructor. * We make a deep copy of B and we trust B's data to be valid. * @param B The board we want to copy * @note We don't use clone as long as we don't inherit Cloneable iface * @note This requires Snake, Apple and Ladder copy constructors */ Board (Board B) { N = B.getN(); 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]; players = B.getPlayers(); // reference only (don't need to clone) // Copy B's guts into new memory copyTiles(B.getTiles()); copySnakes(B.getSnakes()); copyLadders(B.getLadders()); copyApples(B.getApples()); } /** @} */ /** @name Get/Set interface */ /** @{ */ /** Get value N */ int getN () { return N; } /** Set value N */ void setN (int N) { this.N = N; } /** Get value M */ int getM () { return M; } /** Set value M */ void setM (int M) { this.M = M; } /** Get reference to tiles */ int[][] getTiles () { return tiles; } /** * Set tiles * @param tiles Source of tiles to use * @note This has to be called if the board is default constructed */ void setTiles (int[][] tiles) { this.tiles = tiles; } /** Get reference to snakes */ Snake[] getSnakes() { return snakes; } /** * Set snakes * @param snakes Reference to snakes to use * @note This requires snakes must be allocated elsewhere. */ void setSnakes(Snake[] snakes) { this.snakes = snakes; } /** Get reference to ladders */ Ladder[] getLadders() { return ladders; } /** * Set ladders * @param ladders Reference to ladders to use * @note This requires ladders must be allocated elsewhere. */ void setLadders(Ladder[] ladders) { this.ladders = ladders; } /** Get reference to apples */ Apple[] getApples() { return apples; } /** * Set apples * @param apples Reference to apples to use * @note This requires apples must be allocated elsewhere. */ void setApples(Apple[] apples) { this.apples = apples; } /** get reference to players */ ArrayList getPlayers() { return players; } /** set reference to players */ void setPlayers (ArrayList players) { this.players = players; } /** * Copy tiles * @param tiles Source of tiles to use * @note This has to be called if the board is default constructed */ void copyTiles (int[][] tiles) { // Check if we need allocation if (this.tiles == null) this.tiles = new int[N][M]; // Copy-assign the values for (int i =0 ; i=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 placed * below heads and heads must be placed in separate tiles */ private void _placeSnakes () { 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= 0); head[i] = tile; tail[i] = _pickRandom (1, 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 * The constrains here are * that apples have to lie on different tiles and not in some * snake's head. * @note We require we have snakes. */ private void _placeApples () { int [] apple_tiles = new int [apples.length]; // temporary placeholder for apples int [] snake_tiles = new int [snakes.length]; // array with snake head tiles for (int i =0 ; i= 0) || (_search (snake_tiles, tile) >= 0)); apple_tiles[i] = tile; // get points int points = _pickRandom (1, (POINTS_MAX/POINTS_STEP)) * POINTS_STEP; 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 * * We add constrains so each ladder's up-step tile has to be different from: * * A snake's head tile. This ensures ladders and snakes are independent * * A ladders's down-step. This ensure we eliminate ladder chains. * * One other ladder's up-step. This is not critical but helps the printElement functionality * * We add constrains so each ladder's down-step tile has to be different from: * * A snake's head tile. This ensures ladders and snakes are independent * * A ladders's down-step. This is not critical but helps the printElement functionality * * One other ladder's up-step. This ensure we eliminate ladder chains. * @note We require we have snakes. */ private void _placeLadders () { int [] up_step = new int [ladders.length]; // temporary place holder for up-steps int [] down_step = 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= 0) || (_search (down_step, tile) >= 0) || (_search (snake_tiles, tile) >= 0)); up_step[i] = tile; // Keep getting down-steps until they are different from the previous ladder tiles // and not in some snake's head do // Don't use up-step row and up for down-step tile = _pickRandom (1, up_step[i]-up_step[i]%M); while ((_search (up_step, tile) >= 0) || (_search (down_step, tile) >= 0) || (_search (snake_tiles, tile) >= 0)); down_step[i] = tile; ladders[i] = new Ladder (i, up_step[i], down_step[i]); // Allocate ladder } } /** * Make element array of snakes as required by the project * @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= 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 as required by the project * @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= 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 as required by the project * @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= 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=0 Element found */ private int _search (int[] array, int elem) { for (int i=0 ; i players; /**< board's copy of players reference vector */ /** @} */ }