From 544e5d9e04c2dcd3861c3160484abca8f64ba8a6 Mon Sep 17 00:00:00 2001
From: EpicKnarvik97 <kristian.knarvik@knett.no>
Date: Sat, 22 Feb 2020 23:27:59 +0100
Subject: [PATCH] =?UTF-8?q?Oppdaterer=20brettet=20med=20n=C3=B8dvendige=20?=
 =?UTF-8?q?metoder=20for=20=C3=A5=20kunne=20flytte=20en=20robot?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Legger til tester for brettet
---
 .../fiasko/roborally/objects/Board.java       | 205 ++++++++++++++++--
 .../fiasko/roborally/objects/BoardTest.java   |  65 ++++++
 2 files changed, 255 insertions(+), 15 deletions(-)
 create mode 100644 src/test/java/inf112/fiasko/roborally/objects/BoardTest.java

diff --git a/src/main/java/inf112/fiasko/roborally/objects/Board.java b/src/main/java/inf112/fiasko/roborally/objects/Board.java
index 8453e59..ea63450 100644
--- a/src/main/java/inf112/fiasko/roborally/objects/Board.java
+++ b/src/main/java/inf112/fiasko/roborally/objects/Board.java
@@ -1,36 +1,211 @@
 package inf112.fiasko.roborally.objects;
 
+import inf112.fiasko.roborally.element_properties.Direction;
+import inf112.fiasko.roborally.element_properties.Position;
+import inf112.fiasko.roborally.element_properties.RobotID;
+import inf112.fiasko.roborally.element_properties.TileType;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * This class represents a board
  */
