rename packages lower case
This commit is contained in:
394
src/main/java/server/GameBoard.java
Normal file
394
src/main/java/server/GameBoard.java
Normal file
@@ -0,0 +1,394 @@
|
||||
package server;
|
||||
|
||||
import server.cards.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import server.cards.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents the physical game board: the two card rows, the offering tiles,
|
||||
* the turn-order tile, and the card deck.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Owning and mutating the top and bottom card rows.
|
||||
* - Owning the OfferingTiles and the TurnTile.
|
||||
* - Performing end-of-round row transitions and era-change building swaps.
|
||||
* - Exposing visible EventCards to Game for resolution.
|
||||
*
|
||||
* NOT responsible for:
|
||||
* - Resolving events (that is EventsSolver's job, called by Game).
|
||||
* - Applying player rewards/penalties (that is Game's job).
|
||||
* - Holding a reference to the player list.
|
||||
*/
|
||||
public class GameBoard {
|
||||
private static final Logger logger = LogManager.getLogger(GameBoard.class);
|
||||
// -------------------------------------------------------------------------
|
||||
// Fields
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private Era era;
|
||||
|
||||
private final CardDeck cardDeck;
|
||||
private final List<Card> topRow;
|
||||
private final List<Card> bottomRow;
|
||||
private final List<OfferingTile> offeringTiles;
|
||||
private final TurnTile turnTile;
|
||||
private final BuildingManager buildingManager;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates the board for a new game.
|
||||
* Call {@link #setupInitialRows(int)} and {@link #initOfferingTiles(int)}
|
||||
* separately after construction (mirrors the rulebook setup steps).
|
||||
*/
|
||||
public GameBoard(Era startingEra, CardDeck cardDeck, int numPlayers) {
|
||||
this.era = startingEra;
|
||||
this.cardDeck = cardDeck;
|
||||
this.topRow = new ArrayList<>();
|
||||
this.bottomRow = new ArrayList<>();
|
||||
this.offeringTiles = new ArrayList<>();
|
||||
this.turnTile = new TurnTile(numPlayers);
|
||||
this.buildingManager = new BuildingManager();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Setup
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates and places the correct OfferingTiles for the given player count.
|
||||
*
|
||||
* Tile layout (from the rulebook):
|
||||
* orderId 0 → tile A: 3 food (5-player only)
|
||||
* orderId 1 → tile B: 1 DOWN (all)
|
||||
* orderId 2 → tile C: 1 UP (all)
|
||||
* orderId 3 → tile D: 2 DOWN (3+ players)
|
||||
* orderId 4 → tile E: 1 DOWN 1 UP (all)
|
||||
* orderId 5 → tile F: 2 UP (all)
|
||||
* orderId 6 → tile G: 1 DOWN 2 UP (4+ players)
|
||||
*
|
||||
* With n players, exactly n tiles are used, starting from tile A if
|
||||
* 5 players, otherwise starting from tile B.
|
||||
*/
|
||||
public void initOfferingTiles(int numPlayers) {
|
||||
offeringTiles.clear();
|
||||
switch (numPlayers) {
|
||||
case 2:
|
||||
offeringTiles.add(new OfferingTile(1));
|
||||
offeringTiles.add(new OfferingTile(2));
|
||||
offeringTiles.add(new OfferingTile(4));
|
||||
offeringTiles.add(new OfferingTile(5));
|
||||
break;
|
||||
case 3:
|
||||
offeringTiles.add(new OfferingTile(1));
|
||||
offeringTiles.add(new OfferingTile(2));
|
||||
offeringTiles.add(new OfferingTile(3));
|
||||
offeringTiles.add(new OfferingTile(4));
|
||||
offeringTiles.add(new OfferingTile(5));
|
||||
break;
|
||||
case 4:
|
||||
offeringTiles.add(new OfferingTile(1));
|
||||
offeringTiles.add(new OfferingTile(2));
|
||||
offeringTiles.add(new OfferingTile(3));
|
||||
offeringTiles.add(new OfferingTile(4));
|
||||
offeringTiles.add(new OfferingTile(5));
|
||||
offeringTiles.add(new OfferingTile(6));
|
||||
break;
|
||||
case 5:
|
||||
offeringTiles.add(new OfferingTile(0));
|
||||
offeringTiles.add(new OfferingTile(1));
|
||||
offeringTiles.add(new OfferingTile(2));
|
||||
offeringTiles.add(new OfferingTile(3));
|
||||
offeringTiles.add(new OfferingTile(4));
|
||||
offeringTiles.add(new OfferingTile(5));
|
||||
offeringTiles.add(new OfferingTile(6));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Find the oddertingTile selected by Player
|
||||
public OfferingTile getOfferingTile(Player p){
|
||||
OfferingTile offering = offeringTiles.stream().filter(o -> p.equals(o.getOccupant())).findFirst().orElse(null);
|
||||
return offering;
|
||||
}
|
||||
/**
|
||||
* Draws the initial two rows of cards according to rulebook setup steps 4-5:
|
||||
*
|
||||
* Bottom row: draw cards one at a time until (numPlayers + 1) Character cards
|
||||
* have been placed. Any Event card drawn is placed in the top row instead.
|
||||
* Top row: fill to (numPlayers + 4) with additional draws (accounting for
|
||||
* any events already placed there from the bottom-row draw).
|
||||
*/
|
||||
public void setupInitialRows(int numPlayers) {
|
||||
topRow.clear();
|
||||
bottomRow.clear();
|
||||
|
||||
// Draw bottom row — events are bumped to top row
|
||||
while (bottomRow.size() < numPlayers + 1) {
|
||||
Card card = cardDeck.drawTribeOne();
|
||||
if (card instanceof EventCard) {
|
||||
topRow.add(card);
|
||||
logger.info("Setup: Event card " + card.getCardId() + " moved to top row.");
|
||||
} else {
|
||||
bottomRow.add(card);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill top row up to numPlayers + 4
|
||||
int needed = (numPlayers + 4) - topRow.size();
|
||||
if (needed > 0) {
|
||||
topRow.addAll(cardDeck.drawTribe(needed));
|
||||
}
|
||||
|
||||
// Add all Building Era.I on top row 2P 1 3-4-5 2
|
||||
topRow.add(cardDeck.drawBuildingOne(Era.I));
|
||||
if (numPlayers > 2){
|
||||
topRow.add(cardDeck.drawBuildingOne(Era.I));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// End-of-round row management (rulebook "Fine del Round" steps 2-4)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs the three row-management steps at the end of every round:
|
||||
*
|
||||
* Step 2 — Discard all Character and Event cards from the bottom row.
|
||||
* Building cards stay.
|
||||
* Step 3 — Move all Character and Event cards from the top row down to
|
||||
* the bottom row. Building cards stay in the top row.
|
||||
* Step 4 — Draw (numPlayers + 4) new cards into the top row.
|
||||
*
|
||||
* @return true if the newly drawn cards contain a card from the next Era,
|
||||
* signalling that {@link #triggerEraChange()} should be called.
|
||||
*/
|
||||
public boolean advanceRows(int numPlayers) {
|
||||
|
||||
Era eraBeforeDraw = this.era;
|
||||
|
||||
// Step 2: discard non-building cards from the bottom row
|
||||
bottomRow.removeIf(
|
||||
c -> !(c instanceof BuildingCard)
|
||||
);
|
||||
|
||||
// Step 3: move non-building cards from top row down to bottom row
|
||||
Iterator<Card> it = topRow.iterator();
|
||||
while (it.hasNext()) {
|
||||
Card c = it.next();
|
||||
if (!(c instanceof BuildingCard)) {
|
||||
logger.debug("move card from top to bottom " + c);
|
||||
bottomRow.add(c);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: draw fresh cards into the top row
|
||||
List<Card> newCards = cardDeck.drawTribe(numPlayers + 4);
|
||||
topRow.addAll(newCards);
|
||||
logger.info("NEW CARDS ON TOP {} " , newCards);
|
||||
return isNewEraRevealed(eraBeforeDraw, newCards);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the newly drawn cards belongs to an era
|
||||
* that is strictly later than the current one.
|
||||
*/
|
||||
private boolean isNewEraRevealed(Era currentEra, List<Card> newCards) {
|
||||
for (Card c : newCards) {
|
||||
Era cardEra = eraOf(c);
|
||||
if (cardEra != null && cardEra.ordinal() > currentEra.ordinal()) {
|
||||
logger.info("FOUND NEW ERA {} " , cardEra);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Era change (rulebook "Inizio della Nuova Era")
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Advances the era and performs the three building-row transitions:
|
||||
*
|
||||
* Step 1 — (Era III only) Discard all Building cards from the bottom row.
|
||||
* Step 2 — Move all Building cards from the top row to the bottom row
|
||||
* (to the right of the Tribe cards). [Era II and III]
|
||||
* Step 3 — Place the new era's Building cards face-up in the top row.
|
||||
* [Era II and III]
|
||||
*
|
||||
* @return the new Era after the transition
|
||||
*/
|
||||
public Era triggerEraChange(int numPlayers) {
|
||||
era = era.next(); // Era.I → II → III → FINAL
|
||||
logger.info("ERA CHANGED → {} ", era);
|
||||
|
||||
// Step 1 (Era III only): remove old era buildings from the bottom row
|
||||
if (era == Era.III) {
|
||||
bottomRow.removeIf(c -> c instanceof BuildingCard);
|
||||
}
|
||||
|
||||
// Step 2: move buildings from top row to bottom row
|
||||
List<BuildingCard> descending = new ArrayList<>();
|
||||
Iterator<Card> it = topRow.iterator();
|
||||
while (it.hasNext()) {
|
||||
Card c = it.next();
|
||||
if (c instanceof BuildingCard) {
|
||||
descending.add((BuildingCard) c);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
bottomRow.addAll(descending);
|
||||
logger.info("event=MOVED_BUILING era={} count={} cards={}", era, descending.size(), descending);
|
||||
|
||||
// Step 3: place the new era's building cards in the top row
|
||||
int n = BuildingRules.getBuildingCards(numPlayers, era);
|
||||
List<Card> newEraBuildings = cardDeck.drawBuilding(n , era);
|
||||
topRow.addAll(newEraBuildings);
|
||||
|
||||
logger.info("event=ADD_BUILDINGS era={} count={} cards={}", era, n, newEraBuildings);
|
||||
return era;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Offering tiles
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempts to place a player's totem on an offering tile.
|
||||
*
|
||||
* @return true if successful; false if the tile was already occupied
|
||||
*/
|
||||
public boolean placeTotem(Player player, OfferingTile tile, TurnTile turntile) {
|
||||
if (!tile.isEmpty()) return false;
|
||||
tile.setOccupant(player);
|
||||
turntile.leaveTurnTile(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all totems from offering tiles. Call at the start of each new round.
|
||||
*/
|
||||
public void clearOfferingTiles() {
|
||||
for (OfferingTile tile : offeringTiles) {
|
||||
tile.removeOccupant();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Card removal (called by Game when a player takes a card)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes a card from the top row by ID and returns it.
|
||||
*
|
||||
* @return the card, or null if no card with that ID exists in the top row
|
||||
*/
|
||||
public Card takeFromTopRow(int cardId) {
|
||||
return takeFromRow(topRow, cardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a card from the bottom row by ID and returns it.
|
||||
*
|
||||
* @return the card, or null if no card with that ID exists in the bottom row
|
||||
*/
|
||||
public Card takeFromBottomRow(int cardId) {
|
||||
return takeFromRow(bottomRow, cardId);
|
||||
}
|
||||
|
||||
private Card takeFromRow(List<Card> row, int cardId) {
|
||||
Iterator<Card> it = row.iterator();
|
||||
while (it.hasNext()) {
|
||||
Card c = it.next();
|
||||
if (c.getCardId() == cardId) {
|
||||
it.remove();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Event visibility
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns all EventCards currently in the bottom row, sorted by era
|
||||
* (ascending) so they are resolved in the correct order.
|
||||
* Sustainment is NOT sorted last here — that is Game's responsibility.
|
||||
*/
|
||||
public List<EventCard> getVisibleEvents() {
|
||||
List<EventCard> events = new ArrayList<>();
|
||||
for (Card c : bottomRow) {
|
||||
if (c instanceof EventCard) {
|
||||
events.add((EventCard) c);
|
||||
}
|
||||
}
|
||||
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns EventCards from BOTH rows.
|
||||
* Used during the final round when all visible events must be resolved.
|
||||
*/
|
||||
public List<EventCard> getAllVisibleEvents() {
|
||||
List<EventCard> events = new ArrayList<>();
|
||||
for (Card c : bottomRow) {
|
||||
if (c instanceof EventCard) events.add((EventCard) c);
|
||||
}
|
||||
for (Card c : topRow) {
|
||||
if (c instanceof EventCard) events.add((EventCard) c);
|
||||
}
|
||||
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/** Extracts the Era from a card, returning null for building cards. */
|
||||
private Era eraOf(Card c) {
|
||||
if (c instanceof CharacterCard) return ((CharacterCard) c).getEra();
|
||||
if (c instanceof EventCard) return ((EventCard) c).getEra();
|
||||
return null;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Getters
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public Era getEra() { return era; }
|
||||
public List<Card> getTopRow() { return topRow; }
|
||||
public List<Card> getBottomRow() { return bottomRow; }
|
||||
public List<OfferingTile> getOfferingTiles() { return offeringTiles; }
|
||||
public TurnTile getTurnTile() { return turnTile; }
|
||||
public BuildingManager getBuildingManager() { return buildingManager; }
|
||||
public CardDeck getCardDeck() { return cardDeck; }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GameBoard{" +
|
||||
"era=" + era +
|
||||
",\n offeringTiles=" + offeringTiles +
|
||||
",\n turnTile=" + turnTile +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user