|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- 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<Player> getPlayers() { return players; }
- /** set reference to players */
- void setPlayers (ArrayList<Player> 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<N ; ++i)
- for (int j =0 ; j<M ; ++j)
- this.tiles[i][j] = tiles[i][j];
- }
-
- /**
- * Copy snakes (deep copy)
- * @param snakes Source of snakes to use
- * @note Requires Snake copy constructor
- * @note This has to be called if the board is default constructed
- */
- void copySnakes(Snake[] snakes) {
- // Check if we need allocation
- if (this.snakes == null)
- this.snakes = new Snake[snakes.length];
- // Assign values (deep copy)
- for (int i =0 ; i<this.snakes.length ; ++i)
- this.snakes[i] = new Snake(snakes[i]);
- }
- /**
- * Copy ladders (deep copy)
- * @param ladders Source of ladders to use
- * @note Requires Ladder copy constructor
- * @note This has to be called if the board is default constructed
- */
- void copyLadders (Ladder[] ladders) {
- // Check if we need allocation
- if (this.ladders == null)
- this.ladders = new Ladder[ladders.length];
- // Assign values (deep copy)
- for (int i =0 ; i<this.ladders.length ; ++i)
- this.ladders[i] = new Ladder(ladders[i]);
- }
- /**
- * Copy apples (deep copy)
- * @param apples Source of apples to use
- * @note Requires Apple copy constructor
- * @note This has to be called if the board is default constructed
- */
- void copyApples (Apple[] apples) {
- // Check if we need allocation
- if (this.apples == null)
- this.apples = new Apple[apples.length];
- // Assign values (deep copy)
- for (int i =0 ; i<this.apples.length ; ++i)
- this.apples[i] = new Apple(apples[i]);
- }
- /** @} */
-
- /** @name Exposed API members */
- /** @{ */
-
- /**
- * Check if the tile is a snake head. If so return the snake's
- * tails tile. If not return the same tile
- * @param tile The tile to check
- * @return The result tile
- */
- int checkSnake (int tile) {
- for (int i =0 ; i<snakes.length ; ++i) {
- if (snakes[i].getHeadId() == tile)
- return snakes[i].getTailId();
- }
- return tile;
- }
-
- /**
- * Check if the tile is a ladder down step. If so return the ladder's
- * up step tile. If not return the same tile.
- * @note
- * This also break the ladder if used
- * @param tile The tile to check
- * @return The result tile
- */
- int checkLadder (int tile, boolean climb) {
- for (int i =0 ; i<ladders.length ; ++i) {
- if (ladders[i].getDownStepId() == tile &&
- ladders[i].getBroken() == false) {
- ladders[i].setBroken(climb);
- return ladders[i].getUpStepId();
- }
- }
- return tile;
- }
-
- /**
- * Check if the tile is an apple tile. If so eat it and return the score difference
- * @param tile The tile to check
- * @return The score difference
- */
- int checkApple (int tile, boolean eat) {
- int ds =0; // delta-score
- for (int i =0 ; i<apples.length ; ++i) {
- if (apples[i].getAppleTileId() == tile) {
- ds = apples[i].getPoints();
- // eat it
- if (eat)
- apples[i].setPoints(0);
- }
- }
- return ds;
- }
-
- /**
- * Create a playable board for the game.
- * @warning
- * This is not required to be called after construction in order to ensure board's playable
- * condition. In fact this function SHOULD NOT CALLED AT ALL.
- * The project requirements expect this to be public. The preferable mode would be private.
- * @see Board() constructor.
- */
- void createBoard () {
- _tileNumbering (); // Create tile numbering
- _placeSnakes (); // Place snakes
- _placeApples (); // Place Apples
- _placeLadders (); // place ladders
- }
-
- /**
- * @brief
- * make and print element boards
- * This is not required in order for the board to be playable
- * It just produce a stdout output for convenience.
- */
- 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 in a boustrophedon (zig-zag) way.
- * We use a starting point the tile[0][0] and as finish point
- * the tile[N-1][M-1]
- */
- private void _tileNumbering () {
- 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 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<snakes.length ; ++i) {
- // Keep getting heads until they are different from the previous
- do
- tile = _pickRandom (M+1, M*N); // Don't use first row for heads
- while (_search (head, tile) >= 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<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 = _pickRandom (1, M*N);
- while ((_search (apple_tiles, tile) >= 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<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 ladder tiles
- // and not in some snake's head
- do
- tile = _pickRandom (M+1, M*N); // Don't use first row for up-steps
- while ((_search (up_step, tile) >= 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<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 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<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 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<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:
- * <pre>
- * 16 15 14 13
- * 09 10 11 12
- * 08 07 06 05
- * 01 02 03 04
- * </pre>
- */
- 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("");
- }
-
- /**
- * Pick a random tile in range [from..to]
- * @param from The first tile to consider
- * @param to The last tile to consider
- * @return The random pick
- */
- private int _pickRandom (int from, int to) {
- return from + (int)(Math.random() * (to - from));
- }
-
- /** 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 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 */
- private ArrayList<Player> players; /**< board's copy of players reference vector */
- /** @} */
- }
-
|