-
 public class Board {
-    private Grid walls;
-    private Grid otherObjects;
-    private ArrayList<Robot> deadRobots = new ArrayList<>();
-    private ArrayList<Robot> aliveRobots;
+    private int boardHeight;
+    private int boardWidth;
+    private IGrid<Wall> walls;
+    private IGrid<Tile> tiles;
+    private Map<RobotID, Robot> robots;
+    private List<Robot> deadRobots;
 
     /**
      * Initializes the board
-     * @param walls a grid containing all the walls
-     * @param otherObjects a grid containing all the other Objects like flags and conveyor belts
-     * @param aliveRobots a list of all the robots that are currently alive
+     * @param tiles A grid containing all tiles
+     * @param walls A grid containing all walls
+     * @param robots A list of all robots in the game
      */
-    public Board(Grid walls, Grid otherObjects, ArrayList aliveRobots){
-        this.walls=walls;
-        this.otherObjects=otherObjects;
-        this.aliveRobots=aliveRobots;
+    public Board(IGrid<Tile> tiles, IGrid<Wall> walls, List<Robot> robots) {
+        if (walls.getWidth() != tiles.getWidth() || walls.getHeight() != tiles.getHeight()) {
+            throw new IllegalArgumentException("The grids in the input don't have the same dimensions.");
+        }
+        this.robots = new HashMap<>();
+        for (Robot robot : robots) {
+            if (this.robots.get(robot.getRobotId()) != null) {
+                throw new IllegalArgumentException("There can't be two robots with the same robot id.");
+            }
+            this.robots.put(robot.getRobotId(), robot);
+        }
+        this.deadRobots = new ArrayList<>();
+        this.boardWidth = tiles.getWidth();
+        this.boardHeight = tiles.getHeight();
+        this.walls = walls;
+        this.tiles = tiles;
     }
 
     /**
-     * Removes a dead robot from the board over to the dead robot list.
+     * Gets the height of the board
+     * @return The height of the board
+     */
+    public int getBoardHeight() {
+        return boardHeight;
+    }
+
+    /**
+     * Gets the width of the board
+     * @return The width of the board
+     */
+    public int getBoardWidth() {
+        return boardWidth;
+    }
+
+    /**
+     * Moves all dead robots to their backups and makes them part of the board again
+     */
+    public void respawnRobots() {
+        //TODO: Account for several robots re-spawning at same backup
+        for (Robot robot : deadRobots) {
+            robot.setPosition(robot.getBackupPosition());
+            robots.put(robot.getRobotId(), robot);
+        }
+        deadRobots = new ArrayList<>();
+    }
+
+    /**
+     * Removes a dead robot from the board over to the dead robot list
      * @param robot the dead robot
      */
-    public void  removeDeadRobotFromBoard(Robot robot){
-        aliveRobots.remove(robot);
+    public void removeDeadRobotFromBoard(Robot robot) {
+        robots.remove(robot.getRobotId());
         deadRobots.add(robot);
     }
+
+    /**
+     * Moves a robot one unit in a specified direction
+     * @param robotID ID of the robot to move
+     * @param direction The direction to move the robot
+     * @return True if the robot moved away from its old position
+     */
+    public boolean moveRobot(RobotID robotID, Direction direction) {
+        Robot robot = robots.get(robotID);
+        Position robotPosition = robot.getPosition();
+        Position newPosition = getNewPosition(robotPosition, direction);
+        //Robot tried to go outside of the map. Kill it.
+        if (newPosition.getXCoordinate() < 0
+                || newPosition.getXCoordinate() >= boardWidth
+                || newPosition.getYCoordinate() < 0
+                || newPosition.getYCoordinate() >= boardHeight) {
+            killRobot(robot);
+            return true;
+        }
+        //There is a wall blocking the robot. It can't proceed.
+        if (hasWallFacing(robotPosition, direction) ||
+                hasWallFacing(newPosition, Direction.getReverseDirection(direction))) {
+            return false;
+        }
+        //If another robot is blocking this robot's path, try to shove it.
+        if (hasRobotOnPosition(newPosition)) {
+            RobotID otherRobotID = getRobotOnPosition(newPosition);
+            if (otherRobotID != null && !moveRobot(otherRobotID, direction)) {
+                return false;
+            }
+        }
+        //Some tiles may kill the robot if stepped on.
+        Tile tileRobotStepsOn = tiles.getElement(newPosition.getXCoordinate(), newPosition.getYCoordinate());
+        if (tileRobotStepsOn == null) {
+            throw new IllegalArgumentException("The game board is missing a tile. This should not happen.");
+        }
+        TileType tileTypeRobotStepsOn = tileRobotStepsOn.getTileType();
+        if (tileTypeRobotStepsOn == TileType.HOLE || tileTypeRobotStepsOn == TileType.DEATH_TILE) {
+            killRobot(robot);
+            return true;
+        }
+        robot.setPosition(newPosition);
+        return true;
+    }
+
+    /**
+     * Kills the robot
+     *
+     * If the robot steps outside of the board, steps on a hole or takes too much damage, this method should be used to
+     * properly dispose of the robot until the next round.
+     *
+     * @param robot The robot to kill
+     */
+    private void killRobot(Robot robot) {
+        //TODO: Must remove a life from the robot/player
+        removeDeadRobotFromBoard(robot);
+    }
+
+    /**
+     * Returns a robot id for a robot on a specific position if such a robot exists
+     * @param position The position to check
+     * @return The robot id of the robot on the position or null if there is no robot there
+     */
+    private RobotID getRobotOnPosition(Position position) {
+        for (RobotID robotID : robots.keySet()) {
+            Robot robot = robots.get(robotID);
+            if (position.equals(robot.getPosition())) {
+                return robotID;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Checks whether there exists a robot on a specific position
+     * @param position The position to check
+     * @return True if there is a robot on the specified position
+     */
+    private boolean hasRobotOnPosition(Position position) {
+        return getRobotOnPosition(position) != null;
+    }
+
+    /**
+     * Checks whether a position has a wall facing a specific direction
+     * @param position The position to check
+     * @param direction The direction of the wall to check for
+     * @return True if there is a wall on the position facing the input direction
+     */
+    private boolean hasWallFacing(Position position, Direction direction) {
+        Wall wall = walls.getElement(position.getXCoordinate(), position.getYCoordinate());
+        if (wall == null) {
+            return false;
+        }
+        switch (wall.getDirection()) {
+            case NORTH_EAST:
+                return direction == Direction.NORTH || direction == Direction.EAST;
+            case NORTH_WEST:
+                return direction == Direction.NORTH || direction == Direction.WEST;
+            case SOUTH_WEST:
+                return direction == Direction.SOUTH || direction == Direction.WEST;
+            case SOUTH_EAST:
+                return direction == Direction.SOUTH || direction == Direction.EAST;
+        }
+        return wall.getDirection() == direction;
+    }
+
+    /**
+     * Gets the position 1 unit in a specific direction from another position
+     * @param oldPosition The old/current position of the element
+     * @param direction The direction to move the element
+     * @return The new position of the element
+     */
+    private Position getNewPosition(Position oldPosition, Direction direction) {
+        //TODO: Make sure we're accounting for the flipped y axis in libgdx
+        switch (direction) {
+            case NORTH:
+                return new Position(oldPosition.getXCoordinate(), oldPosition.getYCoordinate() + 1);
+            case SOUTH:
+                return new Position(oldPosition.getXCoordinate(), oldPosition.getYCoordinate() - 1);
+            case EAST:
+                return new Position(oldPosition.getXCoordinate() + 1, oldPosition.getYCoordinate());
+            case WEST:
+                return new Position(oldPosition.getXCoordinate() - 1, oldPosition.getYCoordinate());
+            default:
+                throw new IllegalArgumentException("It's not possible to move in that direction.");
+        }
+    }
 }
diff --git a/src/test/java/inf112/fiasko/roborally/objects/BoardTest.java b/src/test/java/inf112/fiasko/roborally/objects/BoardTest.java
new file mode 100644
index 0000000..02c3aad
--- /dev/null
+++ b/src/test/java/inf112/fiasko/roborally/objects/BoardTest.java
@@ -0,0 +1,65 @@
+package inf112.fiasko.roborally.objects;
+
+import inf112.fiasko.roborally.element_properties.*;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BoardTest {
+    private Grid<Tile> tileGrid;
+    private Grid<Wall> wallGrid;
+    private Position someValidPosition;
+    private List<Robot> robotList;
+    private Board board;
+
+    @Before
+    public void setUp() {
+        tileGrid = new Grid<>(5, 5, new Tile(TileType.TILE, Direction.NORTH));
+        wallGrid = new Grid<>(5, 5);
+        someValidPosition = new Position(2, 2);
+        robotList = new ArrayList<>();
+        robotList.add(new Robot(RobotID.ROBOT_1, someValidPosition));
+        wallGrid.setElement(2, 3, new Wall(WallType.WALL_NORMAL, Direction.SOUTH));
+        wallGrid.setElement(2, 2, new Wall(WallType.WALL_NORMAL, Direction.EAST));
+        wallGrid.setElement(1, 2, new Wall(WallType.WALL_CORNER, Direction.NORTH_EAST));
+        board = new Board(tileGrid, wallGrid, robotList);
+    }
+
+    @Test
+    public void robotCanMove() {
+        assertTrue(board.moveRobot(RobotID.ROBOT_1, Direction.SOUTH));
+    }
+
+    @Test
+    public void robotIsStoppedByWallOnSameTile() {
+        assertFalse(board.moveRobot(RobotID.ROBOT_1, Direction.EAST));
+    }
+
+    @Test
+    public void robotIsStoppedByWallOnAdjacentTile() {
+        assertFalse(board.moveRobot(RobotID.ROBOT_1, Direction.NORTH));
+    }
+
+    @Test
+    public void robotIsStoppedByCornerWall() {
+        assertFalse(board.moveRobot(RobotID.ROBOT_1, Direction.WEST));
+    }
+
+    @Test (expected = IllegalArgumentException.class)
+    public void gridsOfDifferentSizeThrowsError() {
+        IGrid<Wall> wallGrid = new Grid<>(1, 1);
+        new Board(tileGrid, wallGrid, robotList);
+    }
+
+    @Test (expected = IllegalArgumentException.class)
+    public void multipleRobotsWithSameIDThrowsError() {
+        Robot robot = new Robot(RobotID.ROBOT_1, someValidPosition);
+        robotList.add(robot);
+        new Board(tileGrid, wallGrid, robotList);
+    }
+}