summaryrefslogtreecommitdiff
path: root/game-engine
diff options
context:
space:
mode:
Diffstat (limited to 'game-engine')
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java233
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/scoring/ScoreBoard.java6
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Game.kt178
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt13
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt2
-rw-r--r--game-engine/src/test/java/org/luxons/sevenwonders/game/GameTest.java2
7 files changed, 197 insertions, 239 deletions
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java
deleted file mode 100644
index 1d88fb9f..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package org.luxons.sevenwonders.game;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.luxons.sevenwonders.game.api.Action;
-import org.luxons.sevenwonders.game.api.HandCard;
-import org.luxons.sevenwonders.game.api.PlayerMove;
-import org.luxons.sevenwonders.game.api.PlayerTurnInfo;
-import org.luxons.sevenwonders.game.api.Table;
-import org.luxons.sevenwonders.game.boards.Board;
-import org.luxons.sevenwonders.game.cards.Card;
-import org.luxons.sevenwonders.game.cards.CardBack;
-import org.luxons.sevenwonders.game.cards.Decks;
-import org.luxons.sevenwonders.game.cards.Hands;
-import org.luxons.sevenwonders.game.effects.SpecialAbility;
-import org.luxons.sevenwonders.game.moves.InvalidMoveException;
-import org.luxons.sevenwonders.game.moves.Move;
-import org.luxons.sevenwonders.game.scoring.ScoreBoard;
-
-public class Game {
-
- private static final int LAST_AGE = 3;
-
- private final long id;
-
- private final Settings settings;
-
- private final int nbPlayers;
-
- private final Table table;
-
- private final Decks decks;
-
- private final List<Card> discardedCards;
-
- private final Map<Integer, Move> preparedMoves;
-
- private Map<Integer, PlayerTurnInfo> currentTurnInfo;
-
- private Hands hands;
-
- public Game(long id, Settings settings, int nbPlayers, List<Board> boards, Decks decks) {
- this.id = id;
- this.settings = settings;
- this.nbPlayers = nbPlayers;
- this.table = new Table(boards);
- this.decks = decks;
- this.discardedCards = new ArrayList<>();
- this.currentTurnInfo = new HashMap<>();
- this.preparedMoves = new HashMap<>();
- startNewAge();
- }
-
- public long getId() {
- return id;
- }
-
- public Settings getSettings() {
- return settings;
- }
-
- private void startNewAge() {
- table.increaseCurrentAge();
- hands = decks.deal(table.getCurrentAge(), table.getNbPlayers());
- startNewTurn();
- }
-
- private void startNewTurn() {
- currentTurnInfo.clear();
- for (int i = 0; i < nbPlayers; i++) {
- currentTurnInfo.put(i, createPlayerTurnInfo(i));
- }
- }
-
- private PlayerTurnInfo createPlayerTurnInfo(int playerIndex) {
- List<HandCard> hand = hands.createHand(table, playerIndex);
- Action action = determineAction(hand, table.getBoard(playerIndex));
-
- List<Card> neighbourGuildCards = Collections.emptyList();
- if (action == Action.PICK_NEIGHBOR_GUILD) {
- neighbourGuildCards = table.getNeighbourGuildCards(playerIndex);
- }
-
- return new PlayerTurnInfo(playerIndex, table, action, hand, neighbourGuildCards);
- }
-
- public Collection<PlayerTurnInfo> getCurrentTurnInfo() {
- return currentTurnInfo.values();
- }
-
- private Action determineAction(List<HandCard> hand, Board board) {
- if (endOfGameReached() && board.hasSpecial(SpecialAbility.COPY_GUILD)) {
- List<Card> neighbourGuildCards = table.getNeighbourGuildCards(board.getPlayerIndex());
- return neighbourGuildCards.isEmpty() ? Action.WAIT : Action.PICK_NEIGHBOR_GUILD;
- } else if (hand.size() == 1 && board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
- return Action.PLAY_LAST;
- } else if (hand.size() == 2 && board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
- return Action.PLAY_2;
- } else if (hand.isEmpty()) {
- return Action.WAIT;
- } else {
- return Action.PLAY;
- }
- }
-
- public CardBack prepareMove(int playerIndex, PlayerMove playerMove) throws InvalidMoveException {
- Card card = decks.getCard(table.getCurrentAge(), playerMove.getCardName());
- Move move = playerMove.getType().resolve(playerIndex, card, playerMove);
- validate(move);
- preparedMoves.put(playerIndex, move);
- return card.getBack();
- }
-
- private void validate(Move move) throws InvalidMoveException {
- List<Card> hand = hands.get(move.getPlayerIndex());
- move.validate(table, hand);
- }
-
- public boolean allPlayersPreparedTheirMove() {
- long nbExpectedMoves = currentTurnInfo.values().stream().filter(pti -> pti.getAction() != Action.WAIT).count();
- return preparedMoves.size() == nbExpectedMoves;
- }
-
- public Table playTurn() {
- makeMoves();
- if (endOfAgeReached()) {
- executeEndOfAgeEvents();
- if (!endOfGameReached()) {
- startNewAge();
- }
- } else {
- rotateHandsIfRelevant();
- startNewTurn();
- }
- return table;
- }
-
- private void rotateHandsIfRelevant() {
- // we don't rotate hands if some player can play his last card (with the special ability)
- if (!hands.maxOneCardRemains()) {
- hands = hands.rotate(table.getHandRotationDirection());
- }
- }
-
- private void makeMoves() {
- List<Move> playedMoves = mapToList(preparedMoves);
-
- // all cards from this turn need to be placed before executing any effect
- // because effects depending on played cards need to take the ones from the current turn into account too
- placePreparedCards(playedMoves);
-
- // same goes for the discarded cards during the last turn, which should be available for special actions
- if (hands.maxOneCardRemains()) {
- discardLastCardsOfHands();
- }
-
- activatePlayedCards(playedMoves);
-
- table.setLastPlayedMoves(playedMoves);
- preparedMoves.clear();
- }
-
- private static List<Move> mapToList(Map<Integer, Move> movesPerPlayer) {
- List<Move> moves = new ArrayList<>(movesPerPlayer.size());
- for (int p = 0; p < movesPerPlayer.size(); p++) {
- Move move = movesPerPlayer.get(p);
- if (move == null) {
- throw new MissingPreparedMoveException(p);
- }
- moves.add(move);
- }
- return moves;
- }
-
- private void placePreparedCards(List<Move> playedMoves) {
- playedMoves.forEach(move -> {
- move.place(table, discardedCards, settings);
- removeFromHand(move.getPlayerIndex(), move.getCard());
- });
- }
-
- private void discardLastCardsOfHands() {
- for (int i = 0; i < nbPlayers; i++) {
- Board board = table.getBoard(i);
- if (!board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
- discardHand(i);
- }
- }
- }
-
- private void discardHand(int playerIndex) {
- List<Card> hand = hands.get(playerIndex);
- discardedCards.addAll(hand);
- hands = hands.discard(playerIndex);
- }
-
- private void removeFromHand(int playerIndex, Card card) {
- hands.get(playerIndex).remove(card);
- }
-
- private void activatePlayedCards(List<Move> playedMoves) {
- playedMoves.forEach(move -> move.activate(table, discardedCards, settings));
- }
-
- private boolean endOfAgeReached() {
- return hands.isEmpty();
- }
-
- private void executeEndOfAgeEvents() {
- table.resolveMilitaryConflicts();
- }
-
- private boolean endOfGameReached() {
- return endOfAgeReached() && table.getCurrentAge() == LAST_AGE;
- }
-
- public ScoreBoard computeScore() {
- ScoreBoard scoreBoard = new ScoreBoard();
- table.getBoards().stream().map(b -> b.computePoints(table)).forEach(scoreBoard::add);
- return scoreBoard;
- }
-
- private static class MissingPreparedMoveException extends IllegalStateException {
- MissingPreparedMoveException(int playerIndex) {
- super("Player " + playerIndex + " is not ready to play");
- }
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/scoring/ScoreBoard.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/scoring/ScoreBoard.java
index e7cdaedd..80f5e510 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/scoring/ScoreBoard.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/scoring/ScoreBoard.java
@@ -1,5 +1,6 @@
package org.luxons.sevenwonders.game.scoring;
+import java.util.Collection;
import java.util.Comparator;
import java.util.PriorityQueue;
@@ -10,8 +11,9 @@ public class ScoreBoard {
private PriorityQueue<PlayerScore> scores;
- public ScoreBoard() {
- scores = new PriorityQueue<>(comparator);
+ public ScoreBoard(Collection<PlayerScore> scores) {
+ this.scores = new PriorityQueue<>(comparator);
+ this.scores.addAll(scores);
}
public void add(PlayerScore score) {
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Game.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Game.kt
new file mode 100644
index 00000000..41e3f764
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Game.kt
@@ -0,0 +1,178 @@
+package org.luxons.sevenwonders.game
+
+import org.luxons.sevenwonders.game.api.Action
+import org.luxons.sevenwonders.game.api.HandCard
+import org.luxons.sevenwonders.game.api.PlayerMove
+import org.luxons.sevenwonders.game.api.PlayerTurnInfo
+import org.luxons.sevenwonders.game.api.Table
+import org.luxons.sevenwonders.game.boards.Board
+import org.luxons.sevenwonders.game.cards.Card
+import org.luxons.sevenwonders.game.cards.CardBack
+import org.luxons.sevenwonders.game.cards.Decks
+import org.luxons.sevenwonders.game.cards.Hands
+import org.luxons.sevenwonders.game.data.LAST_AGE
+import org.luxons.sevenwonders.game.effects.SpecialAbility
+import org.luxons.sevenwonders.game.moves.InvalidMoveException
+import org.luxons.sevenwonders.game.moves.Move
+import org.luxons.sevenwonders.game.scoring.ScoreBoard
+
+class Game(
+ val id: Long,
+ val settings: Settings,
+ boards: List<Board>,
+ private val decks: Decks
+) {
+ private val nbPlayers: Int = boards.size
+ private val table: Table = Table(boards)
+ private val discardedCards: MutableList<Card> = ArrayList()
+ private val preparedMoves: MutableMap<Int, Move> = HashMap()
+ private var currentTurnInfo: List<PlayerTurnInfo> = emptyList()
+ private var hands: Hands = Hands(emptyList())
+
+ init {
+ startNewAge()
+ }
+
+ private fun startNewAge() {
+ table.increaseCurrentAge()
+ hands = decks.deal(table.currentAge, table.nbPlayers)
+ startNewTurn()
+ }
+
+ private fun startNewTurn() {
+ currentTurnInfo = IntRange(0, nbPlayers - 1).map { createPlayerTurnInfo(it) }
+ }
+
+ private fun createPlayerTurnInfo(playerIndex: Int): PlayerTurnInfo {
+ val hand = hands.createHand(table, playerIndex)
+ val action = determineAction(hand, table.getBoard(playerIndex))
+
+ var neighbourGuildCards = emptyList<Card>()
+ if (action === Action.PICK_NEIGHBOR_GUILD) {
+ neighbourGuildCards = table.getNeighbourGuildCards(playerIndex)
+ }
+
+ return PlayerTurnInfo(playerIndex, table, action, hand, neighbourGuildCards)
+ }
+
+ fun getCurrentTurnInfo(): Collection<PlayerTurnInfo> {
+ return currentTurnInfo
+ }
+
+ private fun determineAction(hand: List<HandCard>, board: Board): Action {
+ return if (endOfGameReached() && board.hasSpecial(SpecialAbility.COPY_GUILD)) {
+ determineCopyGuildAction(board)
+ } else if (hand.size == 1 && board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
+ Action.PLAY_LAST
+ } else if (hand.size == 2 && board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
+ Action.PLAY_2
+ } else if (hand.isEmpty()) {
+ Action.WAIT
+ } else {
+ Action.PLAY
+ }
+ }
+
+ private fun determineCopyGuildAction(board: Board): Action {
+ val neighbourGuildCards = table.getNeighbourGuildCards(board.playerIndex)
+ return if (neighbourGuildCards.isEmpty()) Action.WAIT else Action.PICK_NEIGHBOR_GUILD
+ }
+
+ @Throws(InvalidMoveException::class)
+ fun prepareMove(playerIndex: Int, playerMove: PlayerMove): CardBack {
+ val card = decks.getCard(table.currentAge, playerMove.cardName)
+ val move = playerMove.type.resolve(playerIndex, card, playerMove)
+ validate(move)
+ preparedMoves[playerIndex] = move
+ return card.back
+ }
+
+ @Throws(InvalidMoveException::class)
+ private fun validate(move: Move) {
+ move.validate(table, hands[move.playerIndex])
+ }
+
+ fun allPlayersPreparedTheirMove(): Boolean {
+ val nbExpectedMoves = currentTurnInfo.filter { it.action !== Action.WAIT }.count()
+ return preparedMoves.size == nbExpectedMoves
+ }
+
+ fun playTurn(): Table {
+ makeMoves()
+ if (endOfAgeReached()) {
+ executeEndOfAgeEvents()
+ if (!endOfGameReached()) {
+ startNewAge()
+ }
+ } else {
+ rotateHandsIfRelevant()
+ startNewTurn()
+ }
+ return table
+ }
+
+ private fun makeMoves() {
+ val playedMoves = mapToList(preparedMoves)
+
+ // all cards from this turn need to be placed before executing any effect
+ // because effects depending on played cards need to take the ones from the current turn into account too
+ placePreparedCards(playedMoves)
+
+ // same goes for the discarded cards during the last turn, which should be available for special actions
+ if (hands.maxOneCardRemains()) {
+ discardLastCardsOfHands()
+ }
+
+ activatePlayedCards(playedMoves)
+
+ table.lastPlayedMoves = playedMoves
+ preparedMoves.clear()
+ }
+
+ private fun mapToList(movesPerPlayer: Map<Int, Move>): List<Move> =
+ IntRange(0, nbPlayers - 1).map { p -> movesPerPlayer[p] ?: throw MissingPreparedMoveException(p) }
+
+ private fun endOfAgeReached(): Boolean = hands.isEmpty
+
+ private fun executeEndOfAgeEvents() = table.resolveMilitaryConflicts()
+
+ private fun endOfGameReached(): Boolean = endOfAgeReached() && table.currentAge == LAST_AGE
+
+ private fun rotateHandsIfRelevant() {
+ // we don't rotate hands if some player can play his last card (with the special ability)
+ if (!hands.maxOneCardRemains()) {
+ hands = hands.rotate(table.handRotationDirection)
+ }
+ }
+
+ private fun placePreparedCards(playedMoves: List<Move>) {
+ playedMoves.forEach { move ->
+ move.place(table, discardedCards, settings)
+ hands = hands.remove(move.playerIndex, move.card)
+ }
+ }
+
+ private fun discardLastCardsOfHands() {
+ for (i in 0 until nbPlayers) {
+ val board = table.getBoard(i)
+ if (!board.hasSpecial(SpecialAbility.PLAY_LAST_CARD)) {
+ discardHand(i)
+ }
+ }
+ }
+
+ private fun discardHand(playerIndex: Int) {
+ val hand = hands[playerIndex]
+ discardedCards.addAll(hand)
+ hands = hands.discardHand(playerIndex)
+ }
+
+ private fun activatePlayedCards(playedMoves: List<Move>) {
+ playedMoves.forEach { move -> move.activate(table, discardedCards, settings) }
+ }
+
+ fun computeScore(): ScoreBoard = ScoreBoard(table.boards.map { b -> b.computePoints(table) })
+
+ private class MissingPreparedMoveException internal constructor(playerIndex: Int) :
+ IllegalStateException("Player $playerIndex has not prepared his move")
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt
index 35d3599a..50959596 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt
@@ -11,9 +11,16 @@ class Hands internal constructor(private val hands: List<List<Card>>) {
return hands[playerIndex]
}
- fun discard(playerIndex: Int): Hands {
- val newHands = hands.mapIndexed { index, hand -> if (index == playerIndex) emptyList() else hand }
- return Hands(newHands)
+ fun discardHand(playerIndex: Int): Hands {
+ val mutatedHands = hands.toMutableList()
+ mutatedHands[playerIndex] = emptyList()
+ return Hands(mutatedHands)
+ }
+
+ fun remove(playerIndex: Int, card: Card): Hands {
+ val mutatedHands = hands.toMutableList()
+ mutatedHands[playerIndex] = hands[playerIndex] - card
+ return Hands(mutatedHands)
}
fun createHand(table: Table, playerIndex: Int): List<HandCard> {
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt
index b7a859eb..60dce0ee 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt
@@ -19,7 +19,7 @@ class GameDefinition internal constructor(
val settings = Settings(nbPlayers, customSettings)
val boards = assignBoards(settings, nbPlayers)
val decks = decksDefinition.prepareDecks(settings)
- return Game(id, settings, nbPlayers, boards, decks)
+ return Game(id, settings, boards, decks)
}
private fun assignBoards(settings: Settings, nbPlayers: Int): List<Board> {
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt
index f472cc2a..49212ab2 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt
@@ -1,5 +1,7 @@
package org.luxons.sevenwonders.game.data
+const val LAST_AGE: Int = 3
+
internal data class GlobalRules(
val minPlayers: Int,
val maxPlayers: Int
diff --git a/game-engine/src/test/java/org/luxons/sevenwonders/game/GameTest.java b/game-engine/src/test/java/org/luxons/sevenwonders/game/GameTest.java
index de187d26..b0c11836 100644
--- a/game-engine/src/test/java/org/luxons/sevenwonders/game/GameTest.java
+++ b/game-engine/src/test/java/org/luxons/sevenwonders/game/GameTest.java
@@ -39,6 +39,8 @@ public class GameTest {
for (int age = 1; age <= 3; age++) {
playAge(nbPlayers, game, age);
}
+
+ game.computeScore();
}
private void playAge(int nbPlayers, Game game, int age) {
bgstack15