rename packages lower case

This commit is contained in:
2026-04-13 16:00:11 +02:00
parent f79b511843
commit 58a8002eb4
43 changed files with 127 additions and 140 deletions

View 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 +
'}';
}
}