Skip to content

Commit

Permalink
Fix huge bug in ZonePartitions.Builder.connectSides and WIP on Board
Browse files Browse the repository at this point in the history
  • Loading branch information
Mw3y committed Mar 13, 2024
1 parent 95a6498 commit 4a8024d
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 43 deletions.
278 changes: 247 additions & 31 deletions src/ch/epfl/chacun/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

/**
* Represents the board of the game.
* <p>
* This board will have a size of 625 (25×25) elements, and will be organized in reading order,
* starting from the cell at the top left of the board, and going through the rows before the columns.
*
* @author Maxence Espagnet (sciper: 372808)
* @author Balthazar Baillat (sciper: 373420)
*/
public class Board {
public final class Board {

public static final int REACH = 12;
public static final Board EMPTY = new Board();
public static final Board EMPTY = new Board(new PlacedTile[625], new int[0], ZonePartitions.EMPTY, Set.of());
private static final int SIZE = 25;
private final PlacedTile[] placedTiles;
private final int[] tileIndices;
private final ZonePartitions zonePartitions;
Expand All @@ -21,74 +25,286 @@ public class Board {
/**
* Private constructor of the board to initialize values.
*/
private Board() {
this.placedTiles = new PlacedTile[625];
this.tileIndices = new int[0];
this.zonePartitions = ZonePartitions.EMPTY;
this.cancelledAnimals = new HashSet<>();
private Board(PlacedTile[] placedTiles, int[] tileIndices, ZonePartitions zonePartitions, Set<Animal> cancelledAnimals) {
this.placedTiles = placedTiles;
this.tileIndices = tileIndices;
this.zonePartitions = zonePartitions;
this.cancelledAnimals = cancelledAnimals;
}

/**
* Calculates the row major index of the given position.
*
* @param pos the position
* @return the row major index of the given position
*/
private int calculateRowMajorIndex(Pos pos) {
int originIndex = (placedTiles.length - 1) / 2;
return originIndex + pos.x() + pos.y() * SIZE;
}

/**
* Returns the tile in the given position, or null if there is none or if the position is off the board.
*
* @param pos the position of the tile
* @return the tile in the given position, or null if there is none or if the position is off the board
*/
public PlacedTile tileAt(Pos pos) {
// Implement method
return null;
int index = calculateRowMajorIndex(pos);
if (index < 0 || index >= placedTiles.length) {
return null;
}
return placedTiles[index];
}

/**
* Returns the tile with the given id.
*
* @param tileId the id of the tile
* @return the tile with the given id, if found
* @throws IllegalArgumentException if no tile with the given id is found
*/
public PlacedTile tileWithId(int tileId) {
// Implement method
return null;
for (int placedTileIndex : tileIndices) {
PlacedTile placedTile = placedTiles[placedTileIndex];
if (placedTile.tile().id() == tileId) {
return placedTile;
}
}
throw new IllegalArgumentException("No tile with the given id found.");
}

/**
* Returns the cancelled animals.
* <p>Cancelled animals can be, for example, deer eaten by smilodons.
*
* @return the set of cancelled animals
*/
public Set<Animal> cancelledAnimals() {
// Defensive copy
return new HashSet<>(cancelledAnimals);
}

/**
* Return all the occupants on the tiles placed on the board.
*
* @return the set of all occupants on the board
*/
public Set<Occupant> occupants() {
// Implement method
return null;
Set<Occupant> occupants = new HashSet<>();
for (int placedTileIndex : tileIndices) {
occupants.add(placedTiles[placedTileIndex].occupant());
}
return occupants;
}

/**
* Returns the forest area containing the given zone.
*
* @param forest the forest zone
* @return the area containing the given zone
*/
public Area<Zone.Forest> forestArea(Zone.Forest forest) {
// Implement method
return null;
return zonePartitions.forests().areaContaining(forest);
}

/**
* Returns the meadow area containing the given zone.
*
* @param meadow the meadow zone
* @return the area containing the given zone
*/
public Area<Zone.Meadow> meadowArea(Zone.Meadow meadow) {
// Implement method
return null;
return zonePartitions.meadows().areaContaining(meadow);
}

/**
* Returns the river area containing the given zone.
*
* @param riverZone the river zone
* @return the area containing the given zone
*/
public Area<Zone.River> riverArea(Zone.River riverZone) {
// Implement method
return null;
return zonePartitions.rivers().areaContaining(riverZone);
}

public Area<Zone.Water> riverSystemArea(Zone.Water water) {
// Implement method
return null;
/**
* Returns the river system area containing the given zone.
*
* @param waterZone the water zone
* @return the area containing the given zone
*/
public Area<Zone.Water> riverSystemArea(Zone.Water waterZone) {
return zonePartitions.riverSystems().areaContaining(waterZone);
}

/**
* Returns all meadow areas of the board.
*
* @return the set of all meadow areas of the board
*/
public Set<Area<Zone.Meadow>> meadowAreas() {
// Implement method
return null;
return zonePartitions.meadows().areas();
}

/**
* Returns all river system areas of the board.
*
* @return the set of all river system areas of the board
*/
public Set<Area<Zone.Water>> riverSystemAreas() {
// Implement method
return null;
return zonePartitions.riverSystems().areas();
}

/**
* Returns the meadow adjacent to the given zone, in the form of an area which contains only
* the zones of this meadow but all the occupants of the complete meadow,
* and which, for simplicity, has no open connections.
*
* @param pos the position of the tile
* @param meadowZone the meadow zone
* @return the meadow adjacent to the given zone
*/
public Area<Zone.Meadow> adjacentMeadow(Pos pos, Zone.Meadow meadowZone) {
// Implement method
return null;
Area<Zone.Meadow> originalArea = meadowArea(meadowZone);
Set<Zone.Meadow> adjacentZones = new HashSet<>();
for (Zone.Meadow zone : originalArea.zones()) {
// Get the placed tile with the given id
PlacedTile tile = tileWithId(zone.tileId());
// Check if the given tile is adjacent to the given one
int dX = Math.abs(pos.x() - tile.pos().x());
int dY = Math.abs(pos.y() - tile.pos().y());
if (dX <= 1 && dY <= 1) {
adjacentZones.add(zone);
}
}
// Create the adjacent area, with the same occupants
return new Area<>(adjacentZones, originalArea.occupants(), 0);
}

/**
* Return the number of occupants of the given type belonging to the given player on the board.
*
* @param player the player
* @param occupantKind the occupant kind
* @return the number of occupants of the given type belonging to the given player on the board
*/
public int occupantCount(PlayerColor player, Occupant.Kind occupantKind) {
// Implement method
return 0;
int occupantCount = 0;
for (int placedTileIndex : tileIndices) {
PlacedTile placedTile = placedTiles[placedTileIndex];
if (placedTile.occupant().kind() == occupantKind && placedTile.placer() == player) {
occupantCount++;
}
}
return occupantCount;
}

/**
* Returns the set of all insertion positions on the board.
* <p> An insertion position is a position on the board where a tile can be placed.
*
* @return the set of all insertion positions on the board
*/
public Set<Pos> insertionPositions() {
// Implement method
Set<Pos> insertionPositions = new HashSet<>();
for (int placedTileIndex : tileIndices) {
PlacedTile placedTile = placedTiles[placedTileIndex];
for (Direction direction : Direction.ALL) {
Pos neighbor = placedTile.pos().neighbor(direction);
if (tileAt(neighbor) == null) {
insertionPositions.add(neighbor);
}
}
}
return insertionPositions;
}

/**
* Returns the last placed tile on the board.
* <p>
* It can be the starting tile if the first normal tile has not yet been placed, or null if the board is empty
*
* @return the last placed tile on the board
*/
public PlacedTile lastPlacedTile() {
if (tileIndices.length > 0) {
return placedTiles[tileIndices[tileIndices.length - 1]];
}
return null;
}

public Set<Area<Zone.Forest>> forestsClosedByLastTile() {
PlacedTile lastTile = lastPlacedTile();
// TODO: implement this method
return Set.of();
}

public Set<Area<Zone.River>> riversClosedByLastTile() {
// TODO: implement this method
return Set.of();
}

/**
* Returns true if the given placed tile can be added to the board.
* <p>
* i.e. if its position is an insertion position and every edge of the tile that touches
* an edge of a tile already placed is of the same kind as it.
*
* @param tile the placed tile to check
* @return true if the given placed tile can be added to the board
*/
public boolean canAddTile(PlacedTile tile) {
// Check if the tile cannot be placed on the board
if (!insertionPositions().contains(tile.pos())) {
return false;
}
// Check for potential conflicts with adjacent tiles
for (Direction direction : Direction.ALL) {
Pos neighbor = tile.pos().neighbor(direction);
PlacedTile neighborTile = tileAt(neighbor);
if (neighborTile != null) {
TileSide neighborSide = neighborTile.side(direction.opposite());
TileSide tileSide = tile.side(direction);
if (!tileSide.isSameKindAs(neighborSide)) {
return false;
}
}
}
return true;
}

public boolean couldPlaceTile(Tile tile) {
// TODO: implement this method
return false;
}

/**
* Returns an identical board, but with the given tile in addition.
*
* @param tile the tile to place
* @return an identical board, but with the given tile in addition
* @throws IllegalArgumentException if the board is not empty and the given tile cannot be added to the board
*/
public Board withNewTile(PlacedTile tile) {
// Check if the tile can be placed on the board
if (placedTiles.length != 0 && !canAddTile(tile)) {
throw new IllegalArgumentException("The tile cannot be placed on the board.");
}
// Create the new placed tiles array
int newTileIndex = calculateRowMajorIndex(tile.pos());
PlacedTile[] newPlacedTiles = placedTiles.clone();
newPlacedTiles[newTileIndex] = tile;
// Create the new tile indices array
int[] newTileIndices = new int[tileIndices.length + 1];
System.arraycopy(tileIndices, 0, newTileIndices, 0, tileIndices.length);
newTileIndices[tileIndices.length] = newTileIndex;
// Create a new board with the new tile
return new Board(newPlacedTiles, newTileIndices, zonePartitions, Set.copyOf(cancelledAnimals));
}

public Board withOccupant(Occupant occupant) {
// TODO: implement this method
return null;
}
}
29 changes: 17 additions & 12 deletions src/ch/epfl/chacun/ZonePartitions.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,24 @@ public void addTile(Tile tile) {
switch (zone) {
case Zone.Forest f -> forests.addSingleton(f, openConnections[zone.localId()]);
case Zone.Meadow m -> meadows.addSingleton(m, openConnections[zone.localId()]);
case Zone.River r when !r.hasLake() ->
rivers.addSingleton(r, openConnections[r.localId()]);
case Zone.River r -> {
// If a river has a lake, the river has in fact one less open connection
rivers.addSingleton(r, openConnections[r.localId()] - 1);
riverSystems.addSingleton(r, openConnections[r.localId()]);
// Prevent the same lake from being added twice
if (!lakes.contains(r.lake())) {
riverSystems.addSingleton(r.lake(), openConnections[r.lake().localId()]);
lakes.add(r.lake());
// Check if the river has a lake
if (r.hasLake()) {
// If a river has a lake, the river has in fact one less open connection
rivers.addSingleton(r, openConnections[r.localId()] - 1);
riverSystems.addSingleton(r, openConnections[r.localId()]);
// Prevent the same lake from being added twice
if (!lakes.contains(r.lake())) {
riverSystems.addSingleton(r.lake(), openConnections[r.lake().localId()]);
lakes.add(r.lake());
}
// Create the union between the river and the lake
riverSystems.union(r, r.lake());
}
else {
rivers.addSingleton(r, openConnections[r.localId()]);
riverSystems.addSingleton(r, openConnections[r.localId()]);
}
// Create the union between the river and the lake
riverSystems.union(r, r.lake());
}
// A lake should not be in the side zones
default -> throw new IllegalArgumentException("A lake shouldn't be in the side zones");
Expand All @@ -96,7 +101,6 @@ public void addTile(Tile tile) {
* @param s2 the second tile side
* @throws IllegalArgumentException if the two given tile sides are not of the same kind
*/

public void connectSides(TileSide s1, TileSide s2) {
switch (s1) {
case TileSide.Meadow(Zone.Meadow m1) when s2 instanceof TileSide.Meadow(Zone.Meadow m2) -> {
Expand All @@ -108,6 +112,7 @@ public void connectSides(TileSide s1, TileSide s2) {
case TileSide.River(
Zone.Meadow m3, Zone.River r1, Zone.Meadow m4
) when s2 instanceof TileSide.River(Zone.Meadow m5, Zone.River r2, Zone.Meadow m6) -> {
riverSystems.union(r1, r2);
rivers.union(r1, r2);
meadows.union(m3, m6);
meadows.union(m4, m5);
Expand Down
Loading

0 comments on commit 4a8024d

Please sign in to comment.