From 8791cc09526b1ce263d14d96dddea7df69b33c58 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 2 Apr 2025 08:51:12 +0100 Subject: [PATCH 01/16] random wip stuff, oof. --- .../object/ExtendedConcurrentHashMap.java | 42 -------- server/src/main/java/object/BidHistory.java | 30 ------ server/src/main/java/object/Game.kt | 98 +++++++++++++++++++ server/src/main/java/object/GameWrapper.java | 18 ---- server/src/main/java/object/HandDetails.java | 9 -- server/src/main/java/object/Round.kt | 3 + server/src/main/kotlin/room/Room.kt | 42 ++++---- 7 files changed, 119 insertions(+), 123 deletions(-) delete mode 100644 core/src/main/java/object/ExtendedConcurrentHashMap.java create mode 100644 server/src/main/java/object/Game.kt create mode 100644 server/src/main/java/object/Round.kt diff --git a/core/src/main/java/object/ExtendedConcurrentHashMap.java b/core/src/main/java/object/ExtendedConcurrentHashMap.java deleted file mode 100644 index 758206a2..00000000 --- a/core/src/main/java/object/ExtendedConcurrentHashMap.java +++ /dev/null @@ -1,42 +0,0 @@ -package object; - -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; - -import util.Debug; - -/** - * Extension of ConcurrentHashMap so I can add useful methods - */ -public class ExtendedConcurrentHashMap extends ConcurrentHashMap -{ - public ExtendedConcurrentHashMap factoryCopy() - { - ExtendedConcurrentHashMap hmCopy = new ExtendedConcurrentHashMap<>(); - - Iterator it = keySet().iterator(); - for (; it.hasNext();) - { - KeyType key = it.next(); - ValueType value = get(key); - hmCopy.put(key, value); - } - - return hmCopy; - } - - public KeyType getOnlyKey() - { - if (size() != 1) - { - Debug.stackTrace("Calling getOnlyKey() but size is " + size() + ". HashMap: " + this); - if (size() == 0) - { - return null; - } - } - - Iterator it = keySet().iterator(); - return it.next(); - } -} diff --git a/server/src/main/java/object/BidHistory.java b/server/src/main/java/object/BidHistory.java index 43d95671..722f9876 100644 --- a/server/src/main/java/object/BidHistory.java +++ b/server/src/main/java/object/BidHistory.java @@ -36,36 +36,6 @@ public boolean addBidForPlayer(int playerNumber, Bid bid) } } - public Bid getNextBidForPlayer(int playerNumber, Bid currentBid) - { - ArrayList bids = hmBidVectorByPlayerNumber.get(playerNumber); - int size = bids.size(); - - if (currentBid == null) - { - return size>0? bids.get(0):null; - } - - for (int i=0; i bids = hmBidVectorByPlayerNumber.get(playerNumber); diff --git a/server/src/main/java/object/Game.kt b/server/src/main/java/object/Game.kt new file mode 100644 index 00000000..0ef43ece --- /dev/null +++ b/server/src/main/java/object/Game.kt @@ -0,0 +1,98 @@ +package `object` + +import java.util.concurrent.ConcurrentHashMap + +/** Wrap up a single game */ +data class GameWrapper(val gameId: String, val rounds: Map, val currentPlayer: Int) { + var winningPlayer: Int = -1 + var roundNumber: Int = 1 + var gameStartMillis: Long = -1 + private set + + var gameEndMillis: Long = -1 + private var hmRoundDetailsByRoundNumber = ConcurrentHashMap() + private var hmBidHistoryByRoundNumber = ConcurrentHashMap() + private var countdownStartMillis: Long = -1 + + fun factoryCopy(): GameWrapper { + val wrapper = GameWrapper(gameId) + wrapper.winningPlayer = winningPlayer + wrapper.setRoundDetails(hmRoundDetailsByRoundNumber) + wrapper.setBidHistory(hmBidHistoryByRoundNumber) + wrapper.setCountdownStartMillis(countdownStartMillis) + wrapper.setGameStartMillisIfUnset(gameStartMillis) + wrapper.gameEndMillis = System.currentTimeMillis() + wrapper.roundNumber = roundNumber + + return wrapper + } + + val currentRoundDetails: HandDetails? + get() = hmRoundDetailsByRoundNumber[roundNumber] + + fun getDetailsForRound(roundNumber: Int): HandDetails? { + return hmRoundDetailsByRoundNumber[roundNumber] + } + + fun setDetailsForRound(roundNumber: Int, details: HandDetails) { + hmRoundDetailsByRoundNumber[roundNumber] = details + } + + val currentBidHistory: BidHistory? + get() = hmBidHistoryByRoundNumber[roundNumber] + + fun getBidHistoryForRound(roundNumber: Int): BidHistory? { + return hmBidHistoryByRoundNumber[roundNumber] + } + + fun setBidHistoryForRound(roundNumber: Int, bidHistory: BidHistory) { + hmBidHistoryByRoundNumber[roundNumber] = bidHistory + } + + fun setRoundDetails(hmRoundDetailsByRoundNumber: ConcurrentHashMap) { + this.hmRoundDetailsByRoundNumber = hmRoundDetailsByRoundNumber + } + + fun setBidHistory(hmBidHistoryByRoundNumber: ConcurrentHashMap) { + this.hmBidHistoryByRoundNumber = hmBidHistoryByRoundNumber + } + + /** Helpers */ + fun getPersonToStart(roundNumber: Int): Int { + val history = hmBidHistoryByRoundNumber[roundNumber] + return history!!.personToStart + } + + val countdownTimeRemaining: Long + get() { + val timeElapsed = System.currentTimeMillis() - countdownStartMillis + if (timeElapsed > COUNTDOWN_TIME_MILLIS) { + return 0 + } + + return COUNTDOWN_TIME_MILLIS - timeElapsed + } + + fun setCountdownStartMillisIfUnset() { + if (countdownStartMillis == -1L) { + countdownStartMillis = System.currentTimeMillis() + } + } + + fun setCountdownStartMillis(countdownStartMillis: Long) { + this.countdownStartMillis = countdownStartMillis + } + + fun setGameStartMillisIfUnset(millis: Long) { + if (gameStartMillis == -1L) { + gameStartMillis = millis + } + } + + val gameDurationMillis: Long + get() = gameEndMillis - gameStartMillis + + companion object { + private const val COUNTDOWN_TIME_MILLIS = 5000 + } +} diff --git a/server/src/main/java/object/GameWrapper.java b/server/src/main/java/object/GameWrapper.java index aeaeef3e..607f1d2a 100644 --- a/server/src/main/java/object/GameWrapper.java +++ b/server/src/main/java/object/GameWrapper.java @@ -93,23 +93,6 @@ public int getPersonToStart(int roundNumber) BidHistory history = hmBidHistoryByRoundNumber.get(roundNumber); return history.getPersonToStart(); } - public Bid getNextBidForPlayer(int playerNumber, int roundNumber, Bid latestBid) - { - BidHistory history = hmBidHistoryByRoundNumber.get(roundNumber); - if (history == null) - { - //Returning null here will be like 'waiting for player' - logger.error("noBidHistory", "Got NULL bid history for gameId " + gameId + " and round " + roundNumber + ". Dump follows."); - debugDump("Game"); - return null; - } - - return history.getNextBidForPlayer(playerNumber, latestBid); - } - public long getCountdownStartMillis() - { - return countdownStartMillis; - } public long getCountdownTimeRemaining() { long timeElapsed = System.currentTimeMillis() - countdownStartMillis; @@ -199,6 +182,5 @@ private void dumpRoundInformation(StringBuilder sb, int roundNumber) sb.append("\n Last player to act: " + history.getLastPlayerToAct()); sb.append("\n BidHistory: " + history); sb.append("\n " + details.getHandsForLogging()); - sb.append("\n hmHandSizeByPlayerNumber: " + details.getHandSizes()); } } diff --git a/server/src/main/java/object/HandDetails.java b/server/src/main/java/object/HandDetails.java index d0a0ae03..cd926fec 100644 --- a/server/src/main/java/object/HandDetails.java +++ b/server/src/main/java/object/HandDetails.java @@ -6,16 +6,7 @@ public class HandDetails { private ConcurrentHashMap> hmHandByPlayerNumber = new ConcurrentHashMap<>(); - private ExtendedConcurrentHashMap hmHandSizeByPlayerNumber = new ExtendedConcurrentHashMap<>(); - public ExtendedConcurrentHashMap getHandSizes() - { - return hmHandSizeByPlayerNumber; - } - public void setHandSizes(ExtendedConcurrentHashMap hmHandSizeByPlayerNumber) - { - this.hmHandSizeByPlayerNumber = hmHandSizeByPlayerNumber; - } public ConcurrentHashMap> getHands() { return hmHandByPlayerNumber; diff --git a/server/src/main/java/object/Round.kt b/server/src/main/java/object/Round.kt new file mode 100644 index 00000000..03d09dbb --- /dev/null +++ b/server/src/main/java/object/Round.kt @@ -0,0 +1,3 @@ +package `object` + +data class Round(val hands: Map>) diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index e1b55ea8..9c4b4490 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -10,7 +10,6 @@ import java.util.concurrent.ConcurrentHashMap import kotlin.math.max import `object`.Bid import `object`.BidHistory -import `object`.ExtendedConcurrentHashMap import `object`.GameWrapper import `object`.HandDetails import `object`.LeftBid @@ -32,7 +31,7 @@ data class Room( val capacity: Int, private val index: Int = 1, ) : IHasId { - private val hmPlayerByPlayerNumber = ExtendedConcurrentHashMap() + private val hmPlayerByPlayerNumber = ConcurrentHashMap() private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() val chatHistory: MutableList = mutableListOf() private val currentPlayers: MutableList = mutableListOf() @@ -101,7 +100,7 @@ data class Room( } fun removePlayer(username: String, fireLobbyChanged: Boolean) { - for (playerNumber in 0 ..< capacity) { + for (playerNumber in 0..() - for (i in 0 ..< capacity) { - hmHandSizeByPlayerNumber[i] = 5 - } + val handSizes = (0..capacity).associateWith { 5 } - val hmHandByPlayerNumber = dealHandsHashMap(hmHandSizeByPlayerNumber) + val hmHandByPlayerNumber = dealHandsHashMap(handSizes) details.hands = hmHandByPlayerNumber - details.handSizes = hmHandSizeByPlayerNumber newGame.setDetailsForRound(1, details) val personToStart = Random().nextInt(capacity) @@ -305,22 +300,21 @@ data class Room( } val currentRoundDetails: HandDetails = currentGame.getDetailsForRound(currentRoundNumber) - val hmHandSizeByPlayerNumber = currentRoundDetails.handSizes - val handSize = hmHandSizeByPlayerNumber.getValue(losingPlayerNumber) - val newHandSize: Int = max(0, handSize - 1) + val hands = currentRoundDetails.hands - val hmHandSizeByPlayerNumberForNextRound = hmHandSizeByPlayerNumber.factoryCopy() - hmHandSizeByPlayerNumberForNextRound[losingPlayerNumber] = newHandSize + val handSizesForNextRound = + hands.mapValues { (playerNumber, hand) -> + if (playerNumber == losingPlayerNumber) max(0, hand.size - 1) else hand.size + } - val potentialWinner: Int = getWinningPlayer(hmHandSizeByPlayerNumberForNextRound) + val potentialWinner: Int = getWinningPlayer(handSizesForNextRound) if (potentialWinner > -1) { finishCurrentGame(potentialWinner) } else { nextRoundDetails = HandDetails() - nextRoundDetails.handSizes = hmHandSizeByPlayerNumberForNextRound val hmHandByPlayerNumber: ConcurrentHashMap> = - dealHandsHashMap(hmHandSizeByPlayerNumberForNextRound) + dealHandsHashMap(handSizesForNextRound) nextRoundDetails.hands = hmHandByPlayerNumber currentGame.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails) @@ -342,7 +336,7 @@ data class Room( } private fun dealHandsHashMap( - hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap + hmHandSizeByPlayerNumber: Map ): ConcurrentHashMap> { val hmHandByPlayerNumber: ConcurrentHashMap> = ConcurrentHashMap() @@ -357,20 +351,20 @@ data class Room( seed, ) - for (i in 0 ..< capacity) { + for (i in 0..): Int { + private fun getWinningPlayer(hmHandSizeByPlayerNumber: Map): Int { var activePlayers = 0 var potentialWinner = 0 - for (i in 0 ..< capacity) { + for (i in 0.. 0) { activePlayers++ From 4aa02a9fad080ff2fda947eaae1fa9af7cf43280 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 9 Apr 2025 08:48:56 +0100 Subject: [PATCH 02/16] more on new Game structure --- server/src/main/java/object/Game.kt | 110 +++++---------------------- server/src/main/java/object/Round.kt | 2 +- 2 files changed, 22 insertions(+), 90 deletions(-) diff --git a/server/src/main/java/object/Game.kt b/server/src/main/java/object/Game.kt index 0ef43ece..8ef3f987 100644 --- a/server/src/main/java/object/Game.kt +++ b/server/src/main/java/object/Game.kt @@ -1,96 +1,28 @@ package `object` -import java.util.concurrent.ConcurrentHashMap - -/** Wrap up a single game */ -data class GameWrapper(val gameId: String, val rounds: Map, val currentPlayer: Int) { - var winningPlayer: Int = -1 - var roundNumber: Int = 1 - var gameStartMillis: Long = -1 - private set - - var gameEndMillis: Long = -1 - private var hmRoundDetailsByRoundNumber = ConcurrentHashMap() - private var hmBidHistoryByRoundNumber = ConcurrentHashMap() - private var countdownStartMillis: Long = -1 - - fun factoryCopy(): GameWrapper { - val wrapper = GameWrapper(gameId) - wrapper.winningPlayer = winningPlayer - wrapper.setRoundDetails(hmRoundDetailsByRoundNumber) - wrapper.setBidHistory(hmBidHistoryByRoundNumber) - wrapper.setCountdownStartMillis(countdownStartMillis) - wrapper.setGameStartMillisIfUnset(gameStartMillis) - wrapper.gameEndMillis = System.currentTimeMillis() - wrapper.roundNumber = roundNumber - - return wrapper - } - - val currentRoundDetails: HandDetails? - get() = hmRoundDetailsByRoundNumber[roundNumber] - - fun getDetailsForRound(roundNumber: Int): HandDetails? { - return hmRoundDetailsByRoundNumber[roundNumber] - } - - fun setDetailsForRound(roundNumber: Int, details: HandDetails) { - hmRoundDetailsByRoundNumber[roundNumber] = details - } - - val currentBidHistory: BidHistory? - get() = hmBidHistoryByRoundNumber[roundNumber] - - fun getBidHistoryForRound(roundNumber: Int): BidHistory? { - return hmBidHistoryByRoundNumber[roundNumber] - } - - fun setBidHistoryForRound(roundNumber: Int, bidHistory: BidHistory) { - hmBidHistoryByRoundNumber[roundNumber] = bidHistory - } - - fun setRoundDetails(hmRoundDetailsByRoundNumber: ConcurrentHashMap) { - this.hmRoundDetailsByRoundNumber = hmRoundDetailsByRoundNumber - } - - fun setBidHistory(hmBidHistoryByRoundNumber: ConcurrentHashMap) { - this.hmBidHistoryByRoundNumber = hmBidHistoryByRoundNumber - } - - /** Helpers */ - fun getPersonToStart(roundNumber: Int): Int { - val history = hmBidHistoryByRoundNumber[roundNumber] - return history!!.personToStart - } - - val countdownTimeRemaining: Long - get() { - val timeElapsed = System.currentTimeMillis() - countdownStartMillis +typealias Millis = Long + +data class Game( + val gameId: String, + val rounds: Map, + val currentPlayer: Int, + val countdownStart: Millis?, + val gameStart: Millis?, + val gameEnd: Millis?, +) { + fun startCountdown() = copy(countdownStart = System.currentTimeMillis()) + + fun cancelCountdown() = copy(countdownStart = null) + + val countdownTimeRemaining = + countdownStart?.let { + val timeElapsed = System.currentTimeMillis() - countdownStart if (timeElapsed > COUNTDOWN_TIME_MILLIS) { - return 0 + 0 + } else { + COUNTDOWN_TIME_MILLIS - timeElapsed } - - return COUNTDOWN_TIME_MILLIS - timeElapsed - } - - fun setCountdownStartMillisIfUnset() { - if (countdownStartMillis == -1L) { - countdownStartMillis = System.currentTimeMillis() - } - } - - fun setCountdownStartMillis(countdownStartMillis: Long) { - this.countdownStartMillis = countdownStartMillis - } - - fun setGameStartMillisIfUnset(millis: Long) { - if (gameStartMillis == -1L) { - gameStartMillis = millis - } - } - - val gameDurationMillis: Long - get() = gameEndMillis - gameStartMillis + } ?: 0 companion object { private const val COUNTDOWN_TIME_MILLIS = 5000 diff --git a/server/src/main/java/object/Round.kt b/server/src/main/java/object/Round.kt index 03d09dbb..ad3ba8f5 100644 --- a/server/src/main/java/object/Round.kt +++ b/server/src/main/java/object/Round.kt @@ -1,3 +1,3 @@ package `object` -data class Round(val hands: Map>) +data class Round(val hands: Map>, val bidHistory: Map) From 4bc9405ea222b34bfbdae3015d29b012addba959 Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 15 Apr 2025 08:55:49 +0100 Subject: [PATCH 03/16] new action structure --- core/src/main/kotlin/game/BidAction.kt | 12 ++++++++++++ core/src/main/kotlin/game/ChallengeAction.kt | 3 +++ core/src/main/kotlin/game/IllegalAction.kt | 3 +++ core/src/main/kotlin/game/LeaveAction.kt | 3 +++ core/src/main/kotlin/game/PlayerAction.kt | 18 ++++++++++++++++++ 5 files changed, 39 insertions(+) create mode 100644 core/src/main/kotlin/game/BidAction.kt create mode 100644 core/src/main/kotlin/game/ChallengeAction.kt create mode 100644 core/src/main/kotlin/game/IllegalAction.kt create mode 100644 core/src/main/kotlin/game/LeaveAction.kt create mode 100644 core/src/main/kotlin/game/PlayerAction.kt diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt new file mode 100644 index 00000000..7b3a7d1b --- /dev/null +++ b/core/src/main/kotlin/game/BidAction.kt @@ -0,0 +1,12 @@ +package game + +abstract class BidAction> : PlayerAction() { + abstract val cardToReveal: String + abstract val blind: Boolean + + abstract fun higherThan(other: B): Boolean + + abstract fun overAchievementThreshold(): Boolean + + abstract fun isPerfect(hands: List>, settings: GameSettings): Boolean +} diff --git a/core/src/main/kotlin/game/ChallengeAction.kt b/core/src/main/kotlin/game/ChallengeAction.kt new file mode 100644 index 00000000..608cd55d --- /dev/null +++ b/core/src/main/kotlin/game/ChallengeAction.kt @@ -0,0 +1,3 @@ +package game + +data class ChallengeAction(override val playerName: String) : PlayerAction() diff --git a/core/src/main/kotlin/game/IllegalAction.kt b/core/src/main/kotlin/game/IllegalAction.kt new file mode 100644 index 00000000..98c4dfdc --- /dev/null +++ b/core/src/main/kotlin/game/IllegalAction.kt @@ -0,0 +1,3 @@ +package game + +data class IllegalAction(override val playerName: String) : PlayerAction() diff --git a/core/src/main/kotlin/game/LeaveAction.kt b/core/src/main/kotlin/game/LeaveAction.kt new file mode 100644 index 00000000..184123bd --- /dev/null +++ b/core/src/main/kotlin/game/LeaveAction.kt @@ -0,0 +1,3 @@ +package game + +data class LeaveAction(override val playerName: String) : PlayerAction() diff --git a/core/src/main/kotlin/game/PlayerAction.kt b/core/src/main/kotlin/game/PlayerAction.kt new file mode 100644 index 00000000..7bcfe8e3 --- /dev/null +++ b/core/src/main/kotlin/game/PlayerAction.kt @@ -0,0 +1,18 @@ +package game + +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "actionType", +) +@JsonSubTypes( + JsonSubTypes.Type(value = ChallengeAction::class, name = "CHALLENGE"), + JsonSubTypes.Type(value = IllegalAction::class, name = "ILLEGAL"), + JsonSubTypes.Type(value = LeaveAction::class, name = "LEAVE"), +) +abstract class PlayerAction { + abstract val playerName: String +} From 7a5c83c03ac61ab8f9f63a21250a84b017a28611 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 16 Apr 2025 08:49:52 +0100 Subject: [PATCH 04/16] more wip action stuff --- core/src/main/kotlin/game/BidAction.kt | 2 ++ core/src/main/kotlin/game/ChallengeAction.kt | 4 ++- core/src/main/kotlin/game/EntropyBidAction.kt | 31 ++++++++++++++++ core/src/main/kotlin/game/IllegalAction.kt | 4 ++- core/src/main/kotlin/game/LeaveAction.kt | 4 ++- core/src/main/kotlin/game/PlayerAction.kt | 2 ++ core/src/main/kotlin/game/Suit.kt | 35 +++++++++++++++++++ .../src/main/kotlin/game/VectropyBidAction.kt | 3 ++ 8 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 core/src/main/kotlin/game/EntropyBidAction.kt create mode 100644 core/src/main/kotlin/game/Suit.kt create mode 100644 core/src/main/kotlin/game/VectropyBidAction.kt diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt index 7b3a7d1b..420f485f 100644 --- a/core/src/main/kotlin/game/BidAction.kt +++ b/core/src/main/kotlin/game/BidAction.kt @@ -9,4 +9,6 @@ abstract class BidAction> : PlayerAction() { abstract fun overAchievementThreshold(): Boolean abstract fun isPerfect(hands: List>, settings: GameSettings): Boolean + + abstract fun isOverbid(hands: List>, settings: GameSettings): Boolean } diff --git a/core/src/main/kotlin/game/ChallengeAction.kt b/core/src/main/kotlin/game/ChallengeAction.kt index 608cd55d..7640c372 100644 --- a/core/src/main/kotlin/game/ChallengeAction.kt +++ b/core/src/main/kotlin/game/ChallengeAction.kt @@ -1,3 +1,5 @@ package game -data class ChallengeAction(override val playerName: String) : PlayerAction() +data class ChallengeAction(override val playerName: String) : PlayerAction() { + override fun htmlString() = "Challenge" +} diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt new file mode 100644 index 00000000..04061f1f --- /dev/null +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -0,0 +1,31 @@ +package game + +data class EntropyBidAction( + override val playerName: String, + override val cardToReveal: String, + override val blind: Boolean, + val amount: Int, + val suit: Suit, +) : BidAction() { + + override fun overAchievementThreshold() = amount >= 5 + + override fun isPerfect(hands: List>, settings: GameSettings): Boolean { + TODO("Not yet implemented") + } + + override fun isOverbid(hands: List>, settings: GameSettings): Boolean { + TODO("Not yet implemented") + } + + override fun higherThan(other: EntropyBidAction): Boolean { + if (amount > other.amount) { + return true + } + + return amount == other.amount && suit > other.suit + } + + override fun htmlString() = + "$amount${suit.unicodeStr}" +} diff --git a/core/src/main/kotlin/game/IllegalAction.kt b/core/src/main/kotlin/game/IllegalAction.kt index 98c4dfdc..8529b54e 100644 --- a/core/src/main/kotlin/game/IllegalAction.kt +++ b/core/src/main/kotlin/game/IllegalAction.kt @@ -1,3 +1,5 @@ package game -data class IllegalAction(override val playerName: String) : PlayerAction() +data class IllegalAction(override val playerName: String) : PlayerAction() { + override fun htmlString() = "Illegal!" +} diff --git a/core/src/main/kotlin/game/LeaveAction.kt b/core/src/main/kotlin/game/LeaveAction.kt index 184123bd..264098e4 100644 --- a/core/src/main/kotlin/game/LeaveAction.kt +++ b/core/src/main/kotlin/game/LeaveAction.kt @@ -1,3 +1,5 @@ package game -data class LeaveAction(override val playerName: String) : PlayerAction() +data class LeaveAction(override val playerName: String) : PlayerAction() { + override fun htmlString() = "Left" +} diff --git a/core/src/main/kotlin/game/PlayerAction.kt b/core/src/main/kotlin/game/PlayerAction.kt index 7bcfe8e3..efa7985d 100644 --- a/core/src/main/kotlin/game/PlayerAction.kt +++ b/core/src/main/kotlin/game/PlayerAction.kt @@ -15,4 +15,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo ) abstract class PlayerAction { abstract val playerName: String + + abstract fun htmlString(): String } diff --git a/core/src/main/kotlin/game/Suit.kt b/core/src/main/kotlin/game/Suit.kt new file mode 100644 index 00000000..9bcc3349 --- /dev/null +++ b/core/src/main/kotlin/game/Suit.kt @@ -0,0 +1,35 @@ +package game + +import util.CardsUtil.CLUBS_SYMBOL +import util.CardsUtil.DIAMONDS_SYMBOL +import util.CardsUtil.HEARTS_SYMBOL +import util.CardsUtil.MOONS_SYMBOL +import util.CardsUtil.SPADES_SYMBOL +import util.CardsUtil.STARS_SYMBOL +import util.Registry + +enum class Suit( + val twoColour: String, + val fourColour: String, + val unicodeStr: String, + val letter: Char, +) { + Clubs("black", "green", CLUBS_SYMBOL, 'c'), + Diamonds("red", "blue", DIAMONDS_SYMBOL, 'd'), + Hearts("red", "red", HEARTS_SYMBOL, 'h'), + Moons("#E6B800", "purple", MOONS_SYMBOL, 'm'), + Spades("black", "black", SPADES_SYMBOL, 's'), + Stars("#E6B800", "#E6B800", STARS_SYMBOL, 'x'); + + fun getDescription(singular: Boolean): String { + val lower = name.lowercase() + return if (singular) lower.dropLast(1) else lower + } + + fun getColour(): String { + // TODO - Should port preferences to the settings abstraction + val numberOfColoursStr = + Registry.prefs[Registry.PREFERENCES_STRING_NUMBER_OF_COLOURS, Registry.TWO_COLOURS] + return if (numberOfColoursStr == Registry.FOUR_COLOURS) fourColour else twoColour + } +} diff --git a/core/src/main/kotlin/game/VectropyBidAction.kt b/core/src/main/kotlin/game/VectropyBidAction.kt new file mode 100644 index 00000000..f901e496 --- /dev/null +++ b/core/src/main/kotlin/game/VectropyBidAction.kt @@ -0,0 +1,3 @@ +package game + +class VectropyBidAction {} From 6ab228221cecf975b18b818d394a2b33d9a80b0b Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 23 Apr 2025 08:50:12 +0100 Subject: [PATCH 05/16] WIP replacement card utilities --- core/src/main/kotlin/game/CardsUtil.kt | 10 ++++++ core/src/main/kotlin/game/EntropyUtil.kt | 13 ++++++++ core/src/test/kotlin/game/CardsUtilTest.kt | 32 ++++++++++++++++++++ core/src/test/kotlin/game/EntropyUtilTest.kt | 29 ++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 core/src/main/kotlin/game/CardsUtil.kt create mode 100644 core/src/main/kotlin/game/EntropyUtil.kt create mode 100644 core/src/test/kotlin/game/CardsUtilTest.kt create mode 100644 core/src/test/kotlin/game/EntropyUtilTest.kt diff --git a/core/src/main/kotlin/game/CardsUtil.kt b/core/src/main/kotlin/game/CardsUtil.kt new file mode 100644 index 00000000..cff58a5c --- /dev/null +++ b/core/src/main/kotlin/game/CardsUtil.kt @@ -0,0 +1,10 @@ +package game + +fun countSuit(suit: Suit, cards: List, jokerValue: Int) = + cards.sumOf { countContribution(suit, it, jokerValue) } + +fun countContribution(suit: Suit, card: String, jokerValue: Int) = + if (card == "A${suit.letter}") 2 + else if (card.contains("A")) 1 + else if (card.startsWith("Jo")) jokerValue + else if (card == "-J${suit.letter}") -1 else if (card.contains(suit.letter)) 1 else 0 diff --git a/core/src/main/kotlin/game/EntropyUtil.kt b/core/src/main/kotlin/game/EntropyUtil.kt new file mode 100644 index 00000000..e95210b1 --- /dev/null +++ b/core/src/main/kotlin/game/EntropyUtil.kt @@ -0,0 +1,13 @@ +package game + +fun amountRequiredToBid(desiredSuit: Suit, currentSuit: Suit, currentAmount: Int) = + if (desiredSuit > currentSuit) currentAmount else currentAmount + 1 + +fun perfectBidAmount(cards: List, jokerValue: Int) = + Suit.entries.maxOf { countSuit(it, cards, jokerValue) } + +fun perfectBidSuit(cards: List, jokerValue: Int, includeStars: Boolean) = + Suit.entries.last { + countSuit(it, cards, jokerValue) == perfectBidAmount(cards, jokerValue) && + (includeStars || it != Suit.Stars) + } diff --git a/core/src/test/kotlin/game/CardsUtilTest.kt b/core/src/test/kotlin/game/CardsUtilTest.kt new file mode 100644 index 00000000..900d0aef --- /dev/null +++ b/core/src/test/kotlin/game/CardsUtilTest.kt @@ -0,0 +1,32 @@ +package game + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testCore.AbstractTest + +class CardsUtilTest : AbstractTest() { + @Test + fun `Should correctly compute the contribution of individual cards`() { + countContribution(Suit.Hearts, "Jo1", 3) shouldBe 3 + countContribution(Suit.Hearts, "Ah", 3) shouldBe 2 + countContribution(Suit.Hearts, "As", 3) shouldBe 1 + countContribution(Suit.Hearts, "-Jh", 3) shouldBe -1 + countContribution(Suit.Hearts, "Jh", 3) shouldBe 1 + countContribution(Suit.Hearts, "Qh", 3) shouldBe 1 + + countContribution(Suit.Hearts, "3c", 3) shouldBe 0 + countContribution(Suit.Hearts, "-Js", 3) shouldBe 0 + } + + @Test + fun `Should count the total for a given suit`() { + val cards = listOf("Jo1", "Ah", "Ad", "3s", "-Jc") + + countSuit(Suit.Clubs, cards, 3) shouldBe 4 + countSuit(Suit.Diamonds, cards, 3) shouldBe 6 + countSuit(Suit.Hearts, cards, 3) shouldBe 6 + countSuit(Suit.Moons, cards, 3) shouldBe 5 + countSuit(Suit.Spades, cards, 3) shouldBe 6 + countSuit(Suit.Stars, cards, 3) shouldBe 5 + } +} diff --git a/core/src/test/kotlin/game/EntropyUtilTest.kt b/core/src/test/kotlin/game/EntropyUtilTest.kt new file mode 100644 index 00000000..0d066975 --- /dev/null +++ b/core/src/test/kotlin/game/EntropyUtilTest.kt @@ -0,0 +1,29 @@ +package game + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testCore.AbstractTest + +class EntropyUtilTest : AbstractTest() { + @Test + fun `Should correctly compute the amount required to bid a specific suit`() { + amountRequiredToBid(Suit.Clubs, Suit.Diamonds, 3) shouldBe 4 + amountRequiredToBid(Suit.Diamonds, Suit.Diamonds, 3) shouldBe 4 + amountRequiredToBid(Suit.Hearts, Suit.Diamonds, 3) shouldBe 3 + amountRequiredToBid(Suit.Moons, Suit.Diamonds, 3) shouldBe 3 + amountRequiredToBid(Suit.Spades, Suit.Diamonds, 3) shouldBe 3 + amountRequiredToBid(Suit.Stars, Suit.Diamonds, 3) shouldBe 3 + } + + @Test + fun `Should exclude stars from perfect bid suit`() { + val cards = listOf("Jo1", "Jo2", "Jo3") + perfectBidSuit(cards, 2, true) shouldBe Suit.Stars + perfectBidSuit(cards, 2, false) shouldBe Suit.Spades + } + + @Test + fun `Should get the perfect bid suit`() { + perfectBidSuit(listOf("Jo1", "Ah", "Ad", "3s", "-Jc"), 3, true) shouldBe Suit.Spades + } +} From a178b908490488bfd4d01d8199c88d0c927a778a Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 30 Apr 2025 08:49:30 +0100 Subject: [PATCH 06/16] more stuff towards PlayerActions --- core/src/main/kotlin/game/EntropyBidAction.kt | 11 ++-- core/src/main/kotlin/game/PlayerAction.kt | 2 + .../src/main/kotlin/game/VectropyBidAction.kt | 61 ++++++++++++++++++- .../test/kotlin/game/EntropyBidActionTest.kt | 16 +++++ 4 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 core/src/test/kotlin/game/EntropyBidActionTest.kt diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt index 04061f1f..0780b733 100644 --- a/core/src/main/kotlin/game/EntropyBidAction.kt +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -11,13 +11,16 @@ data class EntropyBidAction( override fun overAchievementThreshold() = amount >= 5 override fun isPerfect(hands: List>, settings: GameSettings): Boolean { - TODO("Not yet implemented") - } + val perfectAmount = perfectBidAmount(hands.flatten(), settings.jokerValue) + val perfectSuit = + perfectBidSuit(hands.flatten(), settings.jokerValue, settings.includeStars) - override fun isOverbid(hands: List>, settings: GameSettings): Boolean { - TODO("Not yet implemented") + return amount == perfectAmount && suit == perfectSuit } + override fun isOverbid(hands: List>, settings: GameSettings) = + amount > countSuit(suit, hands.flatten(), settings.jokerValue) + override fun higherThan(other: EntropyBidAction): Boolean { if (amount > other.amount) { return true diff --git a/core/src/main/kotlin/game/PlayerAction.kt b/core/src/main/kotlin/game/PlayerAction.kt index efa7985d..22400396 100644 --- a/core/src/main/kotlin/game/PlayerAction.kt +++ b/core/src/main/kotlin/game/PlayerAction.kt @@ -12,6 +12,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo JsonSubTypes.Type(value = ChallengeAction::class, name = "CHALLENGE"), JsonSubTypes.Type(value = IllegalAction::class, name = "ILLEGAL"), JsonSubTypes.Type(value = LeaveAction::class, name = "LEAVE"), + JsonSubTypes.Type(value = EntropyBidAction::class, name = "ENTROPY_BID"), + JsonSubTypes.Type(value = VectropyBidAction::class, name = "VECTROPY_BID"), ) abstract class PlayerAction { abstract val playerName: String diff --git a/core/src/main/kotlin/game/VectropyBidAction.kt b/core/src/main/kotlin/game/VectropyBidAction.kt index f901e496..c15d7dba 100644 --- a/core/src/main/kotlin/game/VectropyBidAction.kt +++ b/core/src/main/kotlin/game/VectropyBidAction.kt @@ -1,3 +1,62 @@ package game -class VectropyBidAction {} +data class VectropyBidAction( + override val playerName: String, + override val cardToReveal: String, + override val blind: Boolean, + val clubs: Int, + val diamonds: Int, + val hearts: Int, + val moons: Int?, + val spades: Int, + val stars: Int?, +) : BidAction() { + + fun getTotal() = clubs + diamonds + hearts + spades + (moons ?: 0) + (stars ?: 0) + + override fun higherThan(other: VectropyBidAction) = + getTotal() > other.getTotal() && + isAtLeast(other, VectropyBidAction::clubs) && + isAtLeast(other, VectropyBidAction::diamonds) && + isAtLeast(other, VectropyBidAction::hearts) && + isAtLeast(other, VectropyBidAction::moons) && + isAtLeast(other, VectropyBidAction::spades) && + isAtLeast(other, VectropyBidAction::stars) + + private fun isAtLeast(other: VectropyBidAction, suitSelector: (VectropyBidAction) -> Int?) = + suitSelector(this)?.let { it >= suitSelector(other)!! } ?: true + + override fun overAchievementThreshold() = getTotal() >= 5 + + override fun isPerfect(hands: List>, settings: GameSettings): Boolean = + evaluateAllSuits(hands.flatten(), settings, ::isPerfect).all { it } + + private fun isPerfect(cards: List, amount: Int?, suit: Suit, settings: GameSettings) = + amount?.let { amount == countSuit(suit, cards, settings.jokerValue) } ?: true + + override fun isOverbid(hands: List>, settings: GameSettings): Boolean = + evaluateAllSuits(hands.flatten(), settings, ::isOverbid).none { it } + + private fun isOverbid(cards: List, amount: Int?, suit: Suit, settings: GameSettings) = + amount?.let { amount > countSuit(suit, cards, settings.jokerValue) } ?: false + + private fun evaluateAllSuits( + cards: List, + settings: GameSettings, + condition: (List, Int?, Suit, GameSettings) -> Boolean, + ) = + listOf( + condition(cards, clubs, Suit.Clubs, settings), + condition(cards, diamonds, Suit.Diamonds, settings), + condition(cards, hearts, Suit.Hearts, settings), + condition(cards, moons, Suit.Moons, settings), + condition(cards, spades, Suit.Spades, settings), + condition(cards, stars, Suit.Stars, settings), + ) + + override fun htmlString(): String { + fun optional(amount: Int?) = if (amount == null) " " else " $amount," + + return "($clubs, $diamonds, $hearts,${optional(moons)} $spades,${optional(stars)})" + } +} diff --git a/core/src/test/kotlin/game/EntropyBidActionTest.kt b/core/src/test/kotlin/game/EntropyBidActionTest.kt new file mode 100644 index 00000000..ee019074 --- /dev/null +++ b/core/src/test/kotlin/game/EntropyBidActionTest.kt @@ -0,0 +1,16 @@ +package game + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testCore.AbstractTest +import utils.CoreGlobals + +class EntropyBidActionTest : AbstractTest() { + @Test + fun `Should serialise and deserialise correctly`() { + val bid: PlayerAction = EntropyBidAction("Suzie", "Ac", false, 5, Suit.Moons) + val json = CoreGlobals.jsonMapper.writeValueAsString(bid) + val deserialized = CoreGlobals.jsonMapper.readValue(json, PlayerAction::class.java) + deserialized shouldBe bid + } +} From 3e3dd8ae29ad23b2b63883b5803c54ebfa4742c5 Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 6 May 2025 08:27:35 +0100 Subject: [PATCH 07/16] more tests for entropy bids --- core/src/main/kotlin/game/BidAction.kt | 6 +-- core/src/main/kotlin/game/EntropyBidAction.kt | 13 +++-- .../src/main/kotlin/game/VectropyBidAction.kt | 8 ++-- .../test/kotlin/game/EntropyBidActionTest.kt | 48 +++++++++++++++++++ server/src/main/kotlin/room/Room.kt | 9 ++-- .../src/main/kotlin/testCore/DtoFactory.kt | 10 ++++ 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt index 420f485f..af8e5004 100644 --- a/core/src/main/kotlin/game/BidAction.kt +++ b/core/src/main/kotlin/game/BidAction.kt @@ -1,14 +1,14 @@ package game abstract class BidAction> : PlayerAction() { - abstract val cardToReveal: String + abstract val cardToReveal: String? abstract val blind: Boolean abstract fun higherThan(other: B): Boolean abstract fun overAchievementThreshold(): Boolean - abstract fun isPerfect(hands: List>, settings: GameSettings): Boolean + abstract fun isPerfect(cards: List, settings: GameSettings): Boolean - abstract fun isOverbid(hands: List>, settings: GameSettings): Boolean + abstract fun isOverbid(cards: List, settings: GameSettings): Boolean } diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt index 0780b733..d73b629b 100644 --- a/core/src/main/kotlin/game/EntropyBidAction.kt +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -2,7 +2,7 @@ package game data class EntropyBidAction( override val playerName: String, - override val cardToReveal: String, + override val cardToReveal: String?, override val blind: Boolean, val amount: Int, val suit: Suit, @@ -10,16 +10,15 @@ data class EntropyBidAction( override fun overAchievementThreshold() = amount >= 5 - override fun isPerfect(hands: List>, settings: GameSettings): Boolean { - val perfectAmount = perfectBidAmount(hands.flatten(), settings.jokerValue) - val perfectSuit = - perfectBidSuit(hands.flatten(), settings.jokerValue, settings.includeStars) + override fun isPerfect(cards: List, settings: GameSettings): Boolean { + val perfectAmount = perfectBidAmount(cards, settings.jokerValue) + val perfectSuit = perfectBidSuit(cards, settings.jokerValue, settings.includeStars) return amount == perfectAmount && suit == perfectSuit } - override fun isOverbid(hands: List>, settings: GameSettings) = - amount > countSuit(suit, hands.flatten(), settings.jokerValue) + override fun isOverbid(cards: List, settings: GameSettings) = + amount > countSuit(suit, cards, settings.jokerValue) override fun higherThan(other: EntropyBidAction): Boolean { if (amount > other.amount) { diff --git a/core/src/main/kotlin/game/VectropyBidAction.kt b/core/src/main/kotlin/game/VectropyBidAction.kt index c15d7dba..114339dd 100644 --- a/core/src/main/kotlin/game/VectropyBidAction.kt +++ b/core/src/main/kotlin/game/VectropyBidAction.kt @@ -28,14 +28,14 @@ data class VectropyBidAction( override fun overAchievementThreshold() = getTotal() >= 5 - override fun isPerfect(hands: List>, settings: GameSettings): Boolean = - evaluateAllSuits(hands.flatten(), settings, ::isPerfect).all { it } + override fun isPerfect(cards: List, settings: GameSettings): Boolean = + evaluateAllSuits(cards, settings, ::isPerfect).all { it } private fun isPerfect(cards: List, amount: Int?, suit: Suit, settings: GameSettings) = amount?.let { amount == countSuit(suit, cards, settings.jokerValue) } ?: true - override fun isOverbid(hands: List>, settings: GameSettings): Boolean = - evaluateAllSuits(hands.flatten(), settings, ::isOverbid).none { it } + override fun isOverbid(cards: List, settings: GameSettings): Boolean = + evaluateAllSuits(cards, settings, ::isOverbid).none { it } private fun isOverbid(cards: List, amount: Int?, suit: Suit, settings: GameSettings) = amount?.let { amount > countSuit(suit, cards, settings.jokerValue) } ?: false diff --git a/core/src/test/kotlin/game/EntropyBidActionTest.kt b/core/src/test/kotlin/game/EntropyBidActionTest.kt index ee019074..5867e794 100644 --- a/core/src/test/kotlin/game/EntropyBidActionTest.kt +++ b/core/src/test/kotlin/game/EntropyBidActionTest.kt @@ -3,6 +3,8 @@ package game import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import testCore.AbstractTest +import testCore.makeEntropyBidAction +import testCore.makeGameSettings import utils.CoreGlobals class EntropyBidActionTest : AbstractTest() { @@ -13,4 +15,50 @@ class EntropyBidActionTest : AbstractTest() { val deserialized = CoreGlobals.jsonMapper.readValue(json, PlayerAction::class.java) deserialized shouldBe bid } + + @Test + fun `Over achievement threshold`() { + makeEntropyBidAction(amount = 4).overAchievementThreshold() shouldBe false + makeEntropyBidAction(amount = 5).overAchievementThreshold() shouldBe true + makeEntropyBidAction(amount = 6).overAchievementThreshold() shouldBe true + } + + @Test + fun `Should detect a perfect bid`() { + val settings = makeGameSettings(jokerValue = 2) + val cards = listOf("As", "3s", "Jo1", "4h") + + makeEntropyBidAction(4, Suit.Hearts).isPerfect(cards, settings) shouldBe false + makeEntropyBidAction(5, Suit.Hearts).isPerfect(cards, settings) shouldBe false + + makeEntropyBidAction(4, Suit.Spades).isPerfect(cards, settings) shouldBe false + makeEntropyBidAction(5, Suit.Spades).isPerfect(cards, settings) shouldBe true + makeEntropyBidAction(6, Suit.Spades).isPerfect(cards, settings) shouldBe false + } + + @Test + fun `Should detect overbids`() { + val settings = makeGameSettings(jokerValue = 3) + val cards = listOf("As", "3s", "Jo1", "4h") + + makeEntropyBidAction(6, Suit.Spades).isOverbid(cards, settings) shouldBe false + makeEntropyBidAction(7, Suit.Spades).isOverbid(cards, settings) shouldBe true + + makeEntropyBidAction(4, Suit.Clubs).isOverbid(cards, settings) shouldBe false + makeEntropyBidAction(5, Suit.Clubs).isOverbid(cards, settings) shouldBe true + } + + @Test + fun `Should determine which bid is higher`() { + val twoDiamonds = makeEntropyBidAction(2, Suit.Diamonds) + + twoDiamonds.higherThan(makeEntropyBidAction(1, Suit.Diamonds)) shouldBe true + twoDiamonds.higherThan(makeEntropyBidAction(1, Suit.Stars)) shouldBe true + twoDiamonds.higherThan(makeEntropyBidAction(2, Suit.Clubs)) shouldBe true + + twoDiamonds.higherThan(makeEntropyBidAction(2, Suit.Diamonds)) shouldBe false + + twoDiamonds.higherThan(makeEntropyBidAction(2, Suit.Hearts)) shouldBe false + twoDiamonds.higherThan(makeEntropyBidAction(3, Suit.Clubs)) shouldBe false + } } diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index 9c4b4490..d92e0b67 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -113,7 +113,7 @@ data class Room( if (currentGame.gameStartMillis == -1L) { // Unset the countdown if it's going, reset current capacity and get out of this // madness - currentGame.countdownStartMillis = -1 + currentGame.setCountdownStartMillis(-1) resetCurrentPlayers(fireLobbyChanged) return } @@ -129,11 +129,12 @@ data class Room( val history: BidHistory = currentGame.currentBidHistory history.addBidForPlayer(playerNumber, bid) + // TODO - Hand size thing // Moved this into here as otherwise we set it to 0 incorrectly and a person // ends up with no cards! - val details: HandDetails = currentGame.currentRoundDetails - val hmHandSizeByPlayerNumber = details.handSizes - hmHandSizeByPlayerNumber[playerNumber] = 0 + // val details: HandDetails = currentGame.currentRoundDetails + // val hmHandSizeByPlayerNumber = details.handSizes + // hmHandSizeByPlayerNumber[playerNumber] = 0 } val playerSize: Int = hmPlayerByPlayerNumber.size diff --git a/test-core/src/main/kotlin/testCore/DtoFactory.kt b/test-core/src/main/kotlin/testCore/DtoFactory.kt index c54e9d71..212c8209 100644 --- a/test-core/src/main/kotlin/testCore/DtoFactory.kt +++ b/test-core/src/main/kotlin/testCore/DtoFactory.kt @@ -1,7 +1,9 @@ package testCore +import game.EntropyBidAction import game.GameMode import game.GameSettings +import game.Suit import http.dto.JoinRoomResponse import http.dto.OnlineMessage import http.dto.RoomStateResponse @@ -54,3 +56,11 @@ fun makeRoomStateResponse( players: Map = mapOf(1 to "Alyssa"), formerPlayers: Map = emptyMap(), ) = RoomStateResponse(players, formerPlayers) + +fun makeEntropyBidAction( + amount: Int = 3, + suit: Suit = Suit.Spades, + player: String = "Alyssa", + cardToReveal: String? = null, + blind: Boolean = false, +) = EntropyBidAction(player, cardToReveal, blind, amount, suit) From c286f44d9133be0e6a88d93e987f303cc4f1e6d0 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 7 May 2025 07:44:14 +0100 Subject: [PATCH 08/16] tweaks --- core/src/main/kotlin/game/BidAction.kt | 3 +++ core/src/main/kotlin/game/ChallengeAction.kt | 2 +- core/src/main/kotlin/game/EntropyBidAction.kt | 2 ++ core/src/main/kotlin/game/IllegalAction.kt | 2 +- core/src/main/kotlin/game/LeaveAction.kt | 2 +- core/src/main/kotlin/game/PlayerAction.kt | 4 +++- core/src/main/kotlin/game/VectropyBidAction.kt | 2 +- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt index af8e5004..775368d4 100644 --- a/core/src/main/kotlin/game/BidAction.kt +++ b/core/src/main/kotlin/game/BidAction.kt @@ -11,4 +11,7 @@ abstract class BidAction> : PlayerAction() { abstract fun isPerfect(cards: List, settings: GameSettings): Boolean abstract fun isOverbid(cards: List, settings: GameSettings): Boolean + + override fun toString() = + plainString() + if (cardToReveal != null) " (Shows: $cardToReveal)" else "" } diff --git a/core/src/main/kotlin/game/ChallengeAction.kt b/core/src/main/kotlin/game/ChallengeAction.kt index 7640c372..865b8f20 100644 --- a/core/src/main/kotlin/game/ChallengeAction.kt +++ b/core/src/main/kotlin/game/ChallengeAction.kt @@ -1,5 +1,5 @@ package game data class ChallengeAction(override val playerName: String) : PlayerAction() { - override fun htmlString() = "Challenge" + override fun plainString() = "Challenge" } diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt index d73b629b..71e9d18d 100644 --- a/core/src/main/kotlin/game/EntropyBidAction.kt +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -28,6 +28,8 @@ data class EntropyBidAction( return amount == other.amount && suit > other.suit } + override fun plainString() = "$amount ${suit.getDescription(amount > 1)}" + override fun htmlString() = "$amount${suit.unicodeStr}" } diff --git a/core/src/main/kotlin/game/IllegalAction.kt b/core/src/main/kotlin/game/IllegalAction.kt index 8529b54e..64654208 100644 --- a/core/src/main/kotlin/game/IllegalAction.kt +++ b/core/src/main/kotlin/game/IllegalAction.kt @@ -1,5 +1,5 @@ package game data class IllegalAction(override val playerName: String) : PlayerAction() { - override fun htmlString() = "Illegal!" + override fun plainString() = "Illegal!" } diff --git a/core/src/main/kotlin/game/LeaveAction.kt b/core/src/main/kotlin/game/LeaveAction.kt index 264098e4..7e278db8 100644 --- a/core/src/main/kotlin/game/LeaveAction.kt +++ b/core/src/main/kotlin/game/LeaveAction.kt @@ -1,5 +1,5 @@ package game data class LeaveAction(override val playerName: String) : PlayerAction() { - override fun htmlString() = "Left" + override fun plainString() = "Left" } diff --git a/core/src/main/kotlin/game/PlayerAction.kt b/core/src/main/kotlin/game/PlayerAction.kt index 22400396..2b73932b 100644 --- a/core/src/main/kotlin/game/PlayerAction.kt +++ b/core/src/main/kotlin/game/PlayerAction.kt @@ -18,5 +18,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo abstract class PlayerAction { abstract val playerName: String - abstract fun htmlString(): String + abstract fun plainString(): String + + open fun htmlString() = plainString() } diff --git a/core/src/main/kotlin/game/VectropyBidAction.kt b/core/src/main/kotlin/game/VectropyBidAction.kt index 114339dd..d71bb432 100644 --- a/core/src/main/kotlin/game/VectropyBidAction.kt +++ b/core/src/main/kotlin/game/VectropyBidAction.kt @@ -54,7 +54,7 @@ data class VectropyBidAction( condition(cards, stars, Suit.Stars, settings), ) - override fun htmlString(): String { + override fun plainString(): String { fun optional(amount: Int?) = if (amount == null) " " else " $amount," return "($clubs, $diamonds, $hearts,${optional(moons)} $spades,${optional(stars)})" From befa89af3ca0f706239b235af62039b3462d6ad6 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 7 May 2025 08:42:32 +0100 Subject: [PATCH 09/16] WIP start plugging in new player actions --- client/src/main/java/screen/BidPanel.java | 5 +- .../src/main/java/screen/EntropyBidPanel.java | 152 +++++------------- client/src/main/java/screen/GameScreen.java | 5 +- client/src/main/java/util/EntropyColour.java | 4 - .../main/kotlin/help/FundamentalsTheDeck.kt | 10 +- core/src/main/java/util/Registry.java | 2 +- core/src/main/kotlin/game/BidAction.kt | 3 +- core/src/main/kotlin/game/EntropyBidAction.kt | 3 +- core/src/main/kotlin/game/Suit.kt | 39 +++-- .../src/main/kotlin/game/VectropyBidAction.kt | 1 - core/src/main/kotlin/utils/ColourUtil.kt | 9 ++ .../src/main/kotlin/testCore/DtoFactory.kt | 2 +- 12 files changed, 95 insertions(+), 140 deletions(-) create mode 100644 core/src/main/kotlin/utils/ColourUtil.kt diff --git a/client/src/main/java/screen/BidPanel.java b/client/src/main/java/screen/BidPanel.java index 627b4cc5..56e0d1a2 100644 --- a/client/src/main/java/screen/BidPanel.java +++ b/client/src/main/java/screen/BidPanel.java @@ -2,10 +2,11 @@ import java.util.prefs.Preferences; +import game.BidAction; import object.Bid; import util.BidListener; -public abstract class BidPanel extends TransparentPanel +public abstract class BidPanel> extends TransparentPanel { public BidListener listener = null; public int maxBid = -1; @@ -19,7 +20,7 @@ public abstract class BidPanel extends TransparentPanel public abstract void saveState(Preferences savedGame); public abstract void fireAppearancePreferencesChange(); public abstract void init(int maxBid, int totalNumberOfCards, boolean online, boolean includeMoons, boolean includeStars, boolean illegalAllowed); - public abstract void adjust(Bid bid); + public abstract void adjust(B bid); public void addBidListener(BidListener listener) { diff --git a/client/src/main/java/screen/EntropyBidPanel.java b/client/src/main/java/screen/EntropyBidPanel.java index da6eeda6..845aaba0 100644 --- a/client/src/main/java/screen/EntropyBidPanel.java +++ b/client/src/main/java/screen/EntropyBidPanel.java @@ -24,22 +24,20 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import object.Bid; +import game.EntropyBidAction; +import game.Suit; import object.EntropyBid; import util.CardsUtil; import util.Debug; -import util.EntropyColour; import util.Registry; -public class EntropyBidPanel extends BidPanel +public class EntropyBidPanel extends BidPanel implements ActionListener, ChangeListener, Registry { - private String suitSelected = CardsUtil.getSuitSymbolForCode(0); - - private int bidSuitCode = CardsUtil.SUIT_CLUBS; - private int lastBidSuitCode = 0; + private Suit bidSuit = Suit.Clubs; + private Suit lastBidSuit = Suit.Clubs; private int lastBidAmount = 0; private boolean illegalAllowed = false; private boolean includeMoons = false; @@ -171,18 +169,16 @@ public void init(int maxBid, int totalNumberOfCards, boolean online, boolean inc btnMoons.setVisible(includeMoons); btnStars.setVisible(includeStars); - bidSuitCode = CardsUtil.SUIT_CLUBS; + bidSuit = Suit.Clubs; lastBidAmount = 0; - lastBidSuitCode = CardsUtil.SUIT_CLUBS; + lastBidSuit = Suit.Clubs; bidSlider.setMaximum(maxBid); bidSlider.setMinimum(1); bidSlider.setValue(1); btnClubs.setSelected(true); - suitSelected = CardsUtil.getSuitSymbolForCode(0); - bidAmountDisplay.setText("1 " + suitSelected); - setBidAmountDisplayColour(); + updateBidAmountDisplay(); setBidButtonColours(); totalCardsLabel.setText("x " + totalNumberOfCards); @@ -247,44 +243,28 @@ public void enableChallenge(boolean enable) } @Override - public void adjust(Bid bid) + public void adjust(EntropyBidAction bid) { - EntropyBid entropyBid = (EntropyBid)bid; - int lastBidAmount = entropyBid.getBidAmount(); + int lastBidAmount = bid.getAmount(); if (lastBidAmount == 0) { return; } - this.lastBidSuitCode = entropyBid.getBidSuitCode(); - this.lastBidAmount = entropyBid.getBidAmount(); - - if (lastBidSuitCode == CardsUtil.SUIT_HEARTS && !includeMoons) - { - bidSlider.setMinimum(lastBidAmount); - bidButtons[(4)].setSelected(true); - bidSuitCode = CardsUtil.SUIT_SPADES; - } - else if ((lastBidSuitCode == CardsUtil.SUIT_SPADES && !includeStars) - || lastBidSuitCode == CardsUtil.SUIT_STARS) - { + this.lastBidSuit = bid.getSuit(); + this.lastBidAmount = bid.getAmount(); + + bidSuit = lastBidSuit.next(includeMoons, includeStars); + if (bidSuit == Suit.Clubs) { bidSlider.setMinimum(lastBidAmount + 1); - btnClubs.setSelected(true); - bidSuitCode = CardsUtil.SUIT_CLUBS; - } - else - { + } else { bidSlider.setMinimum(lastBidAmount); - bidButtons[(lastBidSuitCode + 1)].setSelected(true); - bidSuitCode = lastBidSuitCode + 1; } - - suitSelected = CardsUtil.getSuitSymbolForCode(bidSuitCode); - - String spaceStr = bidSuitCode == CardsUtil.SUIT_MOONS ? "":" "; - bidAmountDisplay.setText(bidSlider.getValue() + spaceStr + suitSelected); - - setBidAmountDisplayColour(); + + var suitIx = Suit.getEntries().indexOf(bidSuit); + bidButtons[suitIx].setSelected(true); + + updateBidAmountDisplay(); } @Override @@ -293,60 +273,15 @@ public void fireAppearancePreferencesChange() String back = prefs.get(PREFERENCES_STRING_CARD_BACKS, Registry.BACK_CODE_CLASSIC_BLUE); smallCardIcon.setIcon(new ImageIcon(EntropyScreen.class.getResource("/backs/" + back + "Small.png"))); - setBidAmountDisplayColour(); + updateBidAmountDisplay(); setBidButtonColours(); } - private void setBidAmountDisplayColour() + private void updateBidAmountDisplay() { - String numberOfColoursStr = prefs.get(PREFERENCES_STRING_NUMBER_OF_COLOURS, Registry.TWO_COLOURS); - boolean fourColours = (numberOfColoursStr.equals(Registry.FOUR_COLOURS)); - - if (bidSuitCode == CardsUtil.SUIT_HEARTS) - { - bidAmountDisplay.setForeground(Color.red); - } - else if (bidSuitCode == CardsUtil.SUIT_SPADES) - { - bidAmountDisplay.setForeground(Color.black); - } - else if (bidSuitCode == CardsUtil.SUIT_STARS) - { - bidAmountDisplay.setForeground(EntropyColour.COLOUR_SUIT_GOLD); - } - else if (bidSuitCode == CardsUtil.SUIT_CLUBS) - { - if (fourColours) - { - bidAmountDisplay.setForeground(EntropyColour.COLOUR_SUIT_GREEN); - } - else - { - bidAmountDisplay.setForeground(Color.black); - } - } - else if (bidSuitCode == CardsUtil.SUIT_DIAMONDS) - { - if (fourColours) - { - bidAmountDisplay.setForeground(Color.blue); - } - else - { - bidAmountDisplay.setForeground(Color.red); - } - } - else if (bidSuitCode == CardsUtil.SUIT_MOONS) - { - if (fourColours) - { - bidAmountDisplay.setForeground(EntropyColour.COLOUR_SUIT_PURPLE); - } - else - { - bidAmountDisplay.setForeground(EntropyColour.COLOUR_SUIT_GOLD); - } - } + String spaceStr = bidSuit == Suit.Moons ? "":" "; + bidAmountDisplay.setText(bidSlider.getValue() + spaceStr + bidSuit.getUnicodeStr()); + bidAmountDisplay.setForeground(bidSuit.getColour()); } private void setBidButtonColours() @@ -381,7 +316,7 @@ private void setBidButtonColours() @Override public void saveState(Preferences savedGame) { - savedGame.putInt(SAVED_GAME_INT_BID_SUIT_CODE, bidSuitCode); + savedGame.put(SAVED_GAME_STRING_BID_SUIT, bidSuit.name()); savedGame.putBoolean(SAVED_GAME_BOOLEAN_CHALLENGE_ENABLED, btnChallenge.isEnabled()); savedGame.putBoolean(SAVED_GAME_BOOLEAN_ILLEGAL_ENABLED, btnIllegal.isEnabled()); savedGame.put(SAVED_GAME_STRING_TOTAL_CARDS_LABEL, totalCardsLabel.getText()); @@ -395,11 +330,8 @@ public void loadState(Preferences savedGame) maxBid = savedGame.getInt(SAVED_GAME_INT_MAX_BID, 0); init(maxBid, -1, false, includeMoons, includeStars, false); - setBidAmountDisplayColour(); + updateBidAmountDisplay(); setBidButtonColours(); - - suitSelected = CardsUtil.getSuitSymbolForCode(bidSuitCode); - bidAmountDisplay.setText(bidSlider.getValue() + " " + suitSelected); setIllegalButtonState(); @@ -424,7 +356,7 @@ public void actionPerformed(ActionEvent arg0) { if (listener != null) { - EntropyBid bid = new EntropyBid(bidSuitCode, bidSlider.getValue()); + EntropyBid bid = new EntropyBid(bidSuit, bidSlider.getValue()); listener.bidMade(bid); } } @@ -444,34 +376,34 @@ else if (source == btnIllegal) } else if (source == btnClubs) { - actionPerformedBidButton(CardsUtil.SUIT_CLUBS); + actionPerformedBidButton(Suit.Clubs); } else if (source == btnDiamonds) { - actionPerformedBidButton(CardsUtil.SUIT_DIAMONDS); + actionPerformedBidButton(Suit.Diamonds); } else if (source == btnHearts) { - actionPerformedBidButton(CardsUtil.SUIT_HEARTS); + actionPerformedBidButton(Suit.Hearts); } else if (source == btnMoons) { - actionPerformedBidButton(CardsUtil.SUIT_MOONS); + actionPerformedBidButton(Suit.Moons); } else if (source == btnSpades) { - actionPerformedBidButton(CardsUtil.SUIT_SPADES); + actionPerformedBidButton(Suit.Spades); } else if (source == btnStars) { - actionPerformedBidButton(CardsUtil.SUIT_STARS); + actionPerformedBidButton(Suit.Stars); } } - private void actionPerformedBidButton(int suitCode) + private void actionPerformedBidButton(Suit suit) { - bidSuitCode = suitCode; - if (lastBidSuitCode < suitCode) + bidSuit = suit; + if (lastBidSuit.lessThan(suit)) { bidSlider.setMinimum(Math.max(lastBidAmount, 1)); } @@ -479,17 +411,13 @@ private void actionPerformedBidButton(int suitCode) { bidSlider.setMinimum(lastBidAmount + 1); } - suitSelected = CardsUtil.getSuitSymbolForCode(suitCode); - String spaceStr = suitCode == CardsUtil.SUIT_MOONS ? "":" "; - bidAmountDisplay.setText(bidSlider.getValue() + spaceStr + suitSelected); - setBidAmountDisplayColour(); + updateBidAmountDisplay(); } @Override public void stateChanged(ChangeEvent arg0) { - String spaceStr = bidSuitCode == CardsUtil.SUIT_MOONS ? "":" "; - bidAmountDisplay.setText(bidSlider.getValue() + spaceStr + suitSelected); + updateBidAmountDisplay(); } } diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 4ae3f16b..1f588f9f 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -1,6 +1,7 @@ package screen; import achievement.AchievementSetting; +import game.BidAction; import game.GameMode; import object.Bid; import object.ChallengeBid; @@ -16,7 +17,7 @@ import static util.ClientGlobals.achievementStore; import static utils.CoreGlobals.logger; -public abstract class GameScreen extends TransparentPanel +public abstract class GameScreen> extends TransparentPanel implements BidListener, RevealListener, Registry @@ -31,7 +32,7 @@ public abstract class GameScreen extends TransparentPanel private int handicapAmount; - public Bid lastBid = null; + public BidAction lastBid = null; private boolean includeJokers = false; private boolean playBlind; diff --git a/client/src/main/java/util/EntropyColour.java b/client/src/main/java/util/EntropyColour.java index d0424801..93431e3c 100644 --- a/client/src/main/java/util/EntropyColour.java +++ b/client/src/main/java/util/EntropyColour.java @@ -4,10 +4,6 @@ public class EntropyColour { - public static final Color COLOUR_SUIT_PURPLE = Color.getHSBColor((float)7/9, 1, 1); - public static final Color COLOUR_SUIT_GOLD = Color.getHSBColor((float)5/36, 1, (float)0.9); - public static final Color COLOUR_SUIT_GREEN = Color.getHSBColor((float)1/3, 1, (float)0.5); - public static final Color COLOUR_REPLAY_VICTORY = new Color(10, 175, 40); public static final Color COLOUR_REPLAY_UNKNOWN_OUTCOME = new Color(175, 175, 175); public static final Color COLOUR_REPLAY_UNFINISHED_AND_LOST = new Color(233, 133, 133); diff --git a/client/src/main/kotlin/help/FundamentalsTheDeck.kt b/client/src/main/kotlin/help/FundamentalsTheDeck.kt index 285e1417..3220c3f5 100644 --- a/client/src/main/kotlin/help/FundamentalsTheDeck.kt +++ b/client/src/main/kotlin/help/FundamentalsTheDeck.kt @@ -8,6 +8,8 @@ import javax.swing.SwingConstants import util.CardsUtil import util.EntropyColour import util.Registry +import utils.COLOUR_SUIT_GOLD +import utils.COLOUR_SUIT_PURPLE class FundamentalsTheDeck : HelpPanel(), Registry { override val nodeName = "The Deck" @@ -66,7 +68,7 @@ class FundamentalsTheDeck : HelpPanel(), Registry { heartLabel.setBounds(240, 360, 65, 60) add(heartLabel) moonLabel.horizontalAlignment = SwingConstants.CENTER - moonLabel.foreground = EntropyColour.COLOUR_SUIT_PURPLE + moonLabel.foreground = COLOUR_SUIT_PURPLE moonLabel.font = Font("Segoe UI Symbol", Font.PLAIN, 32) moonLabel.setBounds(240, 360, 65, 60) add(moonLabel) @@ -89,7 +91,7 @@ class FundamentalsTheDeck : HelpPanel(), Registry { starLabel.horizontalAlignment = SwingConstants.CENTER starLabel.font = Font("Segoe UI Symbol", Font.PLAIN, 40) starLabel.setBounds(400, 360, 65, 60) - starLabel.foreground = EntropyColour.COLOUR_SUIT_GOLD + starLabel.foreground = COLOUR_SUIT_GOLD add(starLabel) finaliseComponents() @@ -149,11 +151,11 @@ class FundamentalsTheDeck : HelpPanel(), Registry { if (fourColours) { clubLabel.foreground = Color(0, 128, 0) diamondLabel.foreground = Color.BLUE - moonLabel.foreground = EntropyColour.COLOUR_SUIT_PURPLE + moonLabel.foreground = COLOUR_SUIT_PURPLE } else { clubLabel.foreground = Color.black diamondLabel.foreground = Color(255, 0, 0) - moonLabel.foreground = EntropyColour.COLOUR_SUIT_GOLD + moonLabel.foreground = COLOUR_SUIT_GOLD } } diff --git a/core/src/main/java/util/Registry.java b/core/src/main/java/util/Registry.java index 66047b4f..884a46dc 100644 --- a/core/src/main/java/util/Registry.java +++ b/core/src/main/java/util/Registry.java @@ -205,7 +205,6 @@ public interface Registry public static final String SAVED_GAME_INT_MAX_BID = "maxBid"; public static final String SAVED_GAME_INT_LAST_BID_AMOUNT = "lastBidAmount"; public static final String SAVED_GAME_INT_LAST_BID_SUIT_CODE = "lastBidSuitCode"; - public static final String SAVED_GAME_INT_BID_SUIT_CODE = "bidSuitCode"; public static final String SAVED_GAME_INT_HANDICAP_AMOUNT = "handicapAmount"; public static final String SAVED_GAME_INT_OPPONENT_THREE_NUMBER_OF_CARDS = "opponentThreeNumberOfCards"; public static final String SAVED_GAME_INT_OPPONENT_TWO_NUMBER_OF_CARDS = "opponentTwoNumberOfCards"; @@ -213,6 +212,7 @@ public interface Registry public static final String SAVED_GAME_INT_PLAYER_NUMBER_OF_CARDS = "playerNumberOfCards"; public static final String SAVED_GAME_INT_HISTORY_SIZE = "historySize"; public static final String SAVED_GAME_STRING_GAME_MODE = "gameMode"; + public static final String SAVED_GAME_STRING_BID_SUIT = "bidSuit"; //statics for default values etc public static final String TWO_COLOURS = "twocolour"; diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt index 775368d4..b396e3f9 100644 --- a/core/src/main/kotlin/game/BidAction.kt +++ b/core/src/main/kotlin/game/BidAction.kt @@ -1,9 +1,10 @@ package game abstract class BidAction> : PlayerAction() { - abstract val cardToReveal: String? abstract val blind: Boolean + var cardToReveal: String? = null + abstract fun higherThan(other: B): Boolean abstract fun overAchievementThreshold(): Boolean diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt index 71e9d18d..b828a030 100644 --- a/core/src/main/kotlin/game/EntropyBidAction.kt +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -2,7 +2,6 @@ package game data class EntropyBidAction( override val playerName: String, - override val cardToReveal: String?, override val blind: Boolean, val amount: Int, val suit: Suit, @@ -31,5 +30,5 @@ data class EntropyBidAction( override fun plainString() = "$amount ${suit.getDescription(amount > 1)}" override fun htmlString() = - "$amount${suit.unicodeStr}" + "$amount${suit.unicodeStr}" } diff --git a/core/src/main/kotlin/game/Suit.kt b/core/src/main/kotlin/game/Suit.kt index 9bcc3349..6f221f22 100644 --- a/core/src/main/kotlin/game/Suit.kt +++ b/core/src/main/kotlin/game/Suit.kt @@ -1,5 +1,6 @@ package game +import java.awt.Color import util.CardsUtil.CLUBS_SYMBOL import util.CardsUtil.DIAMONDS_SYMBOL import util.CardsUtil.HEARTS_SYMBOL @@ -7,29 +8,47 @@ import util.CardsUtil.MOONS_SYMBOL import util.CardsUtil.SPADES_SYMBOL import util.CardsUtil.STARS_SYMBOL import util.Registry +import utils.COLOUR_SUIT_GOLD +import utils.COLOUR_SUIT_GREEN +import utils.COLOUR_SUIT_PURPLE +import utils.toHexCode enum class Suit( - val twoColour: String, - val fourColour: String, + val twoColour: Color, + val fourColour: Color, val unicodeStr: String, val letter: Char, ) { - Clubs("black", "green", CLUBS_SYMBOL, 'c'), - Diamonds("red", "blue", DIAMONDS_SYMBOL, 'd'), - Hearts("red", "red", HEARTS_SYMBOL, 'h'), - Moons("#E6B800", "purple", MOONS_SYMBOL, 'm'), - Spades("black", "black", SPADES_SYMBOL, 's'), - Stars("#E6B800", "#E6B800", STARS_SYMBOL, 'x'); + Clubs(Color.black, COLOUR_SUIT_GREEN, CLUBS_SYMBOL, 'c'), + Diamonds(Color.red, Color.blue, DIAMONDS_SYMBOL, 'd'), + Hearts(Color.red, Color.red, HEARTS_SYMBOL, 'h'), + Moons(COLOUR_SUIT_GOLD, COLOUR_SUIT_PURPLE, MOONS_SYMBOL, 'm'), + Spades(Color.black, Color.black, SPADES_SYMBOL, 's'), + Stars(COLOUR_SUIT_GOLD, COLOUR_SUIT_GOLD, STARS_SYMBOL, 'x'); fun getDescription(singular: Boolean): String { val lower = name.lowercase() return if (singular) lower.dropLast(1) else lower } - fun getColour(): String { - // TODO - Should port preferences to the settings abstraction + fun getColour(): Color { val numberOfColoursStr = Registry.prefs[Registry.PREFERENCES_STRING_NUMBER_OF_COLOURS, Registry.TWO_COLOURS] return if (numberOfColoursStr == Registry.FOUR_COLOURS) fourColour else twoColour } + + fun getColourHex() = getColour().toHexCode() + + fun lessThan(other: Suit) = this < other + + fun next(includeMoons: Boolean, includeStars: Boolean): Suit { + val nextIx = (Suit.entries.indexOf(this) + 1) % Suit.entries.size + val next = Suit.entries[nextIx] + + return if ((next == Moons && !includeMoons) || next == Stars && !includeStars) { + next.next(includeMoons, includeStars) + } else { + next + } + } } diff --git a/core/src/main/kotlin/game/VectropyBidAction.kt b/core/src/main/kotlin/game/VectropyBidAction.kt index d71bb432..22fa8299 100644 --- a/core/src/main/kotlin/game/VectropyBidAction.kt +++ b/core/src/main/kotlin/game/VectropyBidAction.kt @@ -2,7 +2,6 @@ package game data class VectropyBidAction( override val playerName: String, - override val cardToReveal: String, override val blind: Boolean, val clubs: Int, val diamonds: Int, diff --git a/core/src/main/kotlin/utils/ColourUtil.kt b/core/src/main/kotlin/utils/ColourUtil.kt new file mode 100644 index 00000000..875eab16 --- /dev/null +++ b/core/src/main/kotlin/utils/ColourUtil.kt @@ -0,0 +1,9 @@ +package utils + +import java.awt.Color + +val COLOUR_SUIT_PURPLE: Color = Color.getHSBColor(7f / 9, 1f, 1f) +val COLOUR_SUIT_GOLD: Color = Color.getHSBColor(5f / 36, 1f, 0.9.toFloat()) +val COLOUR_SUIT_GREEN: Color = Color.getHSBColor(1f / 3, 1f, 0.5.toFloat()) + +fun Color.toHexCode() = String.format("#%08X", (rgb shl 8) or alpha) diff --git a/test-core/src/main/kotlin/testCore/DtoFactory.kt b/test-core/src/main/kotlin/testCore/DtoFactory.kt index 212c8209..d4a69030 100644 --- a/test-core/src/main/kotlin/testCore/DtoFactory.kt +++ b/test-core/src/main/kotlin/testCore/DtoFactory.kt @@ -63,4 +63,4 @@ fun makeEntropyBidAction( player: String = "Alyssa", cardToReveal: String? = null, blind: Boolean = false, -) = EntropyBidAction(player, cardToReveal, blind, amount, suit) +) = EntropyBidAction(player, blind, amount, suit).also { it.cardToReveal = cardToReveal } From 10955a35c329501797a7f0ffe0fb96a1dd53d1b0 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 7 May 2025 08:56:59 +0100 Subject: [PATCH 10/16] more WIP --- .../main/java/online/screen/EntropyRoom.java | 3 +- .../src/main/java/online/screen/GameRoom.java | 5 ++- .../main/java/online/screen/VectropyRoom.java | 2 +- client/src/main/java/screen/BidPanel.java | 8 ++++ .../src/main/java/screen/EntropyBidPanel.java | 8 ++-- .../src/main/java/screen/EntropyScreen.java | 4 +- client/src/main/java/screen/GameScreen.java | 2 +- .../main/java/screen/VectropyBidPanel.java | 45 ++++++++----------- .../src/main/java/screen/VectropyScreen.java | 2 +- client/src/main/java/util/BidListener.java | 4 +- core/src/main/kotlin/utils/ColourUtil.kt | 6 +-- .../test/kotlin/game/EntropyBidActionTest.kt | 2 +- 12 files changed, 47 insertions(+), 44 deletions(-) diff --git a/client/src/main/java/online/screen/EntropyRoom.java b/client/src/main/java/online/screen/EntropyRoom.java index 1a033f0e..321c8212 100644 --- a/client/src/main/java/online/screen/EntropyRoom.java +++ b/client/src/main/java/online/screen/EntropyRoom.java @@ -9,6 +9,7 @@ import object.EntropyBid; import screen.EntropyBidPanel; import util.CardsUtil; +import util.ClientUtil; import util.Registry; import util.ReplayConstants; @@ -20,7 +21,7 @@ public EntropyRoom(UUID id, String roomName, GameSettings settings, int players) { super(id, roomName, settings, players); - bidPanel = new EntropyBidPanel(); + bidPanel = new EntropyBidPanel(ClientUtil.getUsername(), handPanel); leftPaneSouth.add(bidPanel, BorderLayout.CENTER); bidPanel.addBidListener(this); } diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index a9e9956f..0d483420 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -1,5 +1,6 @@ package online.screen; +import game.BidAction; import game.GameMode; import game.GameSettings; import http.dto.RoomSummary; @@ -778,7 +779,7 @@ public void clearHands() hmHandByAdjustedPlayerNumber.clear(); } - public void handleBid(int playerNumber, Bid bid) + public void handleBid(int playerNumber, BidAction bid) { int playerNumberAdjusted = adjustForMe(playerNumber); Player player = hmPlayerByAdjustedPlayerNumber.get(playerNumberAdjusted); @@ -1372,7 +1373,7 @@ public void requestFocus() * BidListener */ @Override - public void bidMade(Bid bid) + public void bidMade(BidAction bid) { //1. Set the player on the bid Player player = hmPlayerByAdjustedPlayerNumber.get(0); diff --git a/client/src/main/java/online/screen/VectropyRoom.java b/client/src/main/java/online/screen/VectropyRoom.java index 485e9b7d..d82c1bbe 100644 --- a/client/src/main/java/online/screen/VectropyRoom.java +++ b/client/src/main/java/online/screen/VectropyRoom.java @@ -16,7 +16,7 @@ public VectropyRoom(UUID id, String roomName, GameSettings settings, int players { super(id, roomName, settings, players); - bidPanel = new VectropyBidPanel(); + bidPanel = new VectropyBidPanel(ClientUtil.getUsername(), handPanel); leftPaneSouth.add(bidPanel, BorderLayout.CENTER); bidPanel.addBidListener(this); } diff --git a/client/src/main/java/screen/BidPanel.java b/client/src/main/java/screen/BidPanel.java index 56e0d1a2..73541d0b 100644 --- a/client/src/main/java/screen/BidPanel.java +++ b/client/src/main/java/screen/BidPanel.java @@ -8,6 +8,14 @@ public abstract class BidPanel> extends TransparentPanel { + protected final String playerName; + protected final HandPanelMk2 handPanel; + + BidPanel(String playerName, HandPanelMk2 handPanel) { + this.playerName = playerName; + this.handPanel = handPanel; + } + public BidListener listener = null; public int maxBid = -1; diff --git a/client/src/main/java/screen/EntropyBidPanel.java b/client/src/main/java/screen/EntropyBidPanel.java index 845aaba0..a90ca27e 100644 --- a/client/src/main/java/screen/EntropyBidPanel.java +++ b/client/src/main/java/screen/EntropyBidPanel.java @@ -44,8 +44,10 @@ public class EntropyBidPanel extends BidPanel private boolean includeStars = false; private boolean online = false; - public EntropyBidPanel() + public EntropyBidPanel(String playerName, HandPanelMk2 handPanel) { + super(playerName, handPanel); + setPreferredSize(new Dimension(550, 150)); bidGroup.add(btnClubs); bidGroup.add(btnDiamonds); @@ -356,7 +358,7 @@ public void actionPerformed(ActionEvent arg0) { if (listener != null) { - EntropyBid bid = new EntropyBid(bidSuit, bidSlider.getValue()); + var bid = new EntropyBidAction(playerName, handPanel.isPlayingBlind(), bidSlider.getValue(), bidSuit); listener.bidMade(bid); } } @@ -411,7 +413,7 @@ private void actionPerformedBidButton(Suit suit) { bidSlider.setMinimum(lastBidAmount + 1); } - + updateBidAmountDisplay(); } diff --git a/client/src/main/java/screen/EntropyScreen.java b/client/src/main/java/screen/EntropyScreen.java index 57267379..55a493f8 100644 --- a/client/src/main/java/screen/EntropyScreen.java +++ b/client/src/main/java/screen/EntropyScreen.java @@ -19,7 +19,7 @@ public class EntropyScreen extends GameScreen public EntropyScreen() { setFocusable(true); - bidPanel = new EntropyBidPanel(); + bidPanel = new EntropyBidPanel(player.getName(), handPanel); bidPanel.showBidPanel(false); setLayout(new BorderLayout(0, 0)); @@ -30,8 +30,6 @@ public EntropyScreen() bidPanel.setLogging(true); bidPanel.addBidListener(this); } - -//David added this comment! @Override public void showResult() diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 1f588f9f..7880deba 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -1116,7 +1116,7 @@ public void fireAppearancePreferencesChange() * BidListener */ @Override - public void bidMade(Bid bid) + public void bidMade(BidAction bid) { bidPanel.enableBidPanel(false); bid.setPlayer(player); diff --git a/client/src/main/java/screen/VectropyBidPanel.java b/client/src/main/java/screen/VectropyBidPanel.java index 957fe55b..f4cdab17 100644 --- a/client/src/main/java/screen/VectropyBidPanel.java +++ b/client/src/main/java/screen/VectropyBidPanel.java @@ -23,6 +23,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import game.VectropyBidAction; import object.Bid; import object.VectropyBid; import util.CardsUtil; @@ -31,19 +32,23 @@ import util.Registry; import util.VectropyUtil; -public class VectropyBidPanel extends BidPanel +import static utils.ColourUtilKt.*; + +public class VectropyBidPanel extends BidPanel implements ActionListener, ChangeListener, Registry { - private VectropyBid lastBid = VectropyUtil.getEmptyBid(false, false); + private VectropyBidAction lastBid = null; private boolean illegalAllowed = false; private boolean includeMoons = false; private boolean includeStars = false; private boolean online = false; - public VectropyBidPanel() + public VectropyBidPanel(String playerName, HandPanelMk2 handPanel) { + super(playerName, handPanel); + setLayout(new BorderLayout(0, 0)); updateSpinnerColours(); @@ -160,7 +165,7 @@ public VectropyBidPanel() starLabel.setOpaque(false); starLabel.setBorder(BorderFactory.createEmptyBorder()); starLabel.setBackground(new Color(0,0,0,0)); - starLabel.setForeground(EntropyColour.COLOUR_SUIT_GOLD); + starLabel.setForeground(COLOUR_SUIT_GOLD); starsPanel.add(starLabel); starSpinner.setFont(new Font("Tahoma", Font.PLAIN, 16)); @@ -226,14 +231,13 @@ public void init(int maxBid, int totalNumberOfCards, boolean online, boolean inc String back = prefs.get(PREFERENCES_STRING_CARD_BACKS, Registry.BACK_CODE_CLASSIC_BLUE); smallCardIcon.setIcon(new ImageIcon(EntropyScreen.class.getResource("/backs/" + back + "Small.png"))); - lastBid = VectropyUtil.getEmptyBid(includeMoons, includeStars); + lastBid = null; adjust(lastBid); } @Override - public void adjust(Bid bid) + public void adjust(VectropyBidAction bid) { - VectropyBid lastBid = (VectropyBid)bid; this.lastBid = lastBid; int clubs = lastBid.getClubs(); @@ -268,46 +272,35 @@ private void updateSpinnerColours() if (fourColours) { - clubLabel.setForeground(EntropyColour.COLOUR_SUIT_GREEN); + clubLabel.setForeground(COLOUR_SUIT_GREEN); diamondLabel.setForeground(Color.BLUE); - moonLabel.setForeground(EntropyColour.COLOUR_SUIT_PURPLE); + moonLabel.setForeground(COLOUR_SUIT_PURPLE); } else { clubLabel.setForeground(Color.BLACK); diamondLabel.setForeground(Color.RED); - moonLabel.setForeground(EntropyColour.COLOUR_SUIT_GOLD); + moonLabel.setForeground(COLOUR_SUIT_GOLD); } } private void setBidButtonState() { - VectropyBid currentSelection = getBidFromSpinners(); + VectropyBidAction currentSelection = getBidFromSpinners(); boolean enabled = currentSelection.higherThan(lastBid); btnBid.setEnabled(enabled); } - private VectropyBid getBidFromSpinners() + private VectropyBidAction getBidFromSpinners() { int clubs = (int)clubSpinner.getValue(); int diamonds = (int)diamondSpinner.getValue(); int hearts = (int)heartSpinner.getValue(); - - int moons = 0; - if (includeMoons) - { - moons = (int)moonSpinner.getValue(); - } - + int moons = (int)moonSpinner.getValue(); int spades = (int)spadeSpinner.getValue(); + int stars = (int)starSpinner.getValue(); - int stars = 0; - if (includeStars) - { - stars = (int)starSpinner.getValue(); - } - - return new VectropyBid(clubs, diamonds, hearts, moons, spades, stars, includeMoons, includeStars); + return new VectropyBidAction(playerName, handPanel.isPlayingBlind(), clubs, diamonds, hearts, includeMoons ? moons : null, spades, includeStars ? stars : null); } @Override diff --git a/client/src/main/java/screen/VectropyScreen.java b/client/src/main/java/screen/VectropyScreen.java index e36e62c2..b943de47 100644 --- a/client/src/main/java/screen/VectropyScreen.java +++ b/client/src/main/java/screen/VectropyScreen.java @@ -24,7 +24,7 @@ public VectropyScreen() setLayout(new BorderLayout(0, 0)); add(handPanel, BorderLayout.CENTER); handPanel.setOpaque(false); - bidPanel = new VectropyBidPanel(); + bidPanel = new VectropyBidPanel(player.getName(), handPanel); add(bidPanel, BorderLayout.SOUTH); bidPanel.showBidPanel(false); diff --git a/client/src/main/java/util/BidListener.java b/client/src/main/java/util/BidListener.java index d31fd370..a04d4a08 100644 --- a/client/src/main/java/util/BidListener.java +++ b/client/src/main/java/util/BidListener.java @@ -1,10 +1,10 @@ package util; -import object.Bid; +import game.BidAction; public interface BidListener { - public void bidMade(Bid bid); + public void bidMade(BidAction bid); public void challengeMade(); public void illegalCalled(); } diff --git a/core/src/main/kotlin/utils/ColourUtil.kt b/core/src/main/kotlin/utils/ColourUtil.kt index 875eab16..47d92c7b 100644 --- a/core/src/main/kotlin/utils/ColourUtil.kt +++ b/core/src/main/kotlin/utils/ColourUtil.kt @@ -2,8 +2,8 @@ package utils import java.awt.Color -val COLOUR_SUIT_PURPLE: Color = Color.getHSBColor(7f / 9, 1f, 1f) -val COLOUR_SUIT_GOLD: Color = Color.getHSBColor(5f / 36, 1f, 0.9.toFloat()) -val COLOUR_SUIT_GREEN: Color = Color.getHSBColor(1f / 3, 1f, 0.5.toFloat()) +@JvmField val COLOUR_SUIT_PURPLE: Color = Color.getHSBColor(7f / 9, 1f, 1f) +@JvmField val COLOUR_SUIT_GOLD: Color = Color.getHSBColor(5f / 36, 1f, 0.9.toFloat()) +@JvmField val COLOUR_SUIT_GREEN: Color = Color.getHSBColor(1f / 3, 1f, 0.5.toFloat()) fun Color.toHexCode() = String.format("#%08X", (rgb shl 8) or alpha) diff --git a/core/src/test/kotlin/game/EntropyBidActionTest.kt b/core/src/test/kotlin/game/EntropyBidActionTest.kt index 5867e794..cfa81d33 100644 --- a/core/src/test/kotlin/game/EntropyBidActionTest.kt +++ b/core/src/test/kotlin/game/EntropyBidActionTest.kt @@ -10,7 +10,7 @@ import utils.CoreGlobals class EntropyBidActionTest : AbstractTest() { @Test fun `Should serialise and deserialise correctly`() { - val bid: PlayerAction = EntropyBidAction("Suzie", "Ac", false, 5, Suit.Moons) + val bid: PlayerAction = EntropyBidAction("Suzie", false, 5, Suit.Moons) val json = CoreGlobals.jsonMapper.writeValueAsString(bid) val deserialized = CoreGlobals.jsonMapper.readValue(json, PlayerAction::class.java) deserialized shouldBe bid From 4ad6cf3af24e3585a4831eb7375b3067f01ab215 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 14 May 2025 08:55:08 +0100 Subject: [PATCH 11/16] more WIP --- .../object/EntropyAchievementsTracker.java | 68 ++++++++++--------- .../src/main/java/screen/EntropyBidPanel.java | 1 - .../src/main/java/screen/EntropyScreen.java | 26 +++---- client/src/main/java/screen/GameScreen.java | 58 ++++++++-------- .../main/java/screen/SimulationDialog.java | 3 +- .../src/main/java/screen/VectropyScreen.java | 20 +++--- client/src/main/java/util/GameSimulator.java | 3 +- client/src/main/java/util/GameUtil.java | 4 +- .../src/main/java/util/SimulationParms.java | 17 +---- core/src/main/java/util/CardsUtil.java | 18 ++--- core/src/main/java/util/Registry.java | 15 ++-- core/src/main/kotlin/game/CardsUtil.kt | 2 + core/src/main/kotlin/game/GameSettings.kt | 27 +++++++- core/src/main/kotlin/game/Suit.kt | 13 ++-- server/src/main/kotlin/room/Room.kt | 1 - 15 files changed, 146 insertions(+), 130 deletions(-) diff --git a/client/src/main/java/object/EntropyAchievementsTracker.java b/client/src/main/java/object/EntropyAchievementsTracker.java index 5ad4d861..20b2635c 100644 --- a/client/src/main/java/object/EntropyAchievementsTracker.java +++ b/client/src/main/java/object/EntropyAchievementsTracker.java @@ -1,11 +1,14 @@ package object; import achievement.AchievementUtilKt; +import game.EntropyBidAction; +import game.Suit; import util.AchievementsUtil; -import util.CardsUtil; import util.Registry; import utils.Achievement; +import static game.CardsUtilKt.isCardRelevant; + public class EntropyAchievementsTracker implements Registry { private boolean earnedCaveman = false; @@ -19,7 +22,7 @@ public class EntropyAchievementsTracker implements Registry private boolean revealedDifferentSuit = false; private int cardsRevealed = 0; - private int firstSuitBid = -1; + private Suit firstSuitBid = null; private boolean deviatedFromFirstSuit = false; public void reset() @@ -35,7 +38,7 @@ public void reset() revealedDifferentSuit = false; cardsRevealed = 0; - firstSuitBid = -1; + firstSuitBid = null; deviatedFromFirstSuit = false; } @@ -51,8 +54,12 @@ public void loadState() revealedDifferentSuit = savedGame.getBoolean(SAVED_GAME_BOOLEAN_REVEALED_DIFFERENT_SUIT, false); revealedSameSuit = savedGame.getBoolean(SAVED_GAME_BOOLEAN_REVEALED_SAME_SUIT, false); cardsRevealed = savedGame.getInt(SAVED_GAME_INT_CARDS_REVEALED, 0); - - firstSuitBid = savedGame.getInt(SAVED_GAME_INT_FIRST_SUIT_BID, -1); + + var firstBidSuitStr = savedGame.get(SAVED_GAME_STRING_FIRST_SUIT_BID, null); + if (firstBidSuitStr != null) { + firstSuitBid = Suit.valueOf(firstBidSuitStr); + } + deviatedFromFirstSuit = savedGame.getBoolean(SAVED_GAME_BOOLEAN_DEVIATED_FROM_FIRST_SUIT, false); } @@ -68,8 +75,11 @@ public void saveState() savedGame.putBoolean(SAVED_GAME_BOOLEAN_REVEALED_DIFFERENT_SUIT, revealedDifferentSuit); savedGame.putBoolean(SAVED_GAME_BOOLEAN_REVEALED_SAME_SUIT, revealedSameSuit); savedGame.putInt(SAVED_GAME_INT_CARDS_REVEALED, cardsRevealed); - - savedGame.putInt(SAVED_GAME_INT_FIRST_SUIT_BID, firstSuitBid); + + if (firstSuitBid != null) { + savedGame.put(SAVED_GAME_STRING_FIRST_SUIT_BID, firstSuitBid.name()); + } + savedGame.putBoolean(SAVED_GAME_BOOLEAN_DEVIATED_FROM_FIRST_SUIT, deviatedFromFirstSuit); } @@ -79,57 +89,53 @@ public void unlockPerfectBidAchievements(boolean earnedPsychic) earnedGardener, earnedSpaceman, earnedPsychic); } - public void updatePerfectBidVariables(Bid lastBid) + public void updatePerfectBidVariables(EntropyBidAction lastBid) { - EntropyBid entropyBid = (EntropyBid)lastBid; - int bidSuitCode = entropyBid.getBidSuitCode(); + var bidSuit = lastBid.getSuit(); - if (bidSuitCode == CardsUtil.SUIT_SPADES) + if (bidSuit == Suit.Spades) { earnedGardener = true; } - else if (bidSuitCode == CardsUtil.SUIT_HEARTS) + else if (bidSuit == Suit.Hearts) { earnedLion = true; } - else if (bidSuitCode == CardsUtil.SUIT_DIAMONDS) + else if (bidSuit == Suit.Diamonds) { earnedBurglar = true; } - else if (bidSuitCode == CardsUtil.SUIT_CLUBS) + else if (bidSuit == Suit.Clubs) { earnedCaveman = true; } - else if (bidSuitCode == CardsUtil.SUIT_MOONS) + else if (bidSuit == Suit.Moons) { earnedWerewolf = true; } - else if (bidSuitCode == CardsUtil.SUIT_STARS) + else if (bidSuit == Suit.Stars) { earnedSpaceman = true; } } - public void update(Bid bid) + public void update(EntropyBidAction bid) { - EntropyBid entropyBid = (EntropyBid)bid; - - updateCardReveal(entropyBid); - updateMonotone(entropyBid); + updateCardReveal(bid); + updateMonotone(bid); } - private void updateCardReveal(EntropyBid bidMade) + private void updateCardReveal(EntropyBidAction bidMade) { String card = bidMade.getCardToReveal(); - if (card.isEmpty()) + if (card == null) { return; } cardsRevealed++; - - int bidSuitCode = bidMade.getBidSuitCode(); - if (CardsUtil.isRelevant(card, bidSuitCode)) + + if (isCardRelevant(card, bidMade.getSuit())) { revealedSameSuit = true; } @@ -139,16 +145,16 @@ private void updateCardReveal(EntropyBid bidMade) } } - private void updateMonotone(EntropyBid bid) + private void updateMonotone(EntropyBidAction bid) { - int suitCode = bid.getBidSuitCode(); - if (firstSuitBid == -1) + var suit = bid.getSuit(); + if (firstSuitBid == null) { - firstSuitBid = suitCode; + firstSuitBid = suit; return; } - if (suitCode != firstSuitBid) + if (suit != firstSuitBid) { deviatedFromFirstSuit = true; } diff --git a/client/src/main/java/screen/EntropyBidPanel.java b/client/src/main/java/screen/EntropyBidPanel.java index a90ca27e..453f8070 100644 --- a/client/src/main/java/screen/EntropyBidPanel.java +++ b/client/src/main/java/screen/EntropyBidPanel.java @@ -318,7 +318,6 @@ private void setBidButtonColours() @Override public void saveState(Preferences savedGame) { - savedGame.put(SAVED_GAME_STRING_BID_SUIT, bidSuit.name()); savedGame.putBoolean(SAVED_GAME_BOOLEAN_CHALLENGE_ENABLED, btnChallenge.isEnabled()); savedGame.putBoolean(SAVED_GAME_BOOLEAN_ILLEGAL_ENABLED, btnIllegal.isEnabled()); savedGame.put(SAVED_GAME_STRING_TOTAL_CARDS_LABEL, totalCardsLabel.getText()); diff --git a/client/src/main/java/screen/EntropyScreen.java b/client/src/main/java/screen/EntropyScreen.java index 55a493f8..1d401a25 100644 --- a/client/src/main/java/screen/EntropyScreen.java +++ b/client/src/main/java/screen/EntropyScreen.java @@ -3,6 +3,8 @@ import java.awt.BorderLayout; import java.util.List; +import com.fasterxml.jackson.core.JsonProcessingException; +import game.EntropyBidAction; import game.GameMode; import object.EntropyAchievementsTracker; import object.EntropyBid; @@ -10,7 +12,9 @@ import util.EntropyUtil; import util.Registry; -public class EntropyScreen extends GameScreen +import static utils.CoreGlobals.jsonMapper; + +public class EntropyScreen extends GameScreen { private static final long serialVersionUID = 1L; @@ -34,7 +38,7 @@ public EntropyScreen() @Override public void showResult() { - int lastBidSuitCode = ((EntropyBid)lastBid).getBidSuitCode(); + int lastBidSuitCode = lastBid.getSuit().getLegacyCode(); handPanel.displayAndHighlightHands(lastBidSuitCode); int total = countSuit(lastBidSuitCode); @@ -58,15 +62,14 @@ protected void initVariablesForNewGame() } @Override - public void saveGame() + public void saveGame() throws JsonProcessingException { super.saveGame(); //save bid amounts and bid suits if (lastBid != null) { - savedGame.putInt(Registry.SAVED_GAME_INT_LAST_BID_SUIT_CODE, ((EntropyBid)lastBid).getBidSuitCode()); - savedGame.putInt(Registry.SAVED_GAME_INT_LAST_BID_AMOUNT, ((EntropyBid)lastBid).getBidAmount()); + savedGame.put(Registry.SAVED_GAME_STRING_LAST_BID, jsonMapper.writeValueAsString(lastBid)); } //other booleans @@ -80,19 +83,18 @@ public void saveGame() protected void saveRoundForReplay() { int roundsSoFar = inGameReplay.getInt(Registry.REPLAY_INT_ROUNDS_SO_FAR, 0) + 1; - inGameReplay.putInt(roundsSoFar + Registry.REPLAY_INT_LAST_BID_SUIT_CODE, ((EntropyBid)lastBid).getBidSuitCode()); + inGameReplay.putInt(roundsSoFar + Registry.REPLAY_INT_LAST_BID_SUIT_CODE, lastBid.getSuit().getLegacyCode()); super.saveRoundForReplay(); } @Override - public void loadLastBid() + public void loadLastBid() throws JsonProcessingException { - int lastBidSuitCode = savedGame.getInt(Registry.SAVED_GAME_INT_LAST_BID_SUIT_CODE, -1); - int lastBidAmount = savedGame.getInt(Registry.SAVED_GAME_INT_LAST_BID_AMOUNT, -1); + String lastBidStr = savedGame.get(Registry.SAVED_GAME_STRING_LAST_BID, ""); - if (lastBidSuitCode > -1) + if (!lastBidStr.isEmpty()) { - lastBid = new EntropyBid(lastBidSuitCode, lastBidAmount); + lastBid = jsonMapper.readValue(lastBidStr, EntropyBidAction.class); } } @@ -150,7 +152,7 @@ public GameMode getGameMode() @Override public int getLastBidSuitCode() { - return ((EntropyBid)lastBid).getBidSuitCode(); + return lastBid.getSuit().getLegacyCode(); } @Override diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 7880deba..9e7e63d3 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -1,8 +1,10 @@ package screen; import achievement.AchievementSetting; +import com.fasterxml.jackson.core.JsonProcessingException; import game.BidAction; import game.GameMode; +import game.GameSettings; import object.Bid; import object.ChallengeBid; import object.IllegalBid; @@ -23,6 +25,7 @@ public abstract class GameScreen> extends TransparentPane Registry { //Common variables + private GameSettings settings = null; private int numberOfCards = 5; private int totalNumberOfCards; private int personToStart = -1; @@ -30,11 +33,9 @@ public abstract class GameScreen> extends TransparentPane private int jokerQuantity = -1; public int jokerValue = -1; private int handicapAmount; - - - public BidAction lastBid = null; - - private boolean includeJokers = false; + + protected B lastBid = null; + private boolean playBlind; private boolean playWithHandicap; public boolean gameOver = true; @@ -42,13 +43,12 @@ public abstract class GameScreen> extends TransparentPane private boolean logging = true; public boolean hasOverbid; public boolean hasActedBlindThisGame = false; - + private boolean earnedSpectator = false; public boolean earnedPsychic = false; public boolean includeStars = false; public boolean includeMoons = false; private boolean negativeJacks = false; - private boolean cardReveal = false; private boolean currentlyOnChallenge = false; public boolean cheatUsed = false; @@ -64,7 +64,7 @@ public abstract class GameScreen> extends TransparentPane public HandPanelMk2 handPanel = new HandPanelMk2(this); //Abstract methods - public abstract void loadLastBid(); + public abstract void loadLastBid() throws JsonProcessingException; public abstract void loadSpecificVariables(); public abstract void showResult(); @@ -113,7 +113,7 @@ public void startNewRound() private void startRound() { - List deck = CardsUtil.createAndShuffleDeck(includeJokers, jokerQuantity, includeMoons, + List deck = CardsUtil.createAndShuffleDeck(jokerQuantity, includeMoons, includeStars, negativeJacks); populateHands(deck); displayHands(); @@ -211,7 +211,7 @@ private void initVariables() totalNumberOfCards = player.getNumberOfCards() + opponentOne.getNumberOfCards() + opponentTwo.getNumberOfCards() + opponentThree.getNumberOfCards(); - int maxBid = GameUtil.getMaxBid(includeJokers, jokerQuantity, jokerValue, totalNumberOfCards, negativeJacks); + int maxBid = GameUtil.getMaxBid(jokerQuantity, jokerValue, totalNumberOfCards, negativeJacks); bidPanel.init(maxBid, totalNumberOfCards, false, includeMoons, includeStars, false); DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); @@ -318,7 +318,6 @@ else if (opponentOne.isEnabled()) private void getNewGameVariablesFromRegistry() { numberOfCards = prefs.getInt(PREFERENCES_INT_NUMBER_OF_CARDS, 5); - includeJokers = prefs.getBoolean(PREFERENCES_BOOLEAN_INCLUDE_JOKERS, false); jokerQuantity = prefs.getInt(PREFERENCES_INT_JOKER_QUANTITY, 2); jokerValue = prefs.getInt(PREFERENCES_INT_JOKER_VALUE, 2); playBlind = prefs.getBoolean(PREFERENCES_BOOLEAN_PLAY_BLIND, false); @@ -332,7 +331,8 @@ private void getNewGameVariablesFromRegistry() includeStars = prefs.getBoolean(PREFERENCES_BOOLEAN_INCLUDE_STARS, false); includeMoons = prefs.getBoolean(PREFERENCES_BOOLEAN_INCLUDE_MOONS, false); negativeJacks = prefs.getBoolean(PREFERENCES_BOOLEAN_NEGATIVE_JACKS, false); - cardReveal = prefs.getBoolean(PREFERENCES_BOOLEAN_CARD_REVEAL, false); + + settings = GameSettings.fromRegistry(prefs, getGameMode()); handPanel.fireAppearancePreferencesChange(); handPanel.initPlayerNames(); @@ -404,8 +404,8 @@ else if (winningPlayer == 0) int myCards = inGameReplay.getInt(1 + REPLAY_INT_PLAYER_NUMBER_OF_CARDS, 0); AchievementsUtil.checkForPerfectGame(numberOfRounds, startNumberOfCards); - AchievementsUtil.checkForFullBlindGame(startNumberOfCards, playBlind, handPanel.getHasViewedHandThisGame(), cardReveal); - AchievementsUtil.unlockNuclearStrike(myCards, startNumberOfCards, playBlind, handPanel.getHasViewedHandThisGame(), cardReveal); + AchievementsUtil.checkForFullBlindGame(startNumberOfCards, playBlind, handPanel.getHasViewedHandThisGame(), settings.getCardReveal()); + AchievementsUtil.unlockNuclearStrike(myCards, startNumberOfCards, playBlind, handPanel.getHasViewedHandThisGame(), settings.getCardReveal()); AchievementsUtil.unlockHandicapAchievements(myCards, startNumberOfCards); AchievementsUtil.unlockPrecision(hasOverbid, numberOfRounds); @@ -521,7 +521,7 @@ protected void saveRoundForReplay() ScreenCache.getReplayDialog(IN_GAME_REPLAY).roundAdded(); } - protected void saveGame() + protected void saveGame() throws JsonProcessingException { savedGame.putBoolean(SAVED_GAME_BOOLEAN_IS_GAME_TO_CONTINUE, true); savedGame.put(SAVED_GAME_STRING_GAME_MODE, getGameMode().name()); @@ -585,7 +585,6 @@ protected void saveGame() savedGame.putInt(SAVED_GAME_INT_OPPONENT_THREE_CARDS_TO_SUBTRACT, opponentThree.getCardsToSubtract()); //other booleans - savedGame.putBoolean(SAVED_GAME_BOOLEAN_INCLUDE_JOKERS, includeJokers); savedGame.putBoolean(SAVED_GAME_BOOLEAN_FIRST_ROUND, firstRound); savedGame.putBoolean(SAVED_GAME_BOOLEAN_PLAY_BLIND, playBlind); savedGame.putBoolean(SAVED_GAME_BOOLEAN_PLAY_WITH_HANDICAP, playWithHandicap); @@ -596,7 +595,7 @@ protected void saveGame() savedGame.putBoolean(SAVED_GAME_BOOLEAN_INCLUDE_STARS, includeStars); savedGame.putBoolean(SAVED_GAME_BOOLEAN_INCLUDE_MOONS, includeMoons); savedGame.putBoolean(SAVED_GAME_BOOLEAN_NEGATIVE_JACKS, negativeJacks); - savedGame.putBoolean(SAVED_GAME_BOOLEAN_CARD_REVEAL, cardReveal); + savedGame.putBoolean(SHARED_BOOLEAN_CARD_REVEAL, settings.getCardReveal()); savedGame.putBoolean(SAVED_GAME_BOOLEAN_CHEAT_USED, cheatUsed); //other stuff @@ -615,14 +614,13 @@ public void continueGame() try { //set up deck stuff - includeJokers = savedGame.getBoolean(SAVED_GAME_BOOLEAN_INCLUDE_JOKERS, true); jokerValue = savedGame.getInt(SAVED_GAME_INT_JOKER_VALUE, 2); jokerQuantity = savedGame.getInt(SAVED_GAME_INT_JOKER_QUANTITY, 2); includeStars = savedGame.getBoolean(SAVED_GAME_BOOLEAN_INCLUDE_STARS, false); includeMoons = savedGame.getBoolean(SAVED_GAME_BOOLEAN_INCLUDE_MOONS, false); negativeJacks = savedGame.getBoolean(SAVED_GAME_BOOLEAN_NEGATIVE_JACKS, false); - cardReveal = savedGame.getBoolean(SAVED_GAME_BOOLEAN_CARD_REVEAL, false); cheatUsed = savedGame.getBoolean(SAVED_GAME_BOOLEAN_CHEAT_USED, false); + settings = GameSettings.fromRegistry(savedGame, getGameMode()); resetPlayers(); loadLastBid(); @@ -877,11 +875,10 @@ private void processPlayerBid() boolean actedBlind = handPanel.isPlayingBlind(); hasActedBlindThisGame &= actedBlind; - lastBid.setBlind(actedBlind); addToListmodel(lastBid); updateAchievementVariables(); - if (isPerfect(lastBid)) + if (lastBid.isPerfect(allCards(), settings)) { handlePerfectBid(lastBid); } @@ -1058,11 +1055,14 @@ private void displayHands() handPanel.revealCard(card); } } - - public boolean isPerfect(Bid bid) - { - return bid.isPerfect(player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), - opponentThree.getHand(), jokerValue, includeMoons, includeStars); + + protected List allCards() { + ArrayList cards = new ArrayList<>(); + cards.addAll(player.getHand()); + cards.addAll(opponentOne.getHand()); + cards.addAll(opponentTwo.getHand()); + cards.addAll(opponentThree.getHand()); + return cards; } public boolean isOverbid(Bid bid) @@ -1084,7 +1084,7 @@ public StrategyParms factoryStrategyParms(Player opponent) parms.setIncludeMoons(includeMoons); parms.setIncludeStars(includeStars); parms.setNegativeJacks(negativeJacks); - parms.setCardReveal(cardReveal); + parms.setCardReveal(settings.getCardReveal()); if (includeJokers) { @@ -1122,7 +1122,7 @@ public void bidMade(BidAction bid) bid.setPlayer(player); lastBid = bid; - if (cardReveal + if (settings.getCardReveal() && player.hasMoreCardsToReveal()) { handPanel.activateRevealListener(); @@ -1254,7 +1254,7 @@ else if (bid.isIllegal()) { lastBid = bid; - if (cardReveal) + if (settings.getCardReveal()) { String card = bid.getCardToReveal(); handPanel.revealCard(card); diff --git a/client/src/main/java/screen/SimulationDialog.java b/client/src/main/java/screen/SimulationDialog.java index cdd32171..e3c8060d 100644 --- a/client/src/main/java/screen/SimulationDialog.java +++ b/client/src/main/java/screen/SimulationDialog.java @@ -561,7 +561,7 @@ private SimulationParms factorySimulationParms() int numberOfCards = slider.getValue(); boolean includeJokers = cbIncludeJokers.isSelected(); - int jokerValue = (int) spinnerJokerValue.getValue(); + int jokerValue = includeJokers ? (int) spinnerJokerValue.getValue() : 0; int jokerQuantity = (int) spinnerJokerQuantity.getValue(); boolean includeMoons = chckbxIncludeMoons.isSelected(); boolean includeStars = chckbxIncludeStars.isSelected(); @@ -580,7 +580,6 @@ private SimulationParms factorySimulationParms() SimulationParms parms = new SimulationParms(); parms.setForceStart(forceStart); parms.setGameMode(gameMode); - parms.setIncludeJokers(includeJokers); parms.setIncludeMoons(includeMoons); parms.setIncludeStars(includeStars); parms.setNegativeJacks(negativeJacks); diff --git a/client/src/main/java/screen/VectropyScreen.java b/client/src/main/java/screen/VectropyScreen.java index b943de47..246caa6a 100644 --- a/client/src/main/java/screen/VectropyScreen.java +++ b/client/src/main/java/screen/VectropyScreen.java @@ -2,7 +2,9 @@ import java.awt.BorderLayout; +import com.fasterxml.jackson.core.JsonProcessingException; import game.GameMode; +import game.VectropyBidAction; import object.Bid; import object.VectropyBid; import util.AchievementsUtil; @@ -10,7 +12,10 @@ import util.Registry; import util.VectropyUtil; -public class VectropyScreen extends GameScreen +import static utils.CoreGlobals.jsonMapper; +import static utils.CoreGlobals.logger; + +public class VectropyScreen extends GameScreen { private static final long serialVersionUID = 1L; @@ -54,14 +59,14 @@ protected void initVariablesForNewGame() } @Override - public void saveGame() + public void saveGame() throws JsonProcessingException { super.saveGame(); //save bid amounts and bid suits if (lastBid != null) { - savedGame.put(Registry.SAVED_GAME_STRING_LAST_BID, lastBid.toXmlString()); + savedGame.put(Registry.SAVED_GAME_STRING_LAST_BID, jsonMapper.writeValueAsString(lastBid)); } //other booleans @@ -72,12 +77,12 @@ public void saveGame() } @Override - public void loadLastBid() + public void loadLastBid() throws JsonProcessingException { String lastBidStr = savedGame.get(Registry.SAVED_GAME_STRING_LAST_BID, ""); if (!lastBidStr.isEmpty()) { - lastBid = Bid.factoryFromXmlString(lastBidStr, includeMoons, includeStars); + lastBid = jsonMapper.readValue(lastBidStr, VectropyBidAction.class); } } @@ -117,11 +122,6 @@ else if (command.equals("rainingjokers")) /* * Get/sets */ - public void setLastBid(VectropyBid lastBid) - { - this.lastBid = lastBid; - } - @Override public GameMode getGameMode() { diff --git a/client/src/main/java/util/GameSimulator.java b/client/src/main/java/util/GameSimulator.java index c9b60756..59f4636e 100644 --- a/client/src/main/java/util/GameSimulator.java +++ b/client/src/main/java/util/GameSimulator.java @@ -331,8 +331,7 @@ private void initVariablesForSimulationNewRound() opponentTwo.resetHand(); opponentThree.resetHand(); - List deck = CardsUtil.createAndShuffleDeck(parms.getIncludeJokers(), - parms.getJokerQuantity(), parms.getIncludeMoons(), parms.getIncludeStars(), parms.getNegativeJacks()); + List deck = CardsUtil.createAndShuffleDeck(parms.getJokerQuantity(), parms.getIncludeMoons(), parms.getIncludeStars(), parms.getNegativeJacks()); populateHands(deck); } diff --git a/client/src/main/java/util/GameUtil.java b/client/src/main/java/util/GameUtil.java index bc7af73d..a24f0212 100644 --- a/client/src/main/java/util/GameUtil.java +++ b/client/src/main/java/util/GameUtil.java @@ -52,7 +52,7 @@ public static void showResultDialog(int winningPlayer, HandPanelMk2 handPanel) } } - public static int getMaxBid(boolean includeJokers, int jokerQuantity, int jokerValue, + public static int getMaxBid(int jokerQuantity, int jokerValue, int totalNumberOfCards, boolean negativeJacks) { int theoreticalMaxBid = 0; @@ -63,7 +63,7 @@ public static int getMaxBid(boolean includeJokers, int jokerQuantity, int jokerV maxWithoutJokers = 16; //When J not present - (11*1) + (1*2) + (3*1) } - if (includeJokers) + if (jokerQuantity > 0) { int maxJokerQuantity = Math.min(jokerQuantity, totalNumberOfCards); if (totalNumberOfCards > jokerQuantity) diff --git a/client/src/main/java/util/SimulationParms.java b/client/src/main/java/util/SimulationParms.java index 7142dde5..ed55e84c 100644 --- a/client/src/main/java/util/SimulationParms.java +++ b/client/src/main/java/util/SimulationParms.java @@ -20,7 +20,6 @@ public class SimulationParms //Gameplay private int numberOfCards = -1; - private boolean includeJokers = false; private int jokerValue = -1; private int jokerQuantity = -1; private boolean includeMoons = false; @@ -44,12 +43,8 @@ public StrategyParms getStrategyParms() parms.setIncludeStars(includeStars); parms.setNegativeJacks(negativeJacks); parms.setCardReveal(cardReveal); - - if (includeJokers) - { - parms.setJokerQuantity(jokerQuantity); - parms.setJokerValue(jokerValue); - } + parms.setJokerQuantity(jokerQuantity); + parms.setJokerValue(jokerValue); parms.setLogging(logging); @@ -123,14 +118,6 @@ public void setNumberOfCards(int numberOfCards) { this.numberOfCards = numberOfCards; } - public boolean getIncludeJokers() - { - return includeJokers; - } - public void setIncludeJokers(boolean includeJokers) - { - this.includeJokers = includeJokers; - } public int getJokerValue() { return jokerValue; diff --git a/core/src/main/java/util/CardsUtil.java b/core/src/main/java/util/CardsUtil.java index 36b20a06..616ad275 100644 --- a/core/src/main/java/util/CardsUtil.java +++ b/core/src/main/java/util/CardsUtil.java @@ -92,12 +92,12 @@ else if (suit == SUIT_STARS) * Overloading to allow the seed to be passed through from the Server, allowing this to be secure. * http://www.datamation.com/entdev/article.php/11070_616221_3/How-We-Learned-to-Cheat-at-Online-Poker-A-Study-in-Software-Security.htm */ - public static List createAndShuffleDeck(boolean includeJokers, int jokerQuantity, + public static List createAndShuffleDeck(int jokerQuantity, boolean includeMoons, boolean includeStars, boolean negativeJacks) { - return createAndShuffleDeck(includeJokers, jokerQuantity, includeMoons, includeStars, negativeJacks, -1); + return createAndShuffleDeck(jokerQuantity, includeMoons, includeStars, negativeJacks, -1); } - public static List createAndShuffleDeck(boolean includeJokers, int jokerQuantity, + public static List createAndShuffleDeck(int jokerQuantity, boolean includeMoons, boolean includeStars, boolean negativeJacks, long seed) { // Creating the pack of cards @@ -135,13 +135,10 @@ public static List createAndShuffleDeck(boolean includeJokers, int joker list.add(card); } } - - if (includeJokers) + + for (int i=0; i hand, int jokerValue = parms.getJokerValue(); boolean includeMoons = parms.getIncludeMoons(); boolean includeStars = parms.getIncludeStars(); - boolean includeJokers = jokerQuantity > 0; boolean negativeJacks = parms.getNegativeJacks(); - List deck = createAndShuffleDeck(includeJokers, jokerQuantity, includeMoons, includeStars, negativeJacks); + List deck = createAndShuffleDeck(jokerQuantity, includeMoons, includeStars, negativeJacks); int handSize = hand.size(); int totalCardsNotIncludingMine = parms.getTotalNumberOfCards() - handSize; diff --git a/core/src/main/java/util/Registry.java b/core/src/main/java/util/Registry.java index 884a46dc..c7f43f09 100644 --- a/core/src/main/java/util/Registry.java +++ b/core/src/main/java/util/Registry.java @@ -20,6 +20,14 @@ public interface Registry // instance public static final String INSTANCE_INT_REPLAY_CONVERSION = "replayConversion"; public static final String INSTANCE_STRING_DEVICE_ID = "deviceId"; + + // shared + public static final String SHARED_BOOLEAN_INCLUDE_MOONS = "includeMoons"; + public static final String SHARED_BOOLEAN_INCLUDE_STARS = "includeStars"; + public static final String SHARED_BOOLEAN_NEGATIVE_JACKS = "negativeJacks"; + public static final String SHARED_BOOLEAN_CARD_REVEAL = "cardReveal"; + public static final String SHARED_INT_JOKER_VALUE = "jokerValue"; + public static final String SHARED_INT_JOKER_QUANTITY = "jokerQuantity"; //prefs public static final String PREFERENCES_STRING_REPLAY_DIRECTORY = "replayDirectory"; @@ -44,7 +52,6 @@ public interface Registry public static final String PREFERENCES_BOOLEAN_OPPONENT_TWO_ENABLED = "opponentTwoEnabled"; public static final String PREFERENCES_BOOLEAN_PLAY_BLIND = "playBlind"; public static final String PREFERENCES_BOOLEAN_PLAY_WITH_HANDICAP = "playWithHandicap"; - public static final String PREFERENCES_BOOLEAN_INCLUDE_JOKERS = "includeJokers"; public static final String PREFERENCES_BOOLEAN_INCLUDE_GAME_MODE_COLUMN = "includeMode"; public static final String PREFERENCES_BOOLEAN_INCLUDE_ROUNDS_COLUMN = "includeRounds"; public static final String PREFERENCES_BOOLEAN_INCLUDE_PLAYERS_COLUMN = "includePlayers"; @@ -161,7 +168,6 @@ public interface Registry public static final String SAVED_GAME_BOOLEAN_EXITED_ON_CHALLENGE = "exitedOnChallenge"; public static final String SAVED_GAME_BOOLEAN_FIRST_ROUND = "firstRound"; - public static final String SAVED_GAME_BOOLEAN_INCLUDE_JOKERS = "includeJokers"; public static final String SAVED_GAME_BOOLEAN_CHALLENGE_ENABLED = "challengeEnabled"; public static final String SAVED_GAME_BOOLEAN_ILLEGAL_ENABLED = "illegalEnabled"; public static final String SAVED_GAME_BOOLEAN_INCLUDE_STARS = "includeStars"; @@ -191,7 +197,7 @@ public interface Registry public static final String SAVED_GAME_BOOLEAN_REVEALED_DIFFERENT_SUIT = "revealedDifferentSuit"; public static final String SAVED_GAME_BOOLEAN_REVEALED_SAME_SUIT = "revealedSameSuit"; public static final String SAVED_GAME_INT_CARDS_REVEALED = "cardsRevealed"; - public static final String SAVED_GAME_INT_FIRST_SUIT_BID = "firstSuitBid"; + public static final String SAVED_GAME_STRING_FIRST_SUIT_BID = "firstSuitBid"; public static final String SAVED_GAME_BOOLEAN_DEVIATED_FROM_FIRST_SUIT = "deviatedFromFirstSuit"; public static final String SAVED_GAME_INT_OPPONENT_THREE_CARDS_TO_SUBTRACT = "opponentThreeCardsToSubtract"; @@ -203,8 +209,6 @@ public interface Registry public static final String SAVED_GAME_INT_JOKER_VALUE = "jokerValue"; public static final String SAVED_GAME_INT_PERSON_TO_START = "personToStart"; public static final String SAVED_GAME_INT_MAX_BID = "maxBid"; - public static final String SAVED_GAME_INT_LAST_BID_AMOUNT = "lastBidAmount"; - public static final String SAVED_GAME_INT_LAST_BID_SUIT_CODE = "lastBidSuitCode"; public static final String SAVED_GAME_INT_HANDICAP_AMOUNT = "handicapAmount"; public static final String SAVED_GAME_INT_OPPONENT_THREE_NUMBER_OF_CARDS = "opponentThreeNumberOfCards"; public static final String SAVED_GAME_INT_OPPONENT_TWO_NUMBER_OF_CARDS = "opponentTwoNumberOfCards"; @@ -212,7 +216,6 @@ public interface Registry public static final String SAVED_GAME_INT_PLAYER_NUMBER_OF_CARDS = "playerNumberOfCards"; public static final String SAVED_GAME_INT_HISTORY_SIZE = "historySize"; public static final String SAVED_GAME_STRING_GAME_MODE = "gameMode"; - public static final String SAVED_GAME_STRING_BID_SUIT = "bidSuit"; //statics for default values etc public static final String TWO_COLOURS = "twocolour"; diff --git a/core/src/main/kotlin/game/CardsUtil.kt b/core/src/main/kotlin/game/CardsUtil.kt index cff58a5c..ba07982b 100644 --- a/core/src/main/kotlin/game/CardsUtil.kt +++ b/core/src/main/kotlin/game/CardsUtil.kt @@ -8,3 +8,5 @@ fun countContribution(suit: Suit, card: String, jokerValue: Int) = else if (card.contains("A")) 1 else if (card.startsWith("Jo")) jokerValue else if (card == "-J${suit.letter}") -1 else if (card.contains(suit.letter)) 1 else 0 + +fun isCardRelevant(card: String, suit: Suit) = countContribution(suit, card, 1) > 0 diff --git a/core/src/main/kotlin/game/GameSettings.kt b/core/src/main/kotlin/game/GameSettings.kt index 138aab3d..7519e040 100644 --- a/core/src/main/kotlin/game/GameSettings.kt +++ b/core/src/main/kotlin/game/GameSettings.kt @@ -1,5 +1,13 @@ package game +import java.util.prefs.Preferences +import util.Registry.SHARED_BOOLEAN_CARD_REVEAL +import util.Registry.SHARED_BOOLEAN_INCLUDE_MOONS +import util.Registry.SHARED_BOOLEAN_INCLUDE_STARS +import util.Registry.SHARED_BOOLEAN_NEGATIVE_JACKS +import util.Registry.SHARED_INT_JOKER_QUANTITY +import util.Registry.SHARED_INT_JOKER_VALUE + data class GameSettings( val mode: GameMode, val jokerQuantity: Int = 0, @@ -8,5 +16,20 @@ data class GameSettings( val includeStars: Boolean = false, val negativeJacks: Boolean = false, val cardReveal: Boolean = false, - val illegalAllowed: Boolean = false -) + val illegalAllowed: Boolean = false, +) { + companion object { + @JvmStatic + fun fromRegistry(node: Preferences, mode: GameMode) = + GameSettings( + mode, + jokerQuantity = node.getInt(SHARED_INT_JOKER_QUANTITY, 2), + jokerValue = node.getInt(SHARED_INT_JOKER_VALUE, 2), + includeMoons = node.getBoolean(SHARED_BOOLEAN_INCLUDE_MOONS, false), + includeStars = node.getBoolean(SHARED_BOOLEAN_INCLUDE_STARS, false), + negativeJacks = node.getBoolean(SHARED_BOOLEAN_NEGATIVE_JACKS, false), + cardReveal = node.getBoolean(SHARED_BOOLEAN_CARD_REVEAL, false), + illegalAllowed = true, + ) + } +} diff --git a/core/src/main/kotlin/game/Suit.kt b/core/src/main/kotlin/game/Suit.kt index 6f221f22..b4d41481 100644 --- a/core/src/main/kotlin/game/Suit.kt +++ b/core/src/main/kotlin/game/Suit.kt @@ -18,13 +18,14 @@ enum class Suit( val fourColour: Color, val unicodeStr: String, val letter: Char, + @Deprecated("this should die") val legacyCode: Int, ) { - Clubs(Color.black, COLOUR_SUIT_GREEN, CLUBS_SYMBOL, 'c'), - Diamonds(Color.red, Color.blue, DIAMONDS_SYMBOL, 'd'), - Hearts(Color.red, Color.red, HEARTS_SYMBOL, 'h'), - Moons(COLOUR_SUIT_GOLD, COLOUR_SUIT_PURPLE, MOONS_SYMBOL, 'm'), - Spades(Color.black, Color.black, SPADES_SYMBOL, 's'), - Stars(COLOUR_SUIT_GOLD, COLOUR_SUIT_GOLD, STARS_SYMBOL, 'x'); + Clubs(Color.black, COLOUR_SUIT_GREEN, CLUBS_SYMBOL, 'c', 0), + Diamonds(Color.red, Color.blue, DIAMONDS_SYMBOL, 'd', 1), + Hearts(Color.red, Color.red, HEARTS_SYMBOL, 'h', 2), + Moons(COLOUR_SUIT_GOLD, COLOUR_SUIT_PURPLE, MOONS_SYMBOL, 'm', 3), + Spades(Color.black, Color.black, SPADES_SYMBOL, 's', 4), + Stars(COLOUR_SUIT_GOLD, COLOUR_SUIT_GOLD, STARS_SYMBOL, 'x', 5); fun getDescription(singular: Boolean): String { val lower = name.lowercase() diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index d92e0b67..cad4023d 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -344,7 +344,6 @@ data class Room( val seed: Long = ServerGlobals.server.generateSeed() val deck = CardsUtil.createAndShuffleDeck( - true, settings.jokerQuantity, settings.includeMoons, settings.includeStars, From 98576d19df943ba7aee05f872ff996ed10ad0c86 Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 21 May 2025 08:56:14 +0100 Subject: [PATCH 12/16] more WIP, into the strat stuff --- .../src/main/java/online/screen/GameRoom.java | 2 +- .../src/main/java/screen/ClearDataDialog.java | 1 - client/src/main/java/screen/GameScreen.java | 22 +-- .../java/screen/PreferencesPanelGameplay.java | 5 +- client/src/main/java/util/ApiUtil.java | 40 ++++-- client/src/main/java/util/CpuStrategies.java | 8 +- .../src/main/java/util/EntCpuStrategies.java | 128 ++++++------------ .../java/util/MarkStrategySuitWrapper.java | 22 +-- client/src/main/java/util/StrategyUtil.java | 40 ------ core/src/main/java/util/StrategyParms.java | 7 +- core/src/main/kotlin/game/Suit.kt | 23 ++-- 11 files changed, 112 insertions(+), 186 deletions(-) delete mode 100644 client/src/main/java/util/StrategyUtil.java diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index 0d483420..f8c3c9ba 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -385,7 +385,7 @@ private void resetGameVariables() private void initBidPanel() { - int maxBid = GameUtil.getMaxBid(true, settings.getJokerQuantity(), settings.getJokerValue(), totalNumberOfCards, settings.getNegativeJacks()); + int maxBid = GameUtil.getMaxBid(settings.getJokerQuantity(), settings.getJokerValue(), totalNumberOfCards, settings.getNegativeJacks()); bidPanel.init(maxBid, totalNumberOfCards, true, settings.getIncludeMoons(), settings.getIncludeStars(), settings.getIllegalAllowed()); } diff --git a/client/src/main/java/screen/ClearDataDialog.java b/client/src/main/java/screen/ClearDataDialog.java index 2534027f..089f485a 100644 --- a/client/src/main/java/screen/ClearDataDialog.java +++ b/client/src/main/java/screen/ClearDataDialog.java @@ -179,7 +179,6 @@ private void removeStatisticsVariablesFromNode() private void resetPreferences() { - prefs.remove(PREFERENCES_BOOLEAN_INCLUDE_JOKERS); prefs.remove(PREFERENCES_BOOLEAN_PLAY_BLIND); prefs.remove(PREFERENCES_BOOLEAN_PLAY_WITH_HANDICAP); prefs.remove(PREFERENCES_XML_API_SETTINGS); diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 9e7e63d3..317f8f6d 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -2,9 +2,7 @@ import achievement.AchievementSetting; import com.fasterxml.jackson.core.JsonProcessingException; -import game.BidAction; -import game.GameMode; -import game.GameSettings; +import game.*; import object.Bid; import object.ChallengeBid; import object.IllegalBid; @@ -1085,12 +1083,8 @@ public StrategyParms factoryStrategyParms(Player opponent) parms.setIncludeStars(includeStars); parms.setNegativeJacks(negativeJacks); parms.setCardReveal(settings.getCardReveal()); - - if (includeJokers) - { - parms.setJokerQuantity(jokerQuantity); - parms.setJokerValue(jokerValue); - } + parms.setJokerQuantity(jokerQuantity); + parms.setJokerValue(jokerValue); parms.setLastBid(lastBid); parms.setPlayerCards(player.getNumberOfCards()); @@ -1119,7 +1113,6 @@ public void fireAppearancePreferencesChange() public void bidMade(BidAction bid) { bidPanel.enableBidPanel(false); - bid.setPlayer(player); lastBid = bid; if (settings.getCardReveal() @@ -1220,7 +1213,7 @@ public void run() Debug.appendBanner("Opponent " + opponent, logging); StrategyParms parms = factoryStrategyParms(opponent); - Bid bid = CpuStrategies.processOpponentTurn(parms, opponent); + PlayerAction bid = CpuStrategies.processOpponentTurn(parms, opponent); if (bid == null) { //Something's gone wrong - probably an API strategy that timed out or did something invalid. @@ -1238,15 +1231,14 @@ public void run() ScreenCache.get(MainScreen.class).enableNewGameOption(true); return; } - - bid.setPlayer(opponent); + addToListmodel(bid); - if (bid.isChallenge()) + if (bid instanceof ChallengeAction) { processChallenge(opponent); } - else if (bid.isIllegal()) + else if (bid instanceof IllegalAction) { processIllegal(opponent); } diff --git a/client/src/main/java/screen/PreferencesPanelGameplay.java b/client/src/main/java/screen/PreferencesPanelGameplay.java index 6ef1df73..07432e6b 100644 --- a/client/src/main/java/screen/PreferencesPanelGameplay.java +++ b/client/src/main/java/screen/PreferencesPanelGameplay.java @@ -193,7 +193,7 @@ public void savePreferences() numberOfCards = numberOfCardsSlider.getValue(); negativeJacks = cbNegativeJacks.isSelected(); cardReveal = cbPlayersRevealCards.isSelected(); - jokerQuantity = (int) jokerQuantitySpinner.getValue(); + jokerQuantity = includeJokers ? (int) jokerQuantitySpinner.getValue() : 0; jokerValue = (int) jokerValueSpinner.getValue(); handicapAmount = (int) handicapAmountSpinner.getValue(); playBlind = cbPlayBlind.isSelected(); @@ -202,7 +202,6 @@ public void savePreferences() prefs.putInt(PREFERENCES_INT_NUMBER_OF_CARDS, numberOfCards); prefs.putBoolean(PREFERENCES_BOOLEAN_NEGATIVE_JACKS, negativeJacks); - prefs.putBoolean(PREFERENCES_BOOLEAN_INCLUDE_JOKERS, includeJokers); prefs.putInt(PREFERENCES_INT_JOKER_QUANTITY, jokerQuantity); prefs.putInt(PREFERENCES_INT_JOKER_VALUE, jokerValue); prefs.putBoolean(PREFERENCES_BOOLEAN_PLAY_WITH_HANDICAP, playWithHandicap); @@ -217,8 +216,8 @@ public void savePreferences() private void getVariablesFromPreferences() { numberOfCards = prefs.getInt(PREFERENCES_INT_NUMBER_OF_CARDS, 5); - includeJokers = prefs.getBoolean(PREFERENCES_BOOLEAN_INCLUDE_JOKERS, false); jokerQuantity = prefs.getInt(PREFERENCES_INT_JOKER_QUANTITY, 2); + includeJokers = jokerQuantity > 0; jokerValue = prefs.getInt(PREFERENCES_INT_JOKER_VALUE, 2); playWithHandicap = prefs.getBoolean(PREFERENCES_BOOLEAN_PLAY_WITH_HANDICAP, false); handicapAmount = Math.max(prefs.getInt(PREFERENCES_INT_HANDICAP_AMOUNT, 1), 1); diff --git a/client/src/main/java/util/ApiUtil.java b/client/src/main/java/util/ApiUtil.java index 937c5425..f904eaf0 100644 --- a/client/src/main/java/util/ApiUtil.java +++ b/client/src/main/java/util/ApiUtil.java @@ -1,6 +1,6 @@ package util; -import game.GameMode; +import game.*; import object.*; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -43,7 +43,7 @@ public static void sendTestMessage(int port, boolean xml) sendWithCatch(messageString, port, true, true); } - public static Bid processApiTurn(StrategyParms parms, Player player) + public static PlayerAction processApiTurn(StrategyParms parms, Player player) { apiStrategy = getApiStrategy(player.getStrategy()); int port = apiStrategy.getPortNumber(); @@ -58,7 +58,7 @@ public static Bid processApiTurn(StrategyParms parms, Player player) return null; } - return handleResponse(parms, responseString); + return handleResponse(parms, responseString, player); } private static String sendWithCatch(String messageString, int port, boolean logging, boolean testMode) @@ -221,7 +221,7 @@ private static String factoryJsonApiMessage(StrategyParms parms, Player player) return null; } - private static Bid handleResponse(StrategyParms parms, String responseString) + private static PlayerAction handleResponse(StrategyParms parms, String responseString, Player player) { Document xmlResponse = XmlUtil.getDocumentFromXmlString(responseString); if (xmlResponse == null) @@ -236,18 +236,18 @@ private static Bid handleResponse(StrategyParms parms, String responseString) if (responseName.equals("Bid")) { - Bid bid = factoryBid(parms, root, responseString); + BidAction bid = factoryBid(parms, root, responseString, player); String cardToShow = root.getAttribute("CardToShow"); bid.setCardToReveal(cardToShow); return bid; } else if (responseName.equals("Challenge")) { - return new ChallengeBid(); + return new ChallengeAction(player.getName()); } else if (responseName.equals("Illegal")) { - return new IllegalBid(); + return new IllegalAction(player.getName()); } else { @@ -256,18 +256,18 @@ else if (responseName.equals("Illegal")) } } - private static Bid factoryBid(StrategyParms parms, Element root, String responseString) + private static BidAction factoryBid(StrategyParms parms, Element root, String responseString, Player player) { try { GameMode gameMode = parms.getGameMode(); if (gameMode == GameMode.Entropy) { - return EntropyBid.factoryFromXmlTag(root); + return factoryEntropyBid(root, player); } else { - return VectropyBid.factoryFromXmlTag(root, parms.getIncludeMoons(), parms.getIncludeStars()); + return factoryVectropyBid(root, player, parms); } } catch (IOException ioe) @@ -282,6 +282,26 @@ private static Bid factoryBid(StrategyParms parms, Element root, String response return null; } } + + private static EntropyBidAction factoryEntropyBid(Element root, Player player) throws IOException + { + String bidSuit = XmlUtil.getCompulsoryAttribute(root, "BidSuit"); + int bidAmount = XmlUtil.getAttributeIntCompulsory(root, "BidAmount"); + return new EntropyBidAction(player.getName(), false, bidAmount, Suit.valueOf(bidSuit)); + } + + private static VectropyBidAction factoryVectropyBid(Element root, Player player, StrategyParms parms) throws IOException + { + int clubs = XmlUtil.getAttributeIntCompulsory(root, "Clubs"); + int diamonds = XmlUtil.getAttributeIntCompulsory(root, "Diamonds"); + int hearts = XmlUtil.getAttributeIntCompulsory(root, "Hearts"); + int spades = XmlUtil.getAttributeIntCompulsory(root, "Spades"); + + Integer moons = parms.getIncludeMoons() ? XmlUtil.getAttributeIntCompulsory(root, "Moons") : null; + Integer stars = parms.getIncludeStars() ? XmlUtil.getAttributeIntCompulsory(root, "Stars") : null; + + return new VectropyBidAction(player.getName(), false, clubs, diamonds, hearts, moons, spades, stars); + } private static void showMalformedResponseError(String response) { diff --git a/client/src/main/java/util/CpuStrategies.java b/client/src/main/java/util/CpuStrategies.java index 941bad9f..7d118065 100644 --- a/client/src/main/java/util/CpuStrategies.java +++ b/client/src/main/java/util/CpuStrategies.java @@ -1,6 +1,8 @@ package util; +import game.BidAction; import game.GameMode; +import game.PlayerAction; import object.*; import java.util.ArrayList; @@ -60,10 +62,10 @@ private static Vector getFixedStrategies(boolean entropy) /** * Entry-point for strategy code */ - public static Bid processOpponentTurn(StrategyParms parms, Player opponent) + public static PlayerAction processOpponentTurn(StrategyParms parms, Player opponent) { boolean entropy = parms.getGameMode() == GameMode.Entropy; - Bid bid = getOpponentBid(parms, opponent, entropy); + PlayerAction bid = getOpponentBid(parms, opponent, entropy); if (bid == null) { return bid; @@ -106,7 +108,7 @@ public static Bid processOpponentTurn(StrategyParms parms, Player opponent) return bid; } - private static Bid getOpponentBid(StrategyParms parms, Player opponent, boolean entropy) + private static PlayerAction getOpponentBid(StrategyParms parms, Player opponent, boolean entropy) { if (opponent.isApiStrategy()) { diff --git a/client/src/main/java/util/EntCpuStrategies.java b/client/src/main/java/util/EntCpuStrategies.java index 77725c9f..449e8e8c 100644 --- a/client/src/main/java/util/EntCpuStrategies.java +++ b/client/src/main/java/util/EntCpuStrategies.java @@ -2,11 +2,18 @@ import java.util.*; +import game.ChallengeAction; +import game.EntropyBidAction; +import game.PlayerAction; +import game.Suit; import object.Bid; import object.ChallengeBid; import object.EntropyBid; import object.Player; +import static game.CardsUtilKt.countSuit; +import static util.StrategyUtilKt.getRandomSuit; + public class EntCpuStrategies { private static final String STRATEGY_MARK = "Medium"; @@ -24,12 +31,12 @@ public static Vector getAllStrategies() return allStrategies; } - public static Bid processOpponentTurn(Player opponent, StrategyParms parms) + public static PlayerAction processOpponentTurn(Player opponent, StrategyParms parms) { String strategy = opponent.getStrategy(); return processOpponentTurn(strategy, opponent, parms); } - private static Bid processOpponentTurn(String strategy, Player opponent, StrategyParms parms) + private static PlayerAction processOpponentTurn(String strategy, Player opponent, StrategyParms parms) { if (strategy.equals(CpuStrategies.STRATEGY_BASIC)) { @@ -53,9 +60,9 @@ else if (strategy.equals(CpuStrategies.STRATEGY_EV)) } } - private static Bid processMarkTurn(Player opponent, StrategyParms parms) + private static PlayerAction processMarkTurn(Player opponent, StrategyParms parms) { - EntropyBid bid = (EntropyBid)parms.getLastBid(); + EntropyBidAction bid = (EntropyBidAction)parms.getLastBid(); Random rand = new Random(); List hand = opponent.getHand(); @@ -94,8 +101,8 @@ private static Bid processMarkTurn(Player opponent, StrategyParms parms) //Set the 'hand' variable to be everything I can see. hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - int bidAmountFacedWith = bid.getBidAmount(); - int bidSuitCodeFacedWith = bid.getBidSuitCode(); + int bidAmountFacedWith = bid.getAmount(); + var bidSuitFacedWith = bid.getSuit(); int bidSuitCount = CardsUtil.countSuit(hand, bidSuitCodeFacedWith, jokerValue); int minBiddableSuitCount = suitWrapper.getCountOfSuitsPossibleToMinbid(); @@ -108,7 +115,7 @@ private static Bid processMarkTurn(Player opponent, StrategyParms parms) { //0 or 1 Debug.append("One-upped (50%)", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } else if (choice == 2) { @@ -140,7 +147,7 @@ else if (minBiddableSuitCount >= 3) if (choice <= 1) { Debug.append("One-upping (50%)", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } else { @@ -171,7 +178,7 @@ else if (minBiddableSuitCount == 2) if (choice <= 1) { Debug.append("One-upping (50%)", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } else { @@ -197,12 +204,12 @@ else if (minBiddableSuitCount == 1) if (bidAmountFacedWith > threshold) { Debug.append("Auto-challenging because " + bidAmountFacedWith + " > " + threshold, logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } else if (bidAmountFacedWith > bestSuitCount + bidSuitCount + quarterThreshold) { Debug.append("Auto-challenging because bestSuitCount + bidSuitCount + quarterThreshold = " + (bestSuitCount + bidSuitCount + quarterThreshold), logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } else if (bidAmountFacedWith > 1 + quarterThreshold) { @@ -210,12 +217,12 @@ else if (bidAmountFacedWith > 1 + quarterThreshold) if (choice <= 2) { Debug.append("One-upping (75%)", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } else { Debug.append("Challenging (25%)", logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } } else @@ -224,7 +231,7 @@ else if (bidAmountFacedWith > 1 + quarterThreshold) if (choice <= 1) { Debug.append("One-upping (50%)", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } else { @@ -286,10 +293,10 @@ private static int markBid(int halfThreshold, int totalCards, int suitsInPlay) } } - private static Bid processBasicTurn(Player opponent, StrategyParms parms) + private static PlayerAction processBasicTurn(Player opponent, StrategyParms parms) { //Parms - EntropyBid bid = (EntropyBid)parms.getLastBid(); + EntropyBidAction bid = (EntropyBidAction)parms.getLastBid(); boolean includeMoons = parms.getIncludeMoons(); boolean includeStars = parms.getIncludeStars(); int jokerValue = parms.getJokerValue(); @@ -305,20 +312,20 @@ private static Bid processBasicTurn(Player opponent, StrategyParms parms) { Debug.append("Starting this round", logging); - int suitCode = StrategyUtil.getRandomSuit(includeMoons, includeStars); - int bidAmount = Math.max(1, CardsUtil.countSuit(hand, suitCode, jokerValue) + coin.nextInt(2)); + var suit = Suit.random(includeMoons, includeStars); + int bidAmount = Math.max(1, countSuit(suit, hand, jokerValue) + coin.nextInt(2)); - return new EntropyBid(suitCode, bidAmount); + return new EntropyBidAction(opponent.getName(), false, bidAmount, suit); } else { //Set the 'hand' variable to be everything I can see. hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - int bidAmountFacedWith = bid.getBidAmount(); - int bidSuitCodeFacedWith = bid.getBidSuitCode(); + int bidAmountFacedWith = bid.getAmount(); + Suit bidSuitFacedWith = bid.getSuit(); - int bidSuitCount = CardsUtil.countSuit(hand, bidSuitCodeFacedWith, jokerValue); + int bidSuitCount = countSuit(bidSuitFacedWith, hand, jokerValue); double totalCards = parms.getTotalNumberOfCards(); int thirdThreshold = (int) Math.floor(totalCards/3.5); @@ -331,12 +338,12 @@ private static Bid processBasicTurn(Player opponent, StrategyParms parms) if (decisionTwo == 1) { Debug.append("Opponent " + opponent + " auto-minbid", logging); - return opponentMinBid(bidSuitCodeFacedWith, bidAmountFacedWith, includeMoons, includeStars); + return opponentMinBid(opponent, bidSuitFacedWith, bidAmountFacedWith, includeMoons, includeStars); } else { Debug.append("Opponent " + opponent + " auto-oneupped", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } } else if (bidAmountFacedWith < bidSuitCount + thirdThreshold) @@ -344,35 +351,35 @@ else if (bidAmountFacedWith < bidSuitCount + thirdThreshold) if (decisionTwo == 1) { Debug.append("Opponent " + opponent + " auto-minbid", logging); - return opponentMinBid(bidSuitCodeFacedWith, bidAmountFacedWith, includeMoons, includeStars); + return opponentMinBid(opponent, bidSuitFacedWith, bidAmountFacedWith, includeMoons, includeStars); } else { Debug.append("Opponent " + opponent + " auto-oneupped", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } } else if (bidAmountFacedWith > halfThreshold) { Debug.append("Opponent " + opponent + " auto-challenged", logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } else if (decision == 0) { Debug.append("Opponent " + opponent + " flip-challenged", logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } else { if (decisionTwo == 0) { Debug.append("Opponent " + opponent + " flip-minbid", logging); - return opponentMinBid(bidSuitCodeFacedWith, bidAmountFacedWith, includeMoons, includeStars); + return opponentMinBid(opponent, bidSuitFacedWith, bidAmountFacedWith, includeMoons, includeStars); } else { Debug.append("Opponent " + opponent + " flip-oneupped", logging); - return opponentOneUp(bidSuitCodeFacedWith, bidAmountFacedWith, logging); + return opponentOneUp(opponent, bidSuitFacedWith, bidAmountFacedWith, logging); } } } @@ -490,68 +497,19 @@ else if (maxEv > bidAmountFacedWith - 1) } } } - - /*private static void updateVariables(boolean challenge) - { - if (challenge) - { - //do nothing - } - else - { - EntSimulator.setLastBidSuitCode(lastBidSuitCode); - EntSimulator.setLastBidAmount(lastBidAmount); - - Debug.append("Bid: " + new EntropyBid(lastBidSuitCode, lastBidAmount), logging); - } - }*/ - - /*private static void initVariablesForSimulation() - { - //opponentOneNumberOfCards = EntSimulator.getOpponentOneNumberOfCards(); - //playerNumberOfCards = EntSimulator.getOpponentZeroNumberOfCards(); - //opponentTwoNumberOfCards = EntSimulator.getOpponentTwoNumberOfCards(); - //opponentThreeNumberOfCards = EntSimulator.getOpponentThreeNumberOfCards(); - //opponentOneName = "CPU 1"; - //opponentTwoName = "CPU 2"; - //opponentThreeName = "CPU 3"; - - //lastBidAmount = EntSimulator.getLastBidAmount(); - //lastBidSuitCode = EntSimulator.getLastBidSuitCode(); - - //jokerValue = SimulationDialog.getJokerValue(); - //opponentHand = EntSimulator.getOpponentOneHand(); - //opponentTwoHand = EntSimulator.getOpponentTwoHand(); - //opponentThreeHand = EntSimulator.getOpponentThreeHand(); - //playerHand = EntSimulator.getOpponentZeroHand(); - //opponentHands = new String[][] {playerHand, opponentHand, opponentTwoHand, opponentThreeHand}; - //includeJokers = SimulationDialog.getIncludeJokers(); - //includeMoons = SimulationDialog.getIncludeMoons(); - //includeStars = SimulationDialog.getIncludeStars(); - }*/ - private static EntropyBid opponentMinBid(int bidSuitCodeFacedWith, int bidAmount, + private static EntropyBidAction opponentMinBid(Player opponent, Suit bidSuitFacedWith, int bidAmount, boolean includeMoons, boolean includeStars) { - if (bidSuitCodeFacedWith == 2 && !includeMoons) - { - return new EntropyBid(bidSuitCodeFacedWith + 2, bidAmount); - } - else if ((bidSuitCodeFacedWith == 4 && !includeStars) - || bidSuitCodeFacedWith == 5) - { - return new EntropyBid(0, bidAmount + 1); - } - else - { - return new EntropyBid(bidSuitCodeFacedWith + 1, bidAmount); - } + var nextSuit = bidSuitFacedWith.next(includeMoons, includeStars); + var myAmount = nextSuit.lessThan(bidSuitFacedWith) ? bidAmount + 1 : bidAmount; + return new EntropyBidAction(opponent.getName(), false, myAmount, nextSuit); } - private static EntropyBid opponentOneUp(int bidSuitCodeFacedWith, int bidAmount, boolean logging) + private static EntropyBidAction opponentOneUp(Player opponent, Suit bidSuitFacedWith, int bidAmount, boolean logging) { Debug.append("One Up", logging); - return new EntropyBid(bidSuitCodeFacedWith, bidAmount + 1); + return new EntropyBidAction(opponent.getName(), false, bidAmount + 1, bidSuitFacedWith); } private static EntropyBid opponentMinBidSuit(int bidSuitCodeFacedWith, int bidAmountFacedWith, int suitCode, boolean logging) diff --git a/client/src/main/java/util/MarkStrategySuitWrapper.java b/client/src/main/java/util/MarkStrategySuitWrapper.java index 1038e923..3b5949c7 100644 --- a/client/src/main/java/util/MarkStrategySuitWrapper.java +++ b/client/src/main/java/util/MarkStrategySuitWrapper.java @@ -5,33 +5,21 @@ import java.util.List; import java.util.Random; +import game.EntropyBidAction; +import game.Suit; import object.EntropyBid; public class MarkStrategySuitWrapper { private HashMap hmSuitCountBySuitCode = new HashMap<>(); - private ArrayList applicableSuits = new ArrayList<>(); + private List applicableSuits; private ArrayList suitsPossibleToMinBid = new ArrayList<>(); private int bestSuit = -1; private int worstSuit = -1; - public MarkStrategySuitWrapper(List hand, int jokerValue, boolean includeMoons, boolean includeStars, EntropyBid bid) + public MarkStrategySuitWrapper(List hand, int jokerValue, boolean includeMoons, boolean includeStars, EntropyBidAction bid) { - applicableSuits.add(CardsUtil.SUIT_CLUBS); - applicableSuits.add(CardsUtil.SUIT_DIAMONDS); - applicableSuits.add(CardsUtil.SUIT_HEARTS); - - if (includeMoons) - { - applicableSuits.add(CardsUtil.SUIT_MOONS); - } - - applicableSuits.add(CardsUtil.SUIT_SPADES); - - if (includeStars) - { - applicableSuits.add(CardsUtil.SUIT_STARS); - } + applicableSuits = Suit.filter(includeMoons, includeStars); int bestSuitCount = 0; for (int i=0; i= 3) - { - suit++; - } - - return suit; - } - else - { - int suit = rand.nextInt(4); - if (suit == 3) - { - suit++; - } - - return suit; - } - } -} diff --git a/core/src/main/java/util/StrategyParms.java b/core/src/main/java/util/StrategyParms.java index 27976412..b18ccbab 100644 --- a/core/src/main/java/util/StrategyParms.java +++ b/core/src/main/java/util/StrategyParms.java @@ -1,5 +1,6 @@ package util; +import game.BidAction; import game.GameMode; import object.Bid; import object.Player; @@ -19,7 +20,7 @@ public class StrategyParms private boolean includeStars = false; private boolean negativeJacks = false; private boolean cardReveal = false; - private Bid lastBid = null; + private BidAction lastBid = null; private boolean logging = true; private ArrayList cardsOnShowFromOpponents = new ArrayList<>(); @@ -114,11 +115,11 @@ public void setCardReveal(boolean cardReveal) { this.cardReveal = cardReveal; } - public Bid getLastBid() + public BidAction getLastBid() { return lastBid; } - public void setLastBid(Bid lastBid) + public void setLastBid(BidAction lastBid) { this.lastBid = lastBid; } diff --git a/core/src/main/kotlin/game/Suit.kt b/core/src/main/kotlin/game/Suit.kt index b4d41481..d32e9dc1 100644 --- a/core/src/main/kotlin/game/Suit.kt +++ b/core/src/main/kotlin/game/Suit.kt @@ -43,13 +43,20 @@ enum class Suit( fun lessThan(other: Suit) = this < other fun next(includeMoons: Boolean, includeStars: Boolean): Suit { - val nextIx = (Suit.entries.indexOf(this) + 1) % Suit.entries.size - val next = Suit.entries[nextIx] - - return if ((next == Moons && !includeMoons) || next == Stars && !includeStars) { - next.next(includeMoons, includeStars) - } else { - next - } + val filtered = Suit.filter(includeMoons, includeStars) + val nextIx = (filtered.indexOf(this) + 1) % filtered.size + return filtered[nextIx] + } + + companion object { + @JvmStatic + fun filter(includeMoons: Boolean, includeStars: Boolean) = + Suit.entries.filter { + (it != Suit.Moons || includeMoons) && (it != Suit.Stars || includeStars) + } + + @JvmStatic + fun random(includeMoons: Boolean, includeStars: Boolean) = + filter(includeMoons, includeStars).random() } } From 9c8ecf65d897b05f03472c7913e568c51935092e Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 28 May 2025 08:53:32 +0100 Subject: [PATCH 13/16] more WIP, into the strat stuff --- .../src/main/java/util/EntCpuStrategies.java | 119 +++++++-------- .../java/util/MarkStrategySuitWrapper.java | 143 ------------------ .../src/main/java/util/VectCpuStrategies.java | 12 +- .../strategy/MarkStrategySuitWrapper.kt | 44 ++++++ core/src/main/java/util/CardsUtil.java | 65 -------- core/src/main/kotlin/game/CardsUtil.kt | 27 ++++ 6 files changed, 133 insertions(+), 277 deletions(-) delete mode 100644 client/src/main/java/util/MarkStrategySuitWrapper.java create mode 100644 client/src/main/kotlin/strategy/MarkStrategySuitWrapper.kt diff --git a/client/src/main/java/util/EntCpuStrategies.java b/client/src/main/java/util/EntCpuStrategies.java index 449e8e8c..458337a3 100644 --- a/client/src/main/java/util/EntCpuStrategies.java +++ b/client/src/main/java/util/EntCpuStrategies.java @@ -1,18 +1,18 @@ package util; -import java.util.*; - import game.ChallengeAction; import game.EntropyBidAction; import game.PlayerAction; import game.Suit; -import object.Bid; import object.ChallengeBid; -import object.EntropyBid; import object.Player; +import strategy.MarkStrategySuitWrapper; + +import java.util.*; import static game.CardsUtilKt.countSuit; -import static util.StrategyUtilKt.getRandomSuit; +import static game.CardsUtilKt.getEvMap; +import static strategy.MarkStrategySuitWrapperKt.factoryMarkStrategySuitWrapper; public class EntCpuStrategies { @@ -77,12 +77,12 @@ private static PlayerAction processMarkTurn(Player opponent, StrategyParms parms Debug.append("Mark strategy for this turn", logging); - MarkStrategySuitWrapper suitWrapper = new MarkStrategySuitWrapper(hand, jokerValue, includeMoons, includeStars, bid); + MarkStrategySuitWrapper suitWrapper = factoryMarkStrategySuitWrapper(hand, jokerValue, includeMoons, includeStars, bid); Debug.append("SuitWrapper: " + suitWrapper, logging); - int bestSuit = suitWrapper.getBestSuit(); - int bestSuitCount = CardsUtil.countSuit(hand, bestSuit, jokerValue); - int worstSuit = suitWrapper.getWorstSuit(); + var bestSuit = suitWrapper.getBestSuit(); + int bestSuitCount = countSuit(bestSuit, hand, jokerValue); + var worstSuit = suitWrapper.getWorstSuit(); int suitsInPlay = 4 + (includeMoons?1:0) + (includeStars?1:0); int halfThreshold = (int) Math.ceil(totalCards/2); @@ -91,10 +91,10 @@ private static PlayerAction processMarkTurn(Player opponent, StrategyParms parms if (bid == null) { Debug.append("Starting this round", logging); - int suitCode = getSuitForMarkBid(suitWrapper, logging); + var suit = getSuitForMarkBid(suitWrapper, logging); int bidAmount = markBid(halfThreshold, (int)totalCards, suitsInPlay); - return new EntropyBid(suitCode, bidAmount); + return new EntropyBidAction(opponent.getName(), false, bidAmount, suit); } else { @@ -104,8 +104,8 @@ private static PlayerAction processMarkTurn(Player opponent, StrategyParms parms int bidAmountFacedWith = bid.getAmount(); var bidSuitFacedWith = bid.getSuit(); - int bidSuitCount = CardsUtil.countSuit(hand, bidSuitCodeFacedWith, jokerValue); - int minBiddableSuitCount = suitWrapper.getCountOfSuitsPossibleToMinbid(); + int bidSuitCount = countSuit(bidSuitFacedWith, hand, jokerValue); + int minBiddableSuitCount = suitWrapper.getSuitsPossibleToMinBid().size(); int choice = rand.nextInt(4); if (minBiddableSuitCount == suitsInPlay) @@ -120,22 +120,22 @@ private static PlayerAction processMarkTurn(Player opponent, StrategyParms parms else if (choice == 2) { Debug.append("Minbidding best or worst (25%)", logging); - if (bestSuit == bidSuitCodeFacedWith) + if (bestSuit == bidSuitFacedWith) { Debug.append("BestSuit = BidSuitCodeFacedWith = " + bestSuit + ". Bidding worstSuit: " + worstSuit, logging); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, worstSuit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, worstSuit, logging); } else { Debug.append("Bidding bestSuit as it's different from what I'm faced with.", logging); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, bestSuit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, bestSuit, logging); } } else { Debug.append("Minbid random middle suit (25%)", logging); - int suitToBid = suitWrapper.getRandomMiddleSuit(); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suitToBid, logging); + var suitToBid = suitWrapper.getRandomMiddleSuit(); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suitToBid, logging); } } else if (minBiddableSuitCount >= 3) @@ -152,8 +152,8 @@ else if (minBiddableSuitCount >= 3) else { Debug.append("Bidding random other suit (50%)", logging); - int suitToBid = suitWrapper.getRandomSuitExcluding(bidSuitCodeFacedWith); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suitToBid, logging); + var suitToBid = suitWrapper.randomSuitNot(bidSuitFacedWith); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suitToBid, logging); } } else @@ -162,13 +162,13 @@ else if (minBiddableSuitCount >= 3) if (choice <= 1) { Debug.append("Minbidding my best suit (50%)", logging); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, bestSuit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, bestSuit, logging); } else { Debug.append("Minbidding random other suit (50%)", logging); - int suitToBid = suitWrapper.getRandomSuitExcluding(bestSuit); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suitToBid, logging); + var suitToBid = suitWrapper.randomSuitNot(bestSuit); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suitToBid, logging); } } } @@ -184,15 +184,15 @@ else if (minBiddableSuitCount == 2) { ///minbid one of the two suits I can Debug.append("Minbidding one of the two suits (50%)", logging); - int suitToBid = suitWrapper.getRandomSuitPossibleToMinbid(); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suitToBid, logging); + var suitToBid = suitWrapper.randomSuitPossibleToMinBid(); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suitToBid, logging); } } else if (minBiddableSuitCount == 1) { - int suitToBid = suitWrapper.getRandomSuitPossibleToMinbid(); + var suitToBid = suitWrapper.randomSuitPossibleToMinBid(); Debug.append("Could only minbid suit " + suitToBid + ", so minbidding that.", logging); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suitToBid, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suitToBid, logging); } else { @@ -236,31 +236,31 @@ else if (bidAmountFacedWith > 1 + quarterThreshold) else { Debug.append("Minbidding my best suit (50%)", logging); - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, bestSuit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, bestSuit, logging); } } } } } - private static int getSuitForMarkBid(MarkStrategySuitWrapper suitWrapper, boolean logging) + private static Suit getSuitForMarkBid(MarkStrategySuitWrapper suitWrapper, boolean logging) { Random rand = new Random(); int choice = rand.nextInt(3); if (choice == 0) { - int bestSuit = suitWrapper.getBestSuit(); + var bestSuit = suitWrapper.getBestSuit(); Debug.append("Chose best suit: " + bestSuit, logging); return bestSuit; } else if (choice == 1) { - int worstSuit = suitWrapper.getWorstSuit(); + var worstSuit = suitWrapper.getWorstSuit(); Debug.append("Chose worst suit: " + worstSuit, logging); return worstSuit; } else { - int randomSuit = suitWrapper.getRandomMiddleSuit(); + var randomSuit = suitWrapper.getRandomMiddleSuit(); Debug.append("Chose random middle suit: " + randomSuit, logging); return randomSuit; } @@ -385,17 +385,17 @@ else if (decision == 0) } } - private static Bid processEvTurnAndRevealCard(Player opponent, StrategyParms parms) + private static PlayerAction processEvTurnAndRevealCard(Player opponent, StrategyParms parms) { - Bid bid = processEvTurn(opponent, parms); + PlayerAction bid = processEvTurn(opponent, parms); CpuStrategies.setCardToReveal(bid, parms, opponent); return bid; } - private static Bid processEvTurn(Player opponent, StrategyParms parms) + private static PlayerAction processEvTurn(Player opponent, StrategyParms parms) { //Parms - EntropyBid bid = (EntropyBid)parms.getLastBid(); + EntropyBidAction bid = (EntropyBidAction)parms.getLastBid(); int totalCards = parms.getTotalNumberOfCards(); boolean includeMoons = parms.getIncludeMoons(); boolean includeStars = parms.getIncludeStars(); @@ -410,28 +410,29 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) if (bid == null) { Debug.append("Starting this round", logging); - HashMap hmEvBySuit = CardsUtil.getEvBySuitHashMapIncludingMyHand(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms); - int suitCode = StrategyUtil.getRandomSuit(includeMoons, includeStars); - double suitEv = hmEvBySuit.get(suitCode); + var suit = Suit.random(includeMoons, includeStars); + double suitEv = hmEvBySuit.get(suit); int suitEvRounded = (int) Math.ceil(suitEv); int randomAmount = coin.nextInt(5) - 4; //-4, -3, -2, 1, 0 int bidAmount = Math.max(1, suitEvRounded + randomAmount); - return new EntropyBid(suitCode, bidAmount); + return new EntropyBidAction(opponent.getName(), false, bidAmount, suit); } else { hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - HashMap hmEvBySuit = CardsUtil.getEvBySuitHashMapIncludingMyHand(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms); - int bidAmountFacedWith = bid.getBidAmount(); - int bidSuitCodeFacedWith = bid.getBidSuitCode(); - double expectedValueForBid = hmEvBySuit.get(bidSuitCodeFacedWith); - - Debug.append("EV calculation for bid of " + bidAmountFacedWith + " " + CardsUtil.getSuitDesc(bidAmountFacedWith, bidSuitCodeFacedWith) + ": " + expectedValueForBid, logging); + int bidAmountFacedWith = bid.getAmount(); + var bidSuitFacedWith = bid.getSuit(); + double expectedValueForBid = hmEvBySuit.get(bidSuitFacedWith); + Debug.append("EV calculation for bid of " + bid.plainString() + ": " + expectedValueForBid, logging); + + Suit.filter(includeMoons, includeStars).stream().max((suit) -> hmEvBySuit.get(suit)); ArrayList suits = CardsUtil.getSuitCodesVector(includeMoons, includeStars); int suitsSize = suits.size(); double maxEv = 0; @@ -459,7 +460,7 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) if (bidAmountFacedWith > expectedValueForBid + 1) { - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } else { @@ -470,7 +471,7 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) if (maxEv > bidAmountFacedWith - 1 && totalOpponentCards > 1) { - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suit, logging); } else if (maxEv > bidAmountFacedWith - 1) { @@ -481,18 +482,18 @@ else if (maxEv > bidAmountFacedWith - 1) if (amountRequiredInOneCard < 2) { - return opponentMinBidSuit(bidSuitCodeFacedWith, bidAmountFacedWith, suit, logging); + return opponentMinBidSuit(opponent, bidSuitFacedWith, bidAmountFacedWith, suit, logging); } else { Debug.append("Bidding would've needed >1 in one card, so challenged", logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } } else { Debug.append("Couldn't bid anything 'safely', so challenged.", logging); - return new ChallengeBid(); + return new ChallengeAction(opponent.getName()); } } } @@ -512,20 +513,12 @@ private static EntropyBidAction opponentOneUp(Player opponent, Suit bidSuitFaced return new EntropyBidAction(opponent.getName(), false, bidAmount + 1, bidSuitFacedWith); } - private static EntropyBid opponentMinBidSuit(int bidSuitCodeFacedWith, int bidAmountFacedWith, int suitCode, boolean logging) + private static EntropyBidAction opponentMinBidSuit(Player opponent, Suit bidSuitFacedWith, int bidAmountFacedWith, Suit desiredSuit, boolean logging) { - Debug.append("MinBidSuit " + suitCode, logging); + Debug.append("MinBidSuit " + desiredSuit, logging); - int bidAmount = 0; - if (suitCode > bidSuitCodeFacedWith) - { - bidAmount = bidAmountFacedWith; - } - else - { - bidAmount = bidAmountFacedWith + 1; - } + var bidAmount = desiredSuit.lessThan(bidSuitFacedWith) ? bidAmountFacedWith + 1 : bidAmountFacedWith; - return new EntropyBid(suitCode, bidAmount); + return new EntropyBidAction(opponent.getName(), false, bidAmount, desiredSuit); } } \ No newline at end of file diff --git a/client/src/main/java/util/MarkStrategySuitWrapper.java b/client/src/main/java/util/MarkStrategySuitWrapper.java deleted file mode 100644 index 3b5949c7..00000000 --- a/client/src/main/java/util/MarkStrategySuitWrapper.java +++ /dev/null @@ -1,143 +0,0 @@ -package util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Random; - -import game.EntropyBidAction; -import game.Suit; -import object.EntropyBid; - -public class MarkStrategySuitWrapper -{ - private HashMap hmSuitCountBySuitCode = new HashMap<>(); - private List applicableSuits; - private ArrayList suitsPossibleToMinBid = new ArrayList<>(); - private int bestSuit = -1; - private int worstSuit = -1; - - public MarkStrategySuitWrapper(List hand, int jokerValue, boolean includeMoons, boolean includeStars, EntropyBidAction bid) - { - applicableSuits = Suit.filter(includeMoons, includeStars); - - int bestSuitCount = 0; - for (int i=0; i= bestSuitCount) - { - bestSuit = suit; - bestSuitCount = count; - } - } - - int worstSuitCount = 1000; - for (int i=applicableSuits.size()-1; i>=0; i--) - { - int suit = applicableSuits.get(i); - int count = CardsUtil.countSuit(hand, suit, jokerValue); - if (count <= worstSuitCount) - { - worstSuit = suit; - worstSuitCount = count; - } - } - - initialiseSuitsPossibleToMinBid(bid); - } - - private void initialiseSuitsPossibleToMinBid(EntropyBid bid) - { - //If we're starting this round, we don't care - if (bid == null) - { - return; - } - - int size = applicableSuits.size(); - for (int i=0; i bid.getBidSuitCode() - && amount >= bid.getBidAmount()) - { - suitsPossibleToMinBid.add(suitCode); - } - else if (amount > bid.getBidAmount()) - { - suitsPossibleToMinBid.add(suitCode); - } - } - } - - public int getBestSuit() - { - return bestSuit; - } - public int getWorstSuit() - { - return worstSuit; - } - - public int getRandomMiddleSuit() - { - ArrayList suitsToChooseFrom = new ArrayList<>(); - suitsToChooseFrom.addAll(applicableSuits); - suitsToChooseFrom.remove(Integer.valueOf(bestSuit)); - suitsToChooseFrom.remove(Integer.valueOf(worstSuit)); - - int size = suitsToChooseFrom.size(); - Random rand = new Random(); - int choice = rand.nextInt(size); - - return suitsToChooseFrom.get(choice); - } - - public int getRandomSuitExcluding(int... suitsToExclude) - { - ArrayList suitsToChooseFrom = new ArrayList<>(); - suitsToChooseFrom.addAll(applicableSuits); - - for (int i=0; i hmEvBySuit = CardsUtil.getEvBySuitHashMapIncludingMyHand(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms); Debug.append("EV HashMap = " + hmEvBySuit, logging); int clubsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_CLUBS, hmEvBySuit); @@ -227,7 +227,7 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) else { hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - HashMap hmEvBySuit = CardsUtil.getEvBySuitHashMapIncludingMyHand(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms); Debug.append("EV HashMap = " + hmEvBySuit, logging); double[] diffVector = VectropyUtil.getEvDifferenceVector(lastBid, hmEvBySuit, includeMoons, includeStars); diff --git a/client/src/main/kotlin/strategy/MarkStrategySuitWrapper.kt b/client/src/main/kotlin/strategy/MarkStrategySuitWrapper.kt new file mode 100644 index 00000000..b603c213 --- /dev/null +++ b/client/src/main/kotlin/strategy/MarkStrategySuitWrapper.kt @@ -0,0 +1,44 @@ +package strategy + +import game.EntropyBidAction +import game.Suit +import game.countSuit + +data class MarkStrategySuitWrapper( + val applicableSuits: List, + val bestSuit: Suit, + val worstSuit: Suit, + val suitsPossibleToMinBid: List, +) { + fun getRandomMiddleSuit() = + applicableSuits.filterNot { it == bestSuit || it == worstSuit }.random() + + fun randomSuitPossibleToMinBid() = suitsPossibleToMinBid.random() + + fun randomSuitNot(suitToExclude: Suit) = + applicableSuits.filterNot { it == suitToExclude }.random() +} + +fun factoryMarkStrategySuitWrapper( + hand: List, + jokerValue: Int, + includeMoons: Boolean, + includeStars: Boolean, + bid: EntropyBidAction?, +): MarkStrategySuitWrapper { + val applicableSuits = Suit.filter(includeMoons, includeStars) + + val bestSuit = applicableSuits.reversed().maxBy { countSuit(it, hand, jokerValue) } + val worstSuit = applicableSuits.minBy { countSuit(it, hand, jokerValue) } + + val suitsPossibleToMinBid: List = + if (bid == null) emptyList() + else { + applicableSuits.filter { suit -> + val myCount = countSuit(suit, hand, jokerValue) + myCount > bid.amount || (myCount == bid.amount && suit > bid.suit) + } + } + + return MarkStrategySuitWrapper(applicableSuits, bestSuit, worstSuit, suitsPossibleToMinBid) +} diff --git a/core/src/main/java/util/CardsUtil.java b/core/src/main/java/util/CardsUtil.java index 616ad275..ac550108 100644 --- a/core/src/main/java/util/CardsUtil.java +++ b/core/src/main/java/util/CardsUtil.java @@ -154,71 +154,6 @@ public static List createAndShuffleDeck(int jokerQuantity, return list; } - - public static HashMap getEvBySuitHashMapIncludingMyHand(List hand, StrategyParms parms) - { - double evClubs = getExpectedValueForSuitIncludingMyHand(hand, SUIT_CLUBS, parms); - double evDiamonds = getExpectedValueForSuitIncludingMyHand(hand, SUIT_DIAMONDS, parms); - double evHearts = getExpectedValueForSuitIncludingMyHand(hand, SUIT_HEARTS, parms); - double evMoons = getExpectedValueForSuitIncludingMyHand(hand, SUIT_MOONS, parms); - double evSpades = getExpectedValueForSuitIncludingMyHand(hand, SUIT_SPADES, parms); - double evStars = getExpectedValueForSuitIncludingMyHand(hand, SUIT_STARS, parms); - - HashMap hmEvBySuit = new HashMap<>(); - hmEvBySuit.put(0, evClubs); - hmEvBySuit.put(1, evDiamonds); - hmEvBySuit.put(2, evHearts); - hmEvBySuit.put(3, evMoons); - hmEvBySuit.put(4, evSpades); - hmEvBySuit.put(5, evStars); - - return hmEvBySuit; - } - - private static double getExpectedValueForSuitIncludingMyHand(List hand, int suitCode, StrategyParms parms) - { - double ev = 0; - - int jokerQuantity = parms.getJokerQuantity(); - int jokerValue = parms.getJokerValue(); - boolean includeMoons = parms.getIncludeMoons(); - boolean includeStars = parms.getIncludeStars(); - boolean negativeJacks = parms.getNegativeJacks(); - - List deck = createAndShuffleDeck(jokerQuantity, includeMoons, includeStars, negativeJacks); - - int handSize = hand.size(); - int totalCardsNotIncludingMine = parms.getTotalNumberOfCards() - handSize; - - //go through the deck and take out the cards that we can see - for (int i=0; i cardToCheck = new ArrayList<>(); - cardToCheck.add(deck.get(i)); - ev += countSuit(cardToCheck, suitCode, jokerValue); - } - - return countSuit(hand, suitCode, jokerValue) + (ev * totalCardsNotIncludingMine)/remainingCards; - } public static int countClubs(List hand, int jokerValue) { diff --git a/core/src/main/kotlin/game/CardsUtil.kt b/core/src/main/kotlin/game/CardsUtil.kt index ba07982b..7b30e6d4 100644 --- a/core/src/main/kotlin/game/CardsUtil.kt +++ b/core/src/main/kotlin/game/CardsUtil.kt @@ -1,5 +1,8 @@ package game +import util.CardsUtil +import util.StrategyParms + fun countSuit(suit: Suit, cards: List, jokerValue: Int) = cards.sumOf { countContribution(suit, it, jokerValue) } @@ -10,3 +13,27 @@ fun countContribution(suit: Suit, card: String, jokerValue: Int) = else if (card == "-J${suit.letter}") -1 else if (card.contains(suit.letter)) 1 else 0 fun isCardRelevant(card: String, suit: Suit) = countContribution(suit, card, 1) > 0 + +fun getEvMap(visibleCards: List, strategyParams: StrategyParms): Map { + val unknownCardsInPlay = strategyParams.totalNumberOfCards - visibleCards.size + val remainingDeck = + CardsUtil.createAndShuffleDeck( + strategyParams.jokerQuantity, + strategyParams.includeMoons, + strategyParams.includeStars, + strategyParams.negativeJacks, + ) + .filterNot { visibleCards.contains(it) } + + return Suit.entries.associateWith { suit -> + val known = countSuit(suit, visibleCards, strategyParams.jokerValue) + val possibleOthers = + remainingDeck + .sumOf { countContribution(suit, it, strategyParams.jokerValue) } + .toDouble() + val extraEv = + possibleOthers * (unknownCardsInPlay.toDouble() / remainingDeck.size.toDouble()) + + extraEv + known + } +} From d2af2c53b419bddc518ff1166c7b4cf42526ac02 Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 3 Jun 2025 08:59:26 +0100 Subject: [PATCH 14/16] more WIP, into the strat stuff --- .../main/java/online/screen/EntropyRoom.java | 20 +++--- .../src/main/java/online/screen/GameRoom.java | 47 +++++++------- .../main/java/online/screen/VectropyRoom.java | 9 +-- client/src/main/java/screen/BidPanel.java | 2 +- client/src/main/java/screen/GameScreen.java | 46 ++++++------- client/src/main/java/screen/MainScreen.java | 7 +- client/src/main/java/util/BidListener.java | 4 +- client/src/main/java/util/CpuStrategies.java | 64 ++++++++++--------- .../src/main/java/util/EntCpuStrategies.java | 16 ++--- client/src/main/kotlin/http/RoomApi.kt | 20 +++--- core/src/main/kotlin/game/BidAction.kt | 1 - core/src/main/kotlin/game/CardsUtil.kt | 3 +- core/src/main/kotlin/game/ChallengeAction.kt | 3 +- core/src/main/kotlin/game/EntropyBidAction.kt | 2 +- core/src/main/kotlin/game/IllegalAction.kt | 3 +- core/src/main/kotlin/game/LeaveAction.kt | 2 + core/src/main/kotlin/game/PlayerAction.kt | 1 + 17 files changed, 125 insertions(+), 125 deletions(-) diff --git a/client/src/main/java/online/screen/EntropyRoom.java b/client/src/main/java/online/screen/EntropyRoom.java index 321c8212..dd058341 100644 --- a/client/src/main/java/online/screen/EntropyRoom.java +++ b/client/src/main/java/online/screen/EntropyRoom.java @@ -3,6 +3,8 @@ import java.awt.BorderLayout; import java.util.UUID; +import game.CardsUtilKt; +import game.EntropyBidAction; import game.GameSettings; import object.Bid; import object.EntropyAchievementsTracker; @@ -13,7 +15,7 @@ import util.Registry; import util.ReplayConstants; -public class EntropyRoom extends GameRoom +public class EntropyRoom extends GameRoom { private EntropyAchievementsTracker achievementTracker = new EntropyAchievementsTracker(); @@ -35,7 +37,7 @@ public void doSpecificResetGameVariables() @Override public void resetBids() { - lastBid = new EntropyBid(0, 0); + lastBid = null; hmBidByPlayerNumber.clear(); } @@ -44,13 +46,13 @@ public void updateScreenForChallengeOrIllegal() { if (isVisible()) { - int lastBidSuitCode = ((EntropyBid)lastBid).getBidSuitCode(); - handPanel.displayAndHighlightHands(lastBidSuitCode); + var lastBidSuit = lastBid.getSuit(); + handPanel.displayAndHighlightHands(lastBidSuit); achievementTracker.unlockPerfectBidAchievements(earnedPsychic); - int total = CardsUtil.countSuit(lastBidSuitCode, hmHandByAdjustedPlayerNumber, getJokerValue()); - String suitsStr = CardsUtil.getSuitDesc(total, lastBidSuitCode); + int total = CardsUtilKt.countSuit(lastBidSuit, allCards(), getJokerValue()); + String suitsStr = lastBidSuit.getDescription(total == 1); if (total == 1) { showResult("There was " + total + " " + suitsStr); @@ -67,18 +69,18 @@ public void saveModeSpecificVariablesForReplay() { int roundsSoFar = replay.getInt(Registry.REPLAY_INT_ROUNDS_SO_FAR, 0); replay.putInt(Registry.REPLAY_INT_GAME_MODE, ReplayConstants.GAME_MODE_ENTROPY_ONLINE); - replay.putInt(roundsSoFar + Registry.REPLAY_INT_LAST_BID_SUIT_CODE, ((EntropyBid)lastBid).getBidSuitCode()); + replay.putInt(roundsSoFar + Registry.REPLAY_INT_LAST_BID_SUIT_CODE, lastBid.getSuit().getLegacyCode()); replayDialog.roundAdded(); } @Override - public void updatePerfectBidVariables(Bid bid) + public void updatePerfectBidVariables(EntropyBidAction bid) { achievementTracker.updatePerfectBidVariables(bid); } @Override - public void updateAchievementVariables(Bid bid) + public void updateAchievementVariables(EntropyBidAction bid) { achievementTracker.update(bid); } diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index f8c3c9ba..0572ce2b 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -1,6 +1,7 @@ package online.screen; import game.BidAction; +import game.ChallengeAction; import game.GameMode; import game.GameSettings; import http.dto.RoomSummary; @@ -24,16 +25,17 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.prefs.Preferences; +import java.util.stream.Collectors; import static utils.CoreGlobals.logger; /** * This is an actual room as seen by the player */ -public abstract class GameRoom extends JFrame +public abstract class GameRoom> extends JFrame implements WindowListener, ActionListener, - BidListener, + BidListener, RevealListener, Registry { @@ -60,7 +62,7 @@ public abstract class GameRoom extends JFrame public boolean hasOverbid = false; private boolean seenRoundStart = false; - public Bid lastBid = null; + public B lastBid = null; private ConcurrentHashMap hmPlayerByAdjustedPlayerNumber = new ConcurrentHashMap<>(); public ConcurrentHashMap> hmHandByAdjustedPlayerNumber = new ConcurrentHashMap<>(); @@ -205,8 +207,8 @@ else if (mode == GameMode.Vectropy) public abstract void resetBids(); public abstract void doSpecificResetGameVariables(); public abstract void saveModeSpecificVariablesForReplay(); - public abstract void updatePerfectBidVariables(Bid bid); - public abstract void updateAchievementVariables(Bid bid); + public abstract void updatePerfectBidVariables(B bid); + public abstract void updateAchievementVariables(B bid); public abstract void unlockEndOfGameAchievements(); private void setIcon() @@ -791,7 +793,7 @@ public void handleBid(int playerNumber, BidAction bid) addBidToBidBox(playerNumberAdjusted, bid); handPanel.selectPlayerInAwtThread(playerNumberAdjusted, false); - if (bid.isChallenge() + if (bid instanceof ChallengeAction || bid.isIllegal()) { processChallengeOrIllegal(); @@ -994,21 +996,25 @@ else if (playerNumberAdjusted == playerNumberLocal) } } } - - private int getTotalFromHands() - { - int total = 0; - + + protected List allCards() { + ArrayList cards = new ArrayList<>(); + for (int i=0; i hand = hmHandByAdjustedPlayerNumber.get(i); if (hand != null) { - total += hand.size(); + cards.addAll(hand); } } - - return total; + + return cards; + } + + private int getTotalFromHands() + { + return allCards().size(); } public void setObserver(boolean observer) @@ -1373,12 +1379,10 @@ public void requestFocus() * BidListener */ @Override - public void bidMade(BidAction bid) + public void bidMade(B bid) { - //1. Set the player on the bid - Player player = hmPlayerByAdjustedPlayerNumber.get(0); - bid.setPlayer(player); lastBid = bid; + Player player = hmPlayerByAdjustedPlayerNumber.get(0); //2. Disable the bid panel enableBidPanel(false); @@ -1405,9 +1409,8 @@ private void processPlayerBid() //5. Unlock achievements, including specific perfect bid ones updateAchievementVariables(lastBid); - if (lastBid.isPerfect(hmHandByAdjustedPlayerNumber, - settings.getJokerValue(), settings.getIncludeMoons(), settings.getIncludeStars()) - && lastBid.isOverAchievementThreshold()) + if (lastBid.isPerfect(allCards(), settings) + && lastBid.overAchievementThreshold()) { updatePerfectBidVariables(lastBid); @@ -1417,7 +1420,7 @@ private void processPlayerBid() } } - boolean overBid = lastBid.isOverbid(hmHandByAdjustedPlayerNumber, settings.getJokerValue()); + boolean overBid = lastBid.isOverbid(allCards(), settings); if (overBid) { hasOverbid = true; diff --git a/client/src/main/java/online/screen/VectropyRoom.java b/client/src/main/java/online/screen/VectropyRoom.java index d82c1bbe..1300770e 100644 --- a/client/src/main/java/online/screen/VectropyRoom.java +++ b/client/src/main/java/online/screen/VectropyRoom.java @@ -4,11 +4,12 @@ import java.util.UUID; import game.GameSettings; +import game.VectropyBidAction; import object.Bid; import screen.VectropyBidPanel; import util.*; -public class VectropyRoom extends GameRoom +public class VectropyRoom extends GameRoom { private boolean earnedMathematician = false; @@ -30,12 +31,12 @@ public void doSpecificResetGameVariables() @Override public void resetBids() { - lastBid = VectropyUtil.getEmptyBid(getIncludeMoons(), getIncludeStars()); + lastBid = null; hmBidByPlayerNumber.clear(); } @Override - public void updatePerfectBidVariables(Bid bid) + public void updatePerfectBidVariables(VectropyBidAction bid) { earnedMathematician = true; } @@ -67,7 +68,7 @@ public void unlockEndOfGameAchievements() } @Override - public void updateAchievementVariables(Bid bid) + public void updateAchievementVariables(VectropyBidAction bid) { //do nothing } diff --git a/client/src/main/java/screen/BidPanel.java b/client/src/main/java/screen/BidPanel.java index 73541d0b..989fbedf 100644 --- a/client/src/main/java/screen/BidPanel.java +++ b/client/src/main/java/screen/BidPanel.java @@ -16,7 +16,7 @@ public abstract class BidPanel> extends TransparentPanel this.handPanel = handPanel; } - public BidListener listener = null; + public BidListener listener = null; public int maxBid = -1; private boolean logging = false; diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 317f8f6d..e94b05d0 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -881,7 +881,7 @@ private void processPlayerBid() handlePerfectBid(lastBid); } - if (isOverbid(lastBid)) + if (lastBid.isOverbid(allCards(), settings)) { hasOverbid = true; } @@ -889,10 +889,10 @@ private void processPlayerBid() processNextTurn(0); } - private void handlePerfectBid(Bid bid) + private void handlePerfectBid(B bid) { Debug.append("Player made a perfect bid.", logging); - if (bid.isOverAchievementThreshold()) + if (bid.overAchievementThreshold()) { if (handPanel.isPlayingBlind()) { @@ -911,8 +911,7 @@ public void processChallenge(Player challenger) unlockPerfectBidAchievements(); Player playerChallenged = lastBid.getPlayer(); - if (!lastBid.isOverbid(player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), - opponentThree.getHand(), jokerValue)) + if (!lastBid.isOverbid(allCards(), settings)) { Debug.append("not an overbid", logging); setCardsToSubtract(challenger); @@ -1063,12 +1062,6 @@ protected List allCards() { return cards; } - public boolean isOverbid(Bid bid) - { - return bid.isOverbid(player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), - opponentThree.getHand(), jokerValue); - } - public int countSuit(int suitCode) { return CardsUtil.countSuit(suitCode, player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), @@ -1132,10 +1125,8 @@ public void challengeMade() Debug.append("Player challenged.", logging); boolean actedBlind = handPanel.isPlayingBlind(); hasActedBlindThisGame &= actedBlind; - - Bid bid = new ChallengeBid(); - bid.setPlayer(player); - bid.setBlind(actedBlind); + + ChallengeAction bid = new ChallengeAction(player.getName(), actedBlind); addToListmodel(bid); processChallenge(player); @@ -1147,10 +1138,8 @@ public void illegalCalled() Debug.append("Player called Illegal!", logging); boolean actedBlind = handPanel.isPlayingBlind(); hasActedBlindThisGame &= actedBlind; - - Bid bid = new IllegalBid(); - bid.setPlayer(player); - bid.setBlind(actedBlind); + + var bid = new IllegalAction(player.getName(), actedBlind); addToListmodel(bid); processIllegal(player); @@ -1168,9 +1157,9 @@ public void cardRevealed(String card) processPlayerBid(); } - private void addToListmodel(Bid bid) + private void addToListmodel(PlayerAction bid) { - DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); listmodel.add(0, bid); } @@ -1213,18 +1202,18 @@ public void run() Debug.appendBanner("Opponent " + opponent, logging); StrategyParms parms = factoryStrategyParms(opponent); - PlayerAction bid = CpuStrategies.processOpponentTurn(parms, opponent); - if (bid == null) + PlayerAction action = CpuStrategies.processOpponentTurn(parms, opponent); + if (action == null) { //Something's gone wrong - probably an API strategy that timed out or did something invalid. String info = opponent.getName() + " has had their strategy reset to " + CpuStrategies.STRATEGY_BASIC; DialogUtil.showInfo(info); opponent.setStrategy(CpuStrategies.STRATEGY_BASIC); - bid = CpuStrategies.processOpponentTurn(parms, opponent); + action = CpuStrategies.processOpponentTurn(parms, opponent); } - if (bid == null) + if (action == null) { //Something's gone very wrong... handPanel.selectPlayerInAwtThread(opponent.getPlayerNumber(), false); @@ -1232,18 +1221,19 @@ public void run() return; } - addToListmodel(bid); + addToListmodel(action); - if (bid instanceof ChallengeAction) + if (action instanceof ChallengeAction) { processChallenge(opponent); } - else if (bid instanceof IllegalAction) + else if (action instanceof IllegalAction) { processIllegal(opponent); } else { + var bid = (B)action; lastBid = bid; if (settings.getCardReveal()) diff --git a/client/src/main/java/screen/MainScreen.java b/client/src/main/java/screen/MainScreen.java index 4448ff44..9a35db51 100644 --- a/client/src/main/java/screen/MainScreen.java +++ b/client/src/main/java/screen/MainScreen.java @@ -4,6 +4,7 @@ import achievement.AchievementUtilKt; import bean.AbstractDevScreen; import game.GameMode; +import game.PlayerAction; import object.Bid; import object.BidListCellRenderer; import object.Player; @@ -199,9 +200,9 @@ public MainScreen() private final JMenuItem mntmViewLogs = new JMenuItem("View logs..."); //Screen - private final DefaultListModel listmodel = new DefaultListModel<>(); + private final DefaultListModel listmodel = new DefaultListModel<>(); private final JScrollPane scrollPane = new JScrollPane(); - private final JList history = new JList<>(listmodel); + private final JList history = new JList<>(listmodel); private final JLabel lblBidHistory = new JLabel("Bid History"); private final JButton btnNextRound = new JButton("Next"); private final JTextPane lblResult = new JTextPane(); @@ -798,7 +799,7 @@ private void checkForCoward() } } - public DefaultListModel getListmodel() + public DefaultListModel getListmodel() { return listmodel; } diff --git a/client/src/main/java/util/BidListener.java b/client/src/main/java/util/BidListener.java index a04d4a08..4b642af0 100644 --- a/client/src/main/java/util/BidListener.java +++ b/client/src/main/java/util/BidListener.java @@ -2,9 +2,9 @@ import game.BidAction; -public interface BidListener +public interface BidListener> { - public void bidMade(BidAction bid); + public void bidMade(B bid); public void challengeMade(); public void illegalCalled(); } diff --git a/client/src/main/java/util/CpuStrategies.java b/client/src/main/java/util/CpuStrategies.java index 7d118065..f7f7aac1 100644 --- a/client/src/main/java/util/CpuStrategies.java +++ b/client/src/main/java/util/CpuStrategies.java @@ -1,8 +1,6 @@ package util; -import game.BidAction; -import game.GameMode; -import game.PlayerAction; +import game.*; import object.*; import java.util.ArrayList; @@ -129,11 +127,16 @@ else if (entropy) * then just pick one at random. This is what most built-in strategies will do, and implementing for API too * so that worrying about revealing cards is optional. */ - private static void setRandomCardToRevealIfNecessary(Player opponent, Bid bid, StrategyParms parms) + private static void setRandomCardToRevealIfNecessary(Player opponent, PlayerAction action, StrategyParms parms) { + if (!(action instanceof BidAction)) { + return; + } + + var bid = (BidAction)action; if (parms.getCardReveal() && opponent.hasMoreCardsToReveal() - && bid.getCardToReveal().isEmpty()) + && bid.getCardToReveal() == null) { //Pick a card at random to reveal. ArrayList cardsNotOnShow = opponent.getCardsNotOnShow(); @@ -146,33 +149,34 @@ private static void setRandomCardToRevealIfNecessary(Player opponent, Bid bid, S } } - private static String validateBid(Player opponent, Bid bid, StrategyParms parms) + private static String validateBid(Player opponent, PlayerAction action, StrategyParms parms) { - if (bid.isChallenge() - || bid.isIllegal()) + if (action instanceof ChallengeAction + || action instanceof IllegalAction) { - return validateChallengeOrIllegal(bid, parms); + return validateChallengeOrIllegal(action, parms); } - - if (bid instanceof EntropyBid) + + var bid = (BidAction)action; + if (action instanceof EntropyBidAction) { - String error = validateEntropyBid((EntropyBid)bid, parms); + String error = validateEntropyBid((EntropyBidAction)action, parms); if (error != null) { return error; } } - if (bid instanceof VectropyBid) + if (action instanceof VectropyBidAction) { - String error = validateVectropyBid((VectropyBid)bid); + String error = validateVectropyBid((VectropyBidAction)action); if (error != null) { return error; } } - Bid lastBid = parms.getLastBid(); + BidAction lastBid = parms.getLastBid(); if (lastBid != null && !bid.higherThan(lastBid)) { @@ -204,12 +208,12 @@ private static String validateBid(Player opponent, Bid bid, StrategyParms parms) return null; } - private static String validateChallengeOrIllegal(Bid bid, StrategyParms parms) + private static String validateChallengeOrIllegal(PlayerAction bid, StrategyParms parms) { - Bid lastBid = parms.getLastBid(); + var lastBid = parms.getLastBid(); if (lastBid == null) { - if (bid.isChallenge()) + if (bid instanceof ChallengeAction) { return "Challenged as an opening bid."; } @@ -220,28 +224,23 @@ private static String validateChallengeOrIllegal(Bid bid, StrategyParms parms) return null; } - private static String validateEntropyBid(EntropyBid bid, StrategyParms parms) + private static String validateEntropyBid(EntropyBidAction bid, StrategyParms parms) { - int bidSuitCode = bid.getBidSuitCode(); - if (bidSuitCode < CardsUtil.SUIT_CLUBS - || bidSuitCode > CardsUtil.SUIT_STARS) - { - return "Invalid suitCode: " + bidSuitCode; - } + var bidSuit = bid.getSuit(); - int bidAmount = bid.getBidAmount(); + int bidAmount = bid.getAmount(); if (bidAmount < 1) { return "Invalid bidAmount: " + bidAmount; } - if (bidSuitCode == CardsUtil.SUIT_MOONS + if (bidSuit == Suit.Moons && !parms.getIncludeMoons()) { return "Tried to bid Moons when these haven't been included."; } - if (bidSuitCode == CardsUtil.SUIT_STARS + if (bidSuit == Suit.Stars && !parms.getIncludeStars()) { return "Tried to bid Stars when these haven't been included."; @@ -250,19 +249,22 @@ private static String validateEntropyBid(EntropyBid bid, StrategyParms parms) return null; } - private static String validateVectropyBid(VectropyBid bid) + private static String validateVectropyBid(VectropyBidAction bid) { if (bid.getTotal() < 1) { return "Elements sum to less than 1."; } + + var moons = bid.getMoons(); + var stars = bid.getStars(); if (bid.getClubs() < 0 || bid.getDiamonds() < 0 || bid.getHearts() < 0 - || bid.getMoons() < 0 + || (moons != null && moons < 0) || bid.getSpades() < 0 - || bid.getStars() < 0) + || (stars != null && stars < 0)) { return "Negative amount specified for a suit."; } diff --git a/client/src/main/java/util/EntCpuStrategies.java b/client/src/main/java/util/EntCpuStrategies.java index 458337a3..9da5f5e5 100644 --- a/client/src/main/java/util/EntCpuStrategies.java +++ b/client/src/main/java/util/EntCpuStrategies.java @@ -204,12 +204,12 @@ else if (minBiddableSuitCount == 1) if (bidAmountFacedWith > threshold) { Debug.append("Auto-challenging because " + bidAmountFacedWith + " > " + threshold, logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } else if (bidAmountFacedWith > bestSuitCount + bidSuitCount + quarterThreshold) { Debug.append("Auto-challenging because bestSuitCount + bidSuitCount + quarterThreshold = " + (bestSuitCount + bidSuitCount + quarterThreshold), logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } else if (bidAmountFacedWith > 1 + quarterThreshold) { @@ -222,7 +222,7 @@ else if (bidAmountFacedWith > 1 + quarterThreshold) else { Debug.append("Challenging (25%)", logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } } else @@ -362,12 +362,12 @@ else if (bidAmountFacedWith < bidSuitCount + thirdThreshold) else if (bidAmountFacedWith > halfThreshold) { Debug.append("Opponent " + opponent + " auto-challenged", logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } else if (decision == 0) { Debug.append("Opponent " + opponent + " flip-challenged", logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } else { @@ -460,7 +460,7 @@ private static PlayerAction processEvTurn(Player opponent, StrategyParms parms) if (bidAmountFacedWith > expectedValueForBid + 1) { - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } else { @@ -487,13 +487,13 @@ else if (maxEv > bidAmountFacedWith - 1) else { Debug.append("Bidding would've needed >1 in one card, so challenged", logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } } else { Debug.append("Couldn't bid anything 'safely', so challenged.", logging); - return new ChallengeAction(opponent.getName()); + return new ChallengeAction(opponent.getName(), false); } } } diff --git a/client/src/main/kotlin/http/RoomApi.kt b/client/src/main/kotlin/http/RoomApi.kt index 4adc4fe3..f0c4b941 100644 --- a/client/src/main/kotlin/http/RoomApi.kt +++ b/client/src/main/kotlin/http/RoomApi.kt @@ -11,7 +11,7 @@ import screen.ScreenCache import util.DialogUtilNew class RoomApi(private val httpClient: HttpClient) { - fun joinRoom(room: GameRoom) { + fun joinRoom(room: GameRoom<*>) { val response = httpClient.doCall( HttpMethod.POST, @@ -25,7 +25,7 @@ class RoomApi(private val httpClient: HttpClient) { } } - private fun handleJoinRoom(room: GameRoom, response: JoinRoomResponse) { + private fun handleJoinRoom(room: GameRoom<*>, response: JoinRoomResponse) { room.username = ScreenCache.get().username room.observer = true room.isVisible = true @@ -34,7 +34,7 @@ class RoomApi(private val httpClient: HttpClient) { room.chatPanel.updateChatBox(response.chatHistory) } - fun sitDown(room: GameRoom, seat: Int) { + fun sitDown(room: GameRoom<*>, seat: Int) { val response = httpClient.doCall( HttpMethod.POST, @@ -52,7 +52,7 @@ class RoomApi(private val httpClient: HttpClient) { } } - private fun handleSitDown(room: GameRoom, seat: Int, response: RoomStateResponse) { + private fun handleSitDown(room: GameRoom<*>, seat: Int, response: RoomStateResponse) { room.setObserver(false) room.setPlayerNumber(seat) room.init(true) @@ -66,7 +66,7 @@ class RoomApi(private val httpClient: HttpClient) { } } - fun standUp(room: GameRoom) { + fun standUp(room: GameRoom<*>) { val response = httpClient.doCall( HttpMethod.POST, @@ -80,13 +80,9 @@ class RoomApi(private val httpClient: HttpClient) { } } - fun leaveRoom(room: GameRoom) { + fun leaveRoom(room: GameRoom<*>) { val response = - httpClient.doCall( - HttpMethod.POST, - Routes.LEAVE_ROOM, - SimpleRoomRequest(room.id), - ) + httpClient.doCall(HttpMethod.POST, Routes.LEAVE_ROOM, SimpleRoomRequest(room.id)) when (response) { is SuccessResponse -> room.closeWindow() @@ -94,7 +90,7 @@ class RoomApi(private val httpClient: HttpClient) { } } - private fun handleStandUp(room: GameRoom, response: RoomStateResponse) { + private fun handleStandUp(room: GameRoom<*>, response: RoomStateResponse) { room.setObserver(true) room.init(true) room.synchronisePlayers(response.players, response.formerPlayers) diff --git a/core/src/main/kotlin/game/BidAction.kt b/core/src/main/kotlin/game/BidAction.kt index b396e3f9..ea503d54 100644 --- a/core/src/main/kotlin/game/BidAction.kt +++ b/core/src/main/kotlin/game/BidAction.kt @@ -1,7 +1,6 @@ package game abstract class BidAction> : PlayerAction() { - abstract val blind: Boolean var cardToReveal: String? = null diff --git a/core/src/main/kotlin/game/CardsUtil.kt b/core/src/main/kotlin/game/CardsUtil.kt index 7b30e6d4..16d91565 100644 --- a/core/src/main/kotlin/game/CardsUtil.kt +++ b/core/src/main/kotlin/game/CardsUtil.kt @@ -25,7 +25,8 @@ fun getEvMap(visibleCards: List, strategyParams: StrategyParms): Map + return Suit.filter(strategyParams.includeMoons, strategyParams.includeStars).associateWith { + suit -> val known = countSuit(suit, visibleCards, strategyParams.jokerValue) val possibleOthers = remainingDeck diff --git a/core/src/main/kotlin/game/ChallengeAction.kt b/core/src/main/kotlin/game/ChallengeAction.kt index 865b8f20..ef7f7f5f 100644 --- a/core/src/main/kotlin/game/ChallengeAction.kt +++ b/core/src/main/kotlin/game/ChallengeAction.kt @@ -1,5 +1,6 @@ package game -data class ChallengeAction(override val playerName: String) : PlayerAction() { +data class ChallengeAction(override val playerName: String, override val blind: Boolean) : + PlayerAction() { override fun plainString() = "Challenge" } diff --git a/core/src/main/kotlin/game/EntropyBidAction.kt b/core/src/main/kotlin/game/EntropyBidAction.kt index b828a030..a8477481 100644 --- a/core/src/main/kotlin/game/EntropyBidAction.kt +++ b/core/src/main/kotlin/game/EntropyBidAction.kt @@ -27,7 +27,7 @@ data class EntropyBidAction( return amount == other.amount && suit > other.suit } - override fun plainString() = "$amount ${suit.getDescription(amount > 1)}" + override fun plainString() = "$amount ${suit.getDescription(amount == 1)}" override fun htmlString() = "$amount${suit.unicodeStr}" diff --git a/core/src/main/kotlin/game/IllegalAction.kt b/core/src/main/kotlin/game/IllegalAction.kt index 64654208..87b68ff3 100644 --- a/core/src/main/kotlin/game/IllegalAction.kt +++ b/core/src/main/kotlin/game/IllegalAction.kt @@ -1,5 +1,6 @@ package game -data class IllegalAction(override val playerName: String) : PlayerAction() { +data class IllegalAction(override val playerName: String, override val blind: Boolean) : + PlayerAction() { override fun plainString() = "Illegal!" } diff --git a/core/src/main/kotlin/game/LeaveAction.kt b/core/src/main/kotlin/game/LeaveAction.kt index 7e278db8..e8f0a4af 100644 --- a/core/src/main/kotlin/game/LeaveAction.kt +++ b/core/src/main/kotlin/game/LeaveAction.kt @@ -1,5 +1,7 @@ package game data class LeaveAction(override val playerName: String) : PlayerAction() { + override val blind = false + override fun plainString() = "Left" } diff --git a/core/src/main/kotlin/game/PlayerAction.kt b/core/src/main/kotlin/game/PlayerAction.kt index 2b73932b..3c9adbc4 100644 --- a/core/src/main/kotlin/game/PlayerAction.kt +++ b/core/src/main/kotlin/game/PlayerAction.kt @@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo ) abstract class PlayerAction { abstract val playerName: String + abstract val blind: Boolean abstract fun plainString(): String From b821fef5f67cbbd78e26d259ef0abf72619c521d Mon Sep 17 00:00:00 2001 From: alyssa Date: Wed, 4 Jun 2025 08:56:42 +0100 Subject: [PATCH 15/16] more WIP, into the strat stuff --- .../src/main/java/screen/EntropyScreen.java | 2 +- client/src/main/java/screen/GameScreen.java | 33 +- .../main/java/screen/SimulationDialog.java | 374 ++++++++--------- .../src/main/java/util/AchievementsUtil.java | 8 +- client/src/main/java/util/GameSimulator.java | 384 ------------------ .../src/main/java/util/SimulationParms.java | 193 --------- client/src/main/kotlin/util/GameSimulator.kt | 241 +++++++++++ .../src/main/kotlin/util/SimulationParams.kt | 30 ++ core/src/main/java/util/Registry.java | 2 +- core/src/main/kotlin/game/GameSettings.kt | 3 + .../src/main/kotlin/testCore/DtoFactory.kt | 2 + 11 files changed, 469 insertions(+), 803 deletions(-) delete mode 100644 client/src/main/java/util/GameSimulator.java delete mode 100644 client/src/main/java/util/SimulationParms.java create mode 100644 client/src/main/kotlin/util/GameSimulator.kt create mode 100644 client/src/main/kotlin/util/SimulationParams.kt diff --git a/client/src/main/java/screen/EntropyScreen.java b/client/src/main/java/screen/EntropyScreen.java index 1d401a25..b456ea7c 100644 --- a/client/src/main/java/screen/EntropyScreen.java +++ b/client/src/main/java/screen/EntropyScreen.java @@ -80,7 +80,7 @@ public void saveGame() throws JsonProcessingException } @Override - protected void saveRoundForReplay() + protected void saveRoundForReplay() throws JsonProcessingException { int roundsSoFar = inGameReplay.getInt(Registry.REPLAY_INT_ROUNDS_SO_FAR, 0) + 1; inGameReplay.putInt(roundsSoFar + Registry.REPLAY_INT_LAST_BID_SUIT_CODE, lastBid.getSuit().getLegacyCode()); diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index e94b05d0..7009a368 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -8,6 +8,7 @@ import object.IllegalBid; import object.Player; import util.*; +import utils.CoreGlobals; import javax.swing.*; import java.util.Timer; @@ -212,7 +213,7 @@ private void initVariables() int maxBid = GameUtil.getMaxBid(jokerQuantity, jokerValue, totalNumberOfCards, negativeJacks); bidPanel.init(maxBid, totalNumberOfCards, false, includeMoons, includeStars, false); - DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); listmodel.removeAllElements(); player.resetHand(); @@ -315,7 +316,7 @@ else if (opponentOne.isEnabled()) private void getNewGameVariablesFromRegistry() { - numberOfCards = prefs.getInt(PREFERENCES_INT_NUMBER_OF_CARDS, 5); + numberOfCards = prefs.getInt(SHARED_INT_NUMBER_OF_CARDS, 5); jokerQuantity = prefs.getInt(PREFERENCES_INT_JOKER_QUANTITY, 2); jokerValue = prefs.getInt(PREFERENCES_INT_JOKER_VALUE, 2); playBlind = prefs.getBoolean(PREFERENCES_BOOLEAN_PLAY_BLIND, false); @@ -428,7 +429,12 @@ else if (winningPlayer == 0) protected void roundEnded(int playerLastToAct) { ScreenCache.get(MainScreen.class).enableNewGameOption(true); - saveRoundForReplay(); + + try { + saveRoundForReplay(); + } catch (Exception e) { + logger.error("replay.error", "Failed to save round for replay", e); + } firstRound = false; @@ -460,7 +466,7 @@ protected void roundEnded(int playerLastToAct) } } - protected void saveRoundForReplay() + protected void saveRoundForReplay() throws JsonProcessingException { inGameReplay.putInt(REPLAY_INT_GAME_MODE, ReplayConstantsKt.toReplayConstant(getGameMode())); inGameReplay.put(REPLAY_STRING_OPPONENT_ONE_STRATEGY, opponentOne.getStrategy()); @@ -471,13 +477,13 @@ protected void saveRoundForReplay() inGameReplay.putInt(REPLAY_INT_ROUNDS_SO_FAR, roundsSoFar); //save the listmodel - DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); int historySize = listmodel.size(); inGameReplay.putInt(roundsSoFar + REPLAY_INT_HISTORY_SIZE, historySize); for (int i = 0; i < historySize; i++) { - Bid bid = listmodel.get(i); - inGameReplay.put(roundsSoFar + REPLAY_STRING_LISTMODEL + i, bid.toXmlString()); + PlayerAction bid = listmodel.get(i); + inGameReplay.put(roundsSoFar + REPLAY_STRING_LISTMODEL + i, CoreGlobals.jsonMapper.writeValueAsString(bid)); } inGameReplay.putBoolean(REPLAY_BOOLEAN_PLAY_BLIND, playBlind); @@ -525,13 +531,13 @@ protected void saveGame() throws JsonProcessingException savedGame.put(SAVED_GAME_STRING_GAME_MODE, getGameMode().name()); //save the listmodel - DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); int historySize = listmodel.size(); savedGame.putInt(SAVED_GAME_INT_HISTORY_SIZE, historySize); for (int i=0; i listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); int historySize = savedGame.getInt(SAVED_GAME_INT_HISTORY_SIZE, 0); for (int i = 0; i < historySize; i++) { String modelItem = savedGame.get(SAVED_GAME_STRING_LISTMODEL + i, ""); - Bid bid = Bid.factoryFromXmlString(modelItem, includeMoons, includeStars); + PlayerAction bid = CoreGlobals.jsonMapper.readValue(modelItem, PlayerAction.class); listmodel.addElement(bid); } @@ -935,8 +941,7 @@ public void processIllegal(Player illegaller) Player bidder = lastBid.getPlayer(); Debug.append("Bidder: " + bidder, logging); - if (lastBid.isPerfect(player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), opponentThree.getHand(), - jokerValue, includeMoons, includeStars)) + if (lastBid.isPerfect(allCards(), settings)) { Debug.append("bid was perfect", logging); diff --git a/client/src/main/java/screen/SimulationDialog.java b/client/src/main/java/screen/SimulationDialog.java index e3c8060d..4b89db83 100644 --- a/client/src/main/java/screen/SimulationDialog.java +++ b/client/src/main/java/screen/SimulationDialog.java @@ -2,6 +2,7 @@ import bean.NumberField; import game.GameMode; +import game.GameSettings; import util.*; import javax.swing.*; @@ -28,154 +29,147 @@ public class SimulationDialog extends JDialog public SimulationDialog() { - try - { - getContentPane().setLayout(null); - scrollPane.setBounds(0, 323, 434, 87); - getContentPane().add(scrollPane); - JSeparator separator_2 = new JSeparator(); - separator_2.setBounds(0, 323, 434, 2); - getContentPane().add(separator_2); - btnRunSimulation.setBounds(159, 257, 126, 23); - getContentPane().add(btnRunSimulation); - gamesToSimulate.setBounds(231, 207, 86, 20); - getContentPane().add(gamesToSimulate); - gamesToSimulate.setValue(10000); - JLabel lblSimulate = new JLabel("Games to simulate"); - lblSimulate.setBounds(119, 210, 126, 14); - getContentPane().add(lblSimulate); - tabbedPane.setBounds(0, 0, 434, 184); - getContentPane().add(tabbedPane); - tabbedPane.addTab("CPU Settings", null, cpuPanel, null); - cpuPanel.setLayout(null); - cbOpponentThree.setBounds(368, 54, 29, 23); - cpuPanel.add(cbOpponentThree); - opponentOneStrat.setBounds(149, 26, 135, 20); - cpuPanel.add(opponentOneStrat); - opponentTwoStrat.setBounds(10, 78, 135, 20); - opponentTwoStrat.setEnabled(false); - cpuPanel.add(opponentTwoStrat); - opponentThreeStrat.setBounds(284, 78, 135, 20); - opponentThreeStrat.setEnabled(false); - cpuPanel.add(opponentThreeStrat); - opponentZeroStrat.setBounds(149, 125, 135, 20); - cpuPanel.add(opponentZeroStrat); - cbOpponentTwo.setSelected(false); - cbOpponentTwo.setEnabled(true); - cbOpponentTwo.setBounds(83, 54, 29, 23); - cpuPanel.add(cbOpponentTwo); - JLabel lblCpu_1 = new JLabel("CPU 0"); - lblCpu_1.setForeground(Color.RED); - lblCpu_1.setHorizontalAlignment(SwingConstants.CENTER); - lblCpu_1.setBounds(193, 109, 46, 14); - cpuPanel.add(lblCpu_1); - JLabel lblCpu_2 = new JLabel("CPU 1"); - lblCpu_2.setHorizontalAlignment(SwingConstants.CENTER); - lblCpu_2.setForeground(Color.BLUE); - lblCpu_2.setBounds(193, 11, 46, 14); - cpuPanel.add(lblCpu_2); - lblCpu_3.setHorizontalAlignment(SwingConstants.CENTER); - lblCpu_3.setForeground(new Color(128, 0, 128)); - lblCpu_3.setBounds(317, 58, 46, 14); - cpuPanel.add(lblCpu_3); - lblCpu_4.setHorizontalAlignment(SwingConstants.CENTER); - lblCpu_4.setForeground(new Color(0, 128, 0)); - lblCpu_4.setBounds(31, 58, 46, 14); - cpuPanel.add(lblCpu_4); - JPanel gameModePanel = new JPanel(); - tabbedPane.addTab("Game Mode", null, gameModePanel, null); - gameModePanel.setLayout(null); - rdbtnEntropy.setBounds(18, 17, 109, 23); - gameModePanel.add(rdbtnEntropy); - rdbtnVectropy.setBounds(18, 43, 109, 23); - gameModePanel.add(rdbtnVectropy); - JPanel gameplayPanel = new JPanel(); - tabbedPane.addTab("Gameplay", null, gameplayPanel, null); - gameplayPanel.setLayout(null); - JLabel label = new JLabel("Cards dealt to each player"); - label.setHorizontalAlignment(SwingConstants.CENTER); - label.setBounds(23, 22, 158, 14); - gameplayPanel.add(label); - slider.setValue(5); - slider.setToolTipText(""); - slider.setPaintTicks(true); - slider.setPaintLabels(true); - slider.setMinorTickSpacing(1); - slider.setMinimum(1); - slider.setMaximum(5); - slider.setMajorTickSpacing(4); - slider.setBounds(10, 47, 184, 45); - gameplayPanel.add(slider); - cbIncludeJokers.setFont(new Font("Tahoma", Font.PLAIN, 11)); - cbIncludeJokers.setSelected(false); - cbIncludeJokers.setBounds(6, 108, 115, 22); - gameplayPanel.add(cbIncludeJokers); - spinnerJokerQuantity.setEnabled(false); - spinnerJokerQuantity.setBounds(127, 104, 38, 22); - spinnerJokerQuantity.setModel(new SpinnerNumberModel(2, 1, 4, 1)); - gameplayPanel.add(spinnerJokerQuantity); - spinnerJokerValue.setEnabled(false); - spinnerJokerValue.setBounds(179, 104, 38, 22); - spinnerJokerValue.setModel(new SpinnerNumberModel(2, 2, 4, 1)); - gameplayPanel.add(spinnerJokerValue); - lblValue.setHorizontalAlignment(SwingConstants.CENTER); - lblValue.setEnabled(false); - lblValue.setBounds(171, 131, 55, 14); - gameplayPanel.add(lblValue); - lblQuantity.setHorizontalAlignment(SwingConstants.CENTER); - lblQuantity.setEnabled(false); - lblQuantity.setBounds(118, 131, 57, 14); - gameplayPanel.add(lblQuantity); - chckbxIncludeMoons.setFont(new Font("Tahoma", Font.PLAIN, 11)); - chckbxIncludeMoons.setBounds(232, 18, 115, 23); - gameplayPanel.add(chckbxIncludeMoons); - chckbxIncludeStars.setFont(new Font("Tahoma", Font.PLAIN, 11)); - chckbxIncludeStars.setBounds(232, 47, 115, 23); - gameplayPanel.add(chckbxIncludeStars); - chckbxNegativeJacks.setFont(new Font("Tahoma", Font.PLAIN, 11)); - chckbxNegativeJacks.setBounds(232, 73, 115, 23); - - gameplayPanel.add(chckbxNegativeJacks); - chckbxCardReveal.setFont(new Font("Tahoma", Font.PLAIN, 11)); - chckbxCardReveal.setBounds(232, 99, 115, 23); - gameplayPanel.add(chckbxCardReveal); - tabbedPane.addTab("Advanced Settings", null, advancedSettingsPanel, null); - advancedSettingsPanel.setLayout(null); - cbLogging.setBounds(15, 10, 141, 22); - advancedSettingsPanel.add(cbLogging); - cbLogging.setState(false); - cbLogging.setEnabled(true); - cbForceStart.setState(false); - cbForceStart.setEnabled(true); - cbForceStart.setBounds(15, 39, 141, 22); - advancedSettingsPanel.add(cbForceStart); - cbRandomiseOrder.setState(false); - cbRandomiseOrder.setEnabled(true); - cbRandomiseOrder.setBounds(15, 67, 141, 22); - advancedSettingsPanel.add(cbRandomiseOrder); - lblCpu.setHorizontalAlignment(SwingConstants.CENTER); - lblCpu.setFont(new Font("Tahoma", Font.BOLD, 15)); - JLabel lblResults = new JLabel("Results"); - lblResults.setHorizontalAlignment(SwingConstants.CENTER); - lblResults.setFont(new Font("Tahoma", Font.PLAIN, 14)); - lblResults.setBounds(150, 294, 135, 31); - getContentPane().add(lblResults); - lblTimeTaken.setHorizontalAlignment(SwingConstants.CENTER); - lblTimeTaken.setFont(new Font("Tahoma", Font.ITALIC, 11)); - lblTimeTaken.setBounds(33, 438, 379, 14); - getContentPane().add(lblTimeTaken); - ButtonGroup modeGroup = new ButtonGroup(); - rdbtnEntropy.setSelected(true); - modeGroup.add(rdbtnEntropy); - modeGroup.add(rdbtnVectropy); - - - setUpListeners(); - } - catch (Throwable t) - { - Debug.stackTrace(t); - } + getContentPane().setLayout(null); + scrollPane.setBounds(0, 323, 434, 87); + getContentPane().add(scrollPane); + JSeparator separator_2 = new JSeparator(); + separator_2.setBounds(0, 323, 434, 2); + getContentPane().add(separator_2); + btnRunSimulation.setBounds(159, 257, 126, 23); + getContentPane().add(btnRunSimulation); + gamesToSimulate.setBounds(231, 207, 86, 20); + getContentPane().add(gamesToSimulate); + gamesToSimulate.setValue(10000); + JLabel lblSimulate = new JLabel("Games to simulate"); + lblSimulate.setBounds(119, 210, 126, 14); + getContentPane().add(lblSimulate); + tabbedPane.setBounds(0, 0, 434, 184); + getContentPane().add(tabbedPane); + tabbedPane.addTab("CPU Settings", null, cpuPanel, null); + cpuPanel.setLayout(null); + cbOpponentThree.setBounds(368, 54, 29, 23); + cpuPanel.add(cbOpponentThree); + opponentOneStrat.setBounds(149, 26, 135, 20); + cpuPanel.add(opponentOneStrat); + opponentTwoStrat.setBounds(10, 78, 135, 20); + opponentTwoStrat.setEnabled(false); + cpuPanel.add(opponentTwoStrat); + opponentThreeStrat.setBounds(284, 78, 135, 20); + opponentThreeStrat.setEnabled(false); + cpuPanel.add(opponentThreeStrat); + opponentZeroStrat.setBounds(149, 125, 135, 20); + cpuPanel.add(opponentZeroStrat); + cbOpponentTwo.setSelected(false); + cbOpponentTwo.setEnabled(true); + cbOpponentTwo.setBounds(83, 54, 29, 23); + cpuPanel.add(cbOpponentTwo); + JLabel lblCpu_1 = new JLabel("CPU 0"); + lblCpu_1.setForeground(Color.RED); + lblCpu_1.setHorizontalAlignment(SwingConstants.CENTER); + lblCpu_1.setBounds(193, 109, 46, 14); + cpuPanel.add(lblCpu_1); + JLabel lblCpu_2 = new JLabel("CPU 1"); + lblCpu_2.setHorizontalAlignment(SwingConstants.CENTER); + lblCpu_2.setForeground(Color.BLUE); + lblCpu_2.setBounds(193, 11, 46, 14); + cpuPanel.add(lblCpu_2); + lblCpu_3.setHorizontalAlignment(SwingConstants.CENTER); + lblCpu_3.setForeground(new Color(128, 0, 128)); + lblCpu_3.setBounds(317, 58, 46, 14); + cpuPanel.add(lblCpu_3); + lblCpu_4.setHorizontalAlignment(SwingConstants.CENTER); + lblCpu_4.setForeground(new Color(0, 128, 0)); + lblCpu_4.setBounds(31, 58, 46, 14); + cpuPanel.add(lblCpu_4); + JPanel gameModePanel = new JPanel(); + tabbedPane.addTab("Game Mode", null, gameModePanel, null); + gameModePanel.setLayout(null); + rdbtnEntropy.setBounds(18, 17, 109, 23); + gameModePanel.add(rdbtnEntropy); + rdbtnVectropy.setBounds(18, 43, 109, 23); + gameModePanel.add(rdbtnVectropy); + JPanel gameplayPanel = new JPanel(); + tabbedPane.addTab("Gameplay", null, gameplayPanel, null); + gameplayPanel.setLayout(null); + JLabel label = new JLabel("Cards dealt to each player"); + label.setHorizontalAlignment(SwingConstants.CENTER); + label.setBounds(23, 22, 158, 14); + gameplayPanel.add(label); + slider.setValue(5); + slider.setToolTipText(""); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinorTickSpacing(1); + slider.setMinimum(1); + slider.setMaximum(5); + slider.setMajorTickSpacing(4); + slider.setBounds(10, 47, 184, 45); + gameplayPanel.add(slider); + cbIncludeJokers.setFont(new Font("Tahoma", Font.PLAIN, 11)); + cbIncludeJokers.setSelected(false); + cbIncludeJokers.setBounds(6, 108, 115, 22); + gameplayPanel.add(cbIncludeJokers); + spinnerJokerQuantity.setEnabled(false); + spinnerJokerQuantity.setBounds(127, 104, 38, 22); + spinnerJokerQuantity.setModel(new SpinnerNumberModel(2, 1, 4, 1)); + gameplayPanel.add(spinnerJokerQuantity); + spinnerJokerValue.setEnabled(false); + spinnerJokerValue.setBounds(179, 104, 38, 22); + spinnerJokerValue.setModel(new SpinnerNumberModel(2, 2, 4, 1)); + gameplayPanel.add(spinnerJokerValue); + lblValue.setHorizontalAlignment(SwingConstants.CENTER); + lblValue.setEnabled(false); + lblValue.setBounds(171, 131, 55, 14); + gameplayPanel.add(lblValue); + lblQuantity.setHorizontalAlignment(SwingConstants.CENTER); + lblQuantity.setEnabled(false); + lblQuantity.setBounds(118, 131, 57, 14); + gameplayPanel.add(lblQuantity); + chckbxIncludeMoons.setFont(new Font("Tahoma", Font.PLAIN, 11)); + chckbxIncludeMoons.setBounds(232, 18, 115, 23); + gameplayPanel.add(chckbxIncludeMoons); + chckbxIncludeStars.setFont(new Font("Tahoma", Font.PLAIN, 11)); + chckbxIncludeStars.setBounds(232, 47, 115, 23); + gameplayPanel.add(chckbxIncludeStars); + chckbxNegativeJacks.setFont(new Font("Tahoma", Font.PLAIN, 11)); + chckbxNegativeJacks.setBounds(232, 73, 115, 23); + + gameplayPanel.add(chckbxNegativeJacks); + chckbxCardReveal.setFont(new Font("Tahoma", Font.PLAIN, 11)); + chckbxCardReveal.setBounds(232, 99, 115, 23); + gameplayPanel.add(chckbxCardReveal); + tabbedPane.addTab("Advanced Settings", null, advancedSettingsPanel, null); + advancedSettingsPanel.setLayout(null); + cbLogging.setBounds(15, 10, 141, 22); + advancedSettingsPanel.add(cbLogging); + cbLogging.setState(false); + cbLogging.setEnabled(true); + cbForceStart.setState(false); + cbForceStart.setEnabled(true); + cbForceStart.setBounds(15, 39, 141, 22); + advancedSettingsPanel.add(cbForceStart); + cbRandomiseOrder.setState(false); + cbRandomiseOrder.setEnabled(true); + cbRandomiseOrder.setBounds(15, 67, 141, 22); + advancedSettingsPanel.add(cbRandomiseOrder); + lblCpu.setHorizontalAlignment(SwingConstants.CENTER); + lblCpu.setFont(new Font("Tahoma", Font.BOLD, 15)); + JLabel lblResults = new JLabel("Results"); + lblResults.setHorizontalAlignment(SwingConstants.CENTER); + lblResults.setFont(new Font("Tahoma", Font.PLAIN, 14)); + lblResults.setBounds(150, 294, 135, 31); + getContentPane().add(lblResults); + lblTimeTaken.setHorizontalAlignment(SwingConstants.CENTER); + lblTimeTaken.setFont(new Font("Tahoma", Font.ITALIC, 11)); + lblTimeTaken.setBounds(33, 438, 379, 14); + getContentPane().add(lblTimeTaken); + ButtonGroup modeGroup = new ButtonGroup(); + rdbtnEntropy.setSelected(true); + modeGroup.add(rdbtnEntropy); + modeGroup.add(rdbtnVectropy); + + + setUpListeners(); } private final JPanel cpuPanel = new JPanel(); @@ -213,20 +207,12 @@ public SimulationDialog() private void showLoggingWarningMessage() { - try - { - int dialogButton = DialogUtil.showQuestion("To avoid intensive memory use, " - + "logging should only be turned on for a small number of games. \n\nProceed?", false); + int dialogButton = DialogUtilNew.showQuestion("To avoid intensive memory use, " + + "logging should only be turned on for a small number of games. \n\nProceed?", false); - if (dialogButton == JOptionPane.NO_OPTION) - { - cbLogging.setState(false); - return; - } - } - catch (Throwable t) + if (dialogButton == JOptionPane.NO_OPTION) { - Debug.stackTrace(t); + cbLogging.setState(false); } } @@ -245,22 +231,15 @@ public void initVariables() private void runSimulation() { - try - { - numberOfGames = (int) gamesToSimulate.getValue(); - - SimulationParms parms = factorySimulationParms(); - - btnRunSimulation.setEnabled(false); - runSimulationInSeparateThread(parms); - } - catch (Throwable t) - { - Debug.stackTrace(t); - } + numberOfGames = (int) gamesToSimulate.getValue(); + + SimulationParams parms = factorySimulationParms(); + + btnRunSimulation.setEnabled(false); + runSimulationInSeparateThread(parms); } - private void runSimulationInSeparateThread(final SimulationParms parms) + private void runSimulationInSeparateThread(final SimulationParams parms) { Runnable simulationRunnable = new Runnable() { @@ -272,11 +251,8 @@ public void run() dialog.showCancel(true); dialog.setVisibleLater(); - double tenPercentOfTotal = Math.floor((double)numberOfGames / 10); - Debug.appendBanner("Starting simulation for " + numberOfGames + " games"); - GameSimulator simulator = new GameSimulator(); - simulator.setSimulationParms(parms); + GameSimulator simulator = new GameSimulator(parms); for (int i = 1; i <= numberOfGames; i++) { @@ -306,8 +282,8 @@ public void run() dialog.disposeLater(); btnRunSimulation.setEnabled(true); dumpSimulationDetails(i, numberOfGames, parms); - Debug.stackTrace(t); - DialogUtil.showErrorLater("A serious problem has occurred with the simulation."); + logger.error("simulation.error", "error running simulation", t); + DialogUtilNew.showErrorLater("A serious problem has occurred with the simulation."); resetVariables(); return; } @@ -322,7 +298,7 @@ public void run() t.start(); } - private void simulationCancelled(double startTime, int gamesSimulated, SimulationParms parms) + private void simulationCancelled(double startTime, int gamesSimulated, SimulationParams parms) { numberOfGames = gamesSimulated; generateAndShowTable(parms); @@ -336,7 +312,7 @@ private void simulationCancelled(double startTime, int gamesSimulated, Simulatio btnRunSimulation.setEnabled(true); } - private void simulationCompleted(double startTime, SimulationParms parms) + private void simulationCompleted(double startTime, SimulationParams parms) { btnRunSimulation.setEnabled(true); generateAndShowTable(parms); @@ -351,7 +327,7 @@ private void resetVariables() hmSimulationResultsByOpponentNumber = new HashMap<>(); } - private void generateAndShowTable(SimulationParms parms) + private void generateAndShowTable(SimulationParams parms) { scrollPane.setVisible(true); @@ -551,7 +527,7 @@ private void updateStrategySelection() opponentThreeStrat.setModel(comboModel); } - private SimulationParms factorySimulationParms() + private SimulationParams factorySimulationParms() { GameMode gameMode = GameMode.Entropy; if (rdbtnVectropy.isSelected()) @@ -573,33 +549,17 @@ private SimulationParms factorySimulationParms() String opponentOneStrategy = (String) opponentOneStrat.getSelectedItem(); String opponentTwoStrategy = (String) opponentTwoStrat.getSelectedItem(); String opponentThreeStrategy = (String) opponentThreeStrat.getSelectedItem(); + boolean enableLogging = cbLogging.getState(); boolean forceStart = cbForceStart.getState(); boolean randomiseOrder = cbRandomiseOrder.getState(); - boolean enableLogging = cbLogging.getState(); - - SimulationParms parms = new SimulationParms(); - parms.setForceStart(forceStart); - parms.setGameMode(gameMode); - parms.setIncludeMoons(includeMoons); - parms.setIncludeStars(includeStars); - parms.setNegativeJacks(negativeJacks); - parms.setCardReveal(cardReveal); - parms.setJokerQuantity(jokerQuantity); - parms.setJokerValue(jokerValue); - parms.setLogging(enableLogging); - parms.setNumberOfCards(numberOfCards); - parms.setOpponentThreeEnabled(opponentThreeEnabled); - parms.setOpponentTwoEnabled(opponentTwoEnabled); - parms.setOpponentZeroStrategy(opponentZeroStrategy); - parms.setOpponentOneStrategy(opponentOneStrategy); - parms.setOpponentTwoStrategy(opponentTwoStrategy); - parms.setOpponentThreeStrategy(opponentThreeStrategy); - parms.setRandomiseOrder(randomiseOrder); - - return parms; + + var settings = new GameSettings(gameMode, numberOfCards, jokerQuantity, jokerValue, includeMoons, includeStars, negativeJacks, cardReveal, false); + + return new SimulationParams(settings, opponentTwoEnabled, opponentThreeEnabled, opponentZeroStrategy, opponentOneStrategy, + opponentTwoStrategy, opponentThreeStrategy, enableLogging, randomiseOrder, forceStart); } - private void dumpSimulationDetails(int i, int numberOfGames, SimulationParms parms) + private void dumpSimulationDetails(int i, int numberOfGames, SimulationParams parms) { logger.info("simulationDump", "Dumping simulation details"); logger.info("simulationDump", "Opponent 0: " + parms.getOpponentZeroStrategy()); diff --git a/client/src/main/java/util/AchievementsUtil.java b/client/src/main/java/util/AchievementsUtil.java index 744d3a65..6a7c3961 100644 --- a/client/src/main/java/util/AchievementsUtil.java +++ b/client/src/main/java/util/AchievementsUtil.java @@ -1,7 +1,9 @@ package util; import achievement.AchievementSetting; +import game.BidAction; import game.GameMode; +import game.PlayerAction; import object.Bid; import object.Player; import online.screen.EntropyLobby; @@ -302,7 +304,7 @@ private static int getTotalGamesPlayed() public static void unlockSecondThoughts(String roomId) { - DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); + DefaultListModel listmodel = ScreenCache.get(MainScreen.class).getListmodel(); if (!roomId.isEmpty()) { //We're online, so need a different listmodel... @@ -313,8 +315,8 @@ public static void unlockSecondThoughts(String roomId) int size = listmodel.size(); for (int i=0; i deck = CardsUtil.createAndShuffleDeck(parms.getJokerQuantity(), parms.getIncludeMoons(), parms.getIncludeStars(), parms.getNegativeJacks()); - - populateHands(deck); - } - - private void log(String text) - { - Debug.append(text, parms.getLogging()); - } - - private StrategyParms getStrategyParms(Player opponent) - { - StrategyParms stratParms = parms.getStrategyParms(); - stratParms.setLastBid(lastBid); - stratParms.setOpponentOneCards(opponentOne.getNumberOfCards()); - stratParms.setOpponentTwoCards(opponentTwo.getNumberOfCards()); - stratParms.setOpponentThreeCards(opponentThree.getNumberOfCards()); - stratParms.setPlayerCards(opponentZero.getNumberOfCards()); - - //Set the cards revealed - stratParms.appendCardsOnShowFromOpponent(opponent, opponentZero); - stratParms.appendCardsOnShowFromOpponent(opponent, opponentOne); - stratParms.appendCardsOnShowFromOpponent(opponent, opponentTwo); - stratParms.appendCardsOnShowFromOpponent(opponent, opponentThree); - - return stratParms; - } - - private Player getPlayer(int playerNumber) - { - if (playerNumber == 0) - { - return opponentZero; - } - else if (playerNumber == 1) - { - return opponentOne; - } - else if (playerNumber == 2) - { - return opponentTwo; - } - - return opponentThree; - } - - public void setSimulationParms(SimulationParms parms) - { - this.parms = parms; - } -} \ No newline at end of file diff --git a/client/src/main/java/util/SimulationParms.java b/client/src/main/java/util/SimulationParms.java deleted file mode 100644 index ed55e84c..00000000 --- a/client/src/main/java/util/SimulationParms.java +++ /dev/null @@ -1,193 +0,0 @@ -package util; - -import game.GameMode; - -/** - * All the parameters that can be specified for running an Entropy/Vectropy simulation - */ -public class SimulationParms -{ - //Mode - private GameMode gameMode = null; - - //Opponents - private boolean opponentTwoEnabled = false; - private boolean opponentThreeEnabled = false; - private String opponentZeroStrategy = null; - private String opponentOneStrategy = null; - private String opponentTwoStrategy = null; - private String opponentThreeStrategy = null; - - //Gameplay - private int numberOfCards = -1; - private int jokerValue = -1; - private int jokerQuantity = -1; - private boolean includeMoons = false; - private boolean includeStars = false; - private boolean negativeJacks = false; - private boolean cardReveal = false; - - //Advanced - private boolean logging = false; - private boolean randomiseOrder = false; - private boolean forceStart = false; - - /** - * Helpers - */ - public StrategyParms getStrategyParms() - { - StrategyParms parms = new StrategyParms(); - parms.setGameMode(gameMode); - parms.setIncludeMoons(includeMoons); - parms.setIncludeStars(includeStars); - parms.setNegativeJacks(negativeJacks); - parms.setCardReveal(cardReveal); - parms.setJokerQuantity(jokerQuantity); - parms.setJokerValue(jokerValue); - - parms.setLogging(logging); - - return parms; - } - - /** - * Gets / sets - */ - public GameMode getGameMode() - { - return gameMode; - } - public void setGameMode(GameMode gameMode) - { - this.gameMode = gameMode; - } - public boolean getOpponentTwoEnabled() - { - return opponentTwoEnabled; - } - public void setOpponentTwoEnabled(boolean opponentTwoEnabled) - { - this.opponentTwoEnabled = opponentTwoEnabled; - } - public boolean getOpponentThreeEnabled() - { - return opponentThreeEnabled; - } - public void setOpponentThreeEnabled(boolean opponentThreeEnabled) - { - this.opponentThreeEnabled = opponentThreeEnabled; - } - public String getOpponentZeroStrategy() - { - return opponentZeroStrategy; - } - public void setOpponentZeroStrategy(String opponentZeroStrategy) - { - this.opponentZeroStrategy = opponentZeroStrategy; - } - public String getOpponentOneStrategy() - { - return opponentOneStrategy; - } - public void setOpponentOneStrategy(String opponentOneStrategy) - { - this.opponentOneStrategy = opponentOneStrategy; - } - public String getOpponentTwoStrategy() - { - return opponentTwoStrategy; - } - public void setOpponentTwoStrategy(String opponentTwoStrategy) - { - this.opponentTwoStrategy = opponentTwoStrategy; - } - public String getOpponentThreeStrategy() - { - return opponentThreeStrategy; - } - public void setOpponentThreeStrategy(String opponentThreeStrategy) - { - this.opponentThreeStrategy = opponentThreeStrategy; - } - public int getNumberOfCards() - { - return numberOfCards; - } - public void setNumberOfCards(int numberOfCards) - { - this.numberOfCards = numberOfCards; - } - public int getJokerValue() - { - return jokerValue; - } - public void setJokerValue(int jokerValue) - { - this.jokerValue = jokerValue; - } - public int getJokerQuantity() - { - return jokerQuantity; - } - public void setJokerQuantity(int jokerQuantity) - { - this.jokerQuantity = jokerQuantity; - } - public boolean getIncludeMoons() - { - return includeMoons; - } - public void setIncludeMoons(boolean includeMoons) - { - this.includeMoons = includeMoons; - } - public boolean getIncludeStars() - { - return includeStars; - } - public void setIncludeStars(boolean includeStars) - { - this.includeStars = includeStars; - } - public boolean getNegativeJacks() - { - return negativeJacks; - } - public void setNegativeJacks(boolean negativeJacks) - { - this.negativeJacks = negativeJacks; - } - public boolean getCardReveal() - { - return cardReveal; - } - public void setCardReveal(boolean cardReveal) - { - this.cardReveal = cardReveal; - } - public boolean getLogging() - { - return logging; - } - public void setLogging(boolean logging) - { - this.logging = logging; - } - public boolean getRandomiseOrder() - { - return randomiseOrder; - } - public void setRandomiseOrder(boolean randomiseOrder) - { - this.randomiseOrder = randomiseOrder; - } - public boolean getForceStart() - { - return forceStart; - } - public void setForceStart(boolean forceStart) - { - this.forceStart = forceStart; - } -} diff --git a/client/src/main/kotlin/util/GameSimulator.kt b/client/src/main/kotlin/util/GameSimulator.kt new file mode 100644 index 00000000..cb3dd692 --- /dev/null +++ b/client/src/main/kotlin/util/GameSimulator.kt @@ -0,0 +1,241 @@ +package util + +import game.BidAction +import game.ChallengeAction +import java.util.Random +import `object`.Player +import screen.ScreenCache.get +import screen.SimulationDialog +import utils.CoreGlobals.logger + +class GameSimulator(private val params: SimulationParams) { + private var personToStart = 0 + + private var lastBid: BidAction<*>? = null + + private val opponentZero: Player = Player(0, "").also { it.name = "0" } + private val opponentOne: Player = Player(1, "").also { it.name = "1" } + private val opponentTwo: Player = Player(2, "").also { it.name = "2" } + private val opponentThree: Player = Player(3, "").also { it.name = "3" } + + private var playOrder = listOf(2, 3, 1, 0) // standard play order + + /** Simulation methods */ + fun startNewGame(number: Int) { + log("*** Game $number ***") + + initVariablesForSimulationNewGame() + initVariablesForSimulationNewRound() + personToStart = computeRandomPersonToStart() + + val player: Player = getPlayer(personToStart) + processOpponentTurn(player) + } + + private fun startNewRound() { + log("*** New Round ***") + log("opponentZeroNumberOfCards = " + opponentZero.numberOfCards) + log("opponentOneNumberOfCards = " + opponentOne.numberOfCards) + log("opponentTwoNumberOfCards = " + opponentTwo.numberOfCards) + log("opponentThreeNumberOfCards = " + opponentThree.numberOfCards) + + knockOutPlayers() + val gameOver = checkForResult() + + if (!gameOver) { + initVariablesForSimulationNewRound() + + val playerToStart: Player = getPlayer(personToStart) + processOpponentTurn(playerToStart) + } + } + + private fun populateHands(deck: List) { + log("Dealing hands...") + + GameUtil.populateHand(opponentZero, deck, params.enableLogging) + GameUtil.populateHand(opponentOne, deck, params.enableLogging) + GameUtil.populateHand(opponentTwo, deck, params.enableLogging) + GameUtil.populateHand(opponentThree, deck, params.enableLogging) + } + + private tailrec fun computeRandomPersonToStart(): Int { + if (params.forceStart) { + return 0 + } + + val result = Random().nextInt(4) + return if (getPlayer(result).isEnabled) { + result + } else { + computeRandomPersonToStart() + } + } + + private fun knockOutPlayers() { + log("Knocking out players...") + + knockOut(opponentZero) + knockOut(opponentOne) + knockOut(opponentTwo) + knockOut(opponentThree) + } + + private fun knockOut(player: Player) { + if (player.numberOfCards == 0 && player.isEnabled) { + log("Opponent $player disabled") + player.isEnabled = false + } + } + + private fun checkForResult(): Boolean { + val stillAlive = allPlayers().filter { it.numberOfCards > 0 } + if (stillAlive.size == 1) { + val winner = stillAlive.first() + val perfectGame = winner.numberOfCards == params.settings.numberOfCards + get(SimulationDialog::class.java).recordWin(winner.playerNumber, perfectGame) + return true + } + + return false + } + + private fun allPlayers() = playOrder.map(::getPlayer) + + private fun processOpponentTurn(opponent: Player) { + if (!opponent.isEnabled) { + val nextPlayerNumber = playOrder[opponent.playerNumber] + processOpponentTurn(getPlayer(nextPlayerNumber)) + return + } + + log("*** Opponent $opponent ***") + if (lastBid != null) { + get(SimulationDialog::class.java).recordOpportunityToChallenge(opponent.playerNumber) + } + + val stratParms = getStrategyParms(opponent) + val action = + CpuStrategies.processOpponentTurn(stratParms, opponent) + ?: // Abort the simulation, something's gone wrong + throw SimulationException("Simulation error") + + if (action is ChallengeAction) { + processChallenge(opponent) + } else { + lastBid = action as BidAction<*> + val nextPlayerNumber = playOrder[opponent.playerNumber] + processOpponentTurn(getPlayer(nextPlayerNumber)) + } + } + + private fun allCards() = + listOf(opponentZero.hand, opponentOne.hand, opponentTwo.hand, opponentThree.hand).flatten() + + private fun processChallenge(challenger: Player) { + log("Challenged") + + val dialog = get(SimulationDialog::class.java) + if (!lastBid!!.isOverbid(allCards(), params.settings)) { + log("not overbid") + dialog.recordChallenge(challenger.playerNumber, false) + + challenger.cardsToSubtract = 1 + challenger.doSubtraction() + personToStart = challenger.playerNumber + } else { + log("overbid") + dialog.recordChallenge(challenger.playerNumber, true) + + val bidder = allPlayers().first { it.name == lastBid?.playerName } + bidder.cardsToSubtract = 1 + bidder.doSubtraction() + personToStart = bidder.playerNumber + } + + startNewRound() + } + + private fun initVariablesForSimulationNewGame() { + opponentZero.isEnabled = true + opponentOne.isEnabled = true + val opponentTwoEnabled = params.opponentTwoEnabled + opponentTwo.isEnabled = opponentTwoEnabled + val opponentThreeEnabled = params.opponentThreeEnabled + opponentThree.isEnabled = opponentThreeEnabled + val opponentTwoCoeff = if (opponentTwoEnabled) 1 else 0 + val opponentThreeCoeff = if (opponentThreeEnabled) 1 else 0 + + val numberOfCards = params.settings.numberOfCards + opponentZero.numberOfCards = numberOfCards + opponentOne.numberOfCards = numberOfCards + opponentTwo.numberOfCards = opponentTwoCoeff * numberOfCards + opponentThree.numberOfCards = opponentThreeCoeff * numberOfCards + + opponentZero.strategy = params.opponentZeroStrategy + opponentOne.strategy = params.opponentOneStrategy + opponentTwo.strategy = params.opponentTwoStrategy + opponentThree.strategy = params.opponentThreeStrategy + + setPlayOrder(params.randomiseOrder) + } + + private fun setPlayOrder(randomiseOrder: Boolean) { + playOrder = + if (randomiseOrder) { + playOrder.shuffled() + } else { + listOf(2, 3, 1, 0) + } + } + + private fun initVariablesForSimulationNewRound() { + lastBid = null + + opponentZero.resetHand() + opponentOne.resetHand() + opponentTwo.resetHand() + opponentThree.resetHand() + + val deck = + CardsUtil.createAndShuffleDeck( + params.settings.jokerQuantity, + params.settings.includeMoons, + params.settings.includeStars, + params.settings.negativeJacks, + ) + + populateHands(deck) + } + + private fun log(text: String) { + if (params.enableLogging) { + logger.info("simulation", text) + } + } + + private fun getStrategyParms(opponent: Player): StrategyParms { + val stratParms: StrategyParms = params.toStrategyParams() + stratParms.lastBid = lastBid + stratParms.opponentOneCards = opponentOne.numberOfCards + stratParms.opponentTwoCards = opponentTwo.numberOfCards + stratParms.opponentThreeCards = opponentThree.numberOfCards + stratParms.playerCards = opponentZero.numberOfCards + + // Set the cards revealed + stratParms.appendCardsOnShowFromOpponent(opponent, opponentZero) + stratParms.appendCardsOnShowFromOpponent(opponent, opponentOne) + stratParms.appendCardsOnShowFromOpponent(opponent, opponentTwo) + stratParms.appendCardsOnShowFromOpponent(opponent, opponentThree) + + return stratParms + } + + private fun getPlayer(playerNumber: Int) = + when (playerNumber) { + 0 -> opponentZero + 1 -> opponentOne + 2 -> opponentTwo + else -> opponentThree + } +} diff --git a/client/src/main/kotlin/util/SimulationParams.kt b/client/src/main/kotlin/util/SimulationParams.kt new file mode 100644 index 00000000..2be2af8e --- /dev/null +++ b/client/src/main/kotlin/util/SimulationParams.kt @@ -0,0 +1,30 @@ +package util + +import game.GameSettings + +data class SimulationParams( + val settings: GameSettings, + val opponentTwoEnabled: Boolean, + val opponentThreeEnabled: Boolean, + val opponentZeroStrategy: String, + val opponentOneStrategy: String, + val opponentTwoStrategy: String, + val opponentThreeStrategy: String, + val enableLogging: Boolean, + val randomiseOrder: Boolean, + val forceStart: Boolean, +) { + fun toStrategyParams(): StrategyParms { + val parms = StrategyParms() + parms.gameMode = settings.mode + parms.includeMoons = settings.includeMoons + parms.includeStars = settings.includeStars + parms.negativeJacks = settings.negativeJacks + parms.cardReveal = settings.cardReveal + parms.jokerQuantity = settings.jokerQuantity + parms.jokerValue = settings.jokerValue + parms.logging = enableLogging + + return parms + } +} diff --git a/core/src/main/java/util/Registry.java b/core/src/main/java/util/Registry.java index c7f43f09..891a96e8 100644 --- a/core/src/main/java/util/Registry.java +++ b/core/src/main/java/util/Registry.java @@ -28,6 +28,7 @@ public interface Registry public static final String SHARED_BOOLEAN_CARD_REVEAL = "cardReveal"; public static final String SHARED_INT_JOKER_VALUE = "jokerValue"; public static final String SHARED_INT_JOKER_QUANTITY = "jokerQuantity"; + public static final String SHARED_INT_NUMBER_OF_CARDS = "numberOfCards"; //prefs public static final String PREFERENCES_STRING_REPLAY_DIRECTORY = "replayDirectory"; @@ -69,7 +70,6 @@ public interface Registry public static final String PREFERENCES_INT_HANDICAP_AMOUNT = "handicapAmount"; public static final String PREFERENCES_INT_JOKER_VALUE = "jokerValue"; public static final String PREFERENCES_INT_JOKER_QUANTITY = "jokerQuantity"; - public static final String PREFERENCES_INT_NUMBER_OF_CARDS = "numberOfCards"; public static final String PREFERENCES_INT_REPLAY_VIEWER_HEIGHT = "rvheight"; public static final String PREFERENCES_INT_REPLAY_VIEWER_WIDTH = "rvwidth"; public static final String PREFERENCES_STRING_GAME_MODE = "gameMode"; diff --git a/core/src/main/kotlin/game/GameSettings.kt b/core/src/main/kotlin/game/GameSettings.kt index 7519e040..8d939146 100644 --- a/core/src/main/kotlin/game/GameSettings.kt +++ b/core/src/main/kotlin/game/GameSettings.kt @@ -7,9 +7,11 @@ import util.Registry.SHARED_BOOLEAN_INCLUDE_STARS import util.Registry.SHARED_BOOLEAN_NEGATIVE_JACKS import util.Registry.SHARED_INT_JOKER_QUANTITY import util.Registry.SHARED_INT_JOKER_VALUE +import util.Registry.SHARED_INT_NUMBER_OF_CARDS data class GameSettings( val mode: GameMode, + val numberOfCards: Int = 5, val jokerQuantity: Int = 0, val jokerValue: Int = 0, val includeMoons: Boolean = false, @@ -23,6 +25,7 @@ data class GameSettings( fun fromRegistry(node: Preferences, mode: GameMode) = GameSettings( mode, + numberOfCards = node.getInt(SHARED_INT_NUMBER_OF_CARDS, 5), jokerQuantity = node.getInt(SHARED_INT_JOKER_QUANTITY, 2), jokerValue = node.getInt(SHARED_INT_JOKER_VALUE, 2), includeMoons = node.getBoolean(SHARED_BOOLEAN_INCLUDE_MOONS, false), diff --git a/test-core/src/main/kotlin/testCore/DtoFactory.kt b/test-core/src/main/kotlin/testCore/DtoFactory.kt index d4a69030..51892dc8 100644 --- a/test-core/src/main/kotlin/testCore/DtoFactory.kt +++ b/test-core/src/main/kotlin/testCore/DtoFactory.kt @@ -18,6 +18,7 @@ fun makeOnlineMessage( fun makeGameSettings( mode: GameMode = GameMode.Entropy, + numberOfCards: Int = 5, jokerQuantity: Int = 0, jokerValue: Int = 0, includeMoons: Boolean = false, @@ -28,6 +29,7 @@ fun makeGameSettings( ) = GameSettings( mode, + numberOfCards, jokerQuantity, jokerValue, includeMoons, From 990029bffff57c2a8ba994265d25de363bb7f91b Mon Sep 17 00:00:00 2001 From: alyssa Date: Fri, 27 Jun 2025 16:06:12 +0100 Subject: [PATCH 16/16] more WIP --- .../src/main/java/online/screen/GameRoom.java | 37 +++++++++---------- .../src/main/java/util/VectCpuStrategies.java | 28 +++++++------- core/src/main/java/util/VectropyUtil.java | 25 +++++++------ 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index 8d3966ce..e3e70431 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -1,9 +1,6 @@ package online.screen; -import game.BidAction; -import game.ChallengeAction; -import game.GameMode; -import game.GameSettings; +import game.*; import http.dto.RoomSummary; import object.*; import online.util.XmlBuilderClient; @@ -66,14 +63,14 @@ public abstract class GameRoom> extends JFrame private ConcurrentHashMap hmPlayerByAdjustedPlayerNumber = new ConcurrentHashMap<>(); public ConcurrentHashMap> hmHandByAdjustedPlayerNumber = new ConcurrentHashMap<>(); - public ConcurrentHashMap hmBidByPlayerNumber = new ConcurrentHashMap<>(); + public ConcurrentHashMap hmBidByPlayerNumber = new ConcurrentHashMap<>(); private int personToStartLocal = -1; private int personToStart = -1; public int lastPlayerToAct = 0; private OnlineChatPanel chatPanel = null; public Preferences replay = null; public ReplayDialog replayDialog = new ReplayDialog(); - public BidPanel bidPanel = null; + public BidPanel bidPanel = null; public GameRoom(UUID id, String roomName, GameSettings settings, int players) { @@ -769,7 +766,7 @@ public void startObserving(int personToStart, int lastPlayerToAct) handPanel.displayHandsOnline(hmHandByAdjustedPlayerNumber); handPanel.setInitted(true); - Bid lastBid = hmBidByPlayerNumber.get(lastPlayerToAct); + PlayerAction lastBid = hmBidByPlayerNumber.get(lastPlayerToAct); if (lastBid != null) { handleBid(lastPlayerToAct, lastBid); @@ -781,39 +778,39 @@ public void clearHands() hmHandByAdjustedPlayerNumber.clear(); } - public void handleBid(int playerNumber, BidAction bid) + public void handleBid(int playerNumber, PlayerAction action) { int playerNumberAdjusted = adjustForMe(playerNumber); Player player = hmPlayerByAdjustedPlayerNumber.get(playerNumberAdjusted); - if (player != null) - { - bid.setPlayer(player); - } - addBidToBidBox(playerNumberAdjusted, bid); + addBidToBidBox(playerNumberAdjusted, action); handPanel.selectPlayerInAwtThread(playerNumberAdjusted, false); - if (bid instanceof ChallengeAction - || bid.isIllegal()) + if (action instanceof ChallengeAction + || action instanceof IllegalAction) { processChallengeOrIllegal(); return; } + + if (!(action instanceof BidAction)) { + throw new RuntimeException("Wtf"); + } lastPlayerToAct = playerNumber; - lastBid = bid; - hmBidByPlayerNumber.put(playerNumber, bid); + lastBid = (B)action; + hmBidByPlayerNumber.put(playerNumber, lastBid); if (playerNumber != this.playerNumber && !observer) { - bidPanel.adjust(bid); + bidPanel.adjust(lastBid); } if (settings.getCardReveal()) { - String card = bid.getCardToReveal(); - if (!card.isEmpty()) + String card = lastBid.getCardToReveal(); + if (card != null) { handPanel.revealCard(card); player.addRevealedCard(card); diff --git a/client/src/main/java/util/VectCpuStrategies.java b/client/src/main/java/util/VectCpuStrategies.java index 7a30fe3a..22e39371 100644 --- a/client/src/main/java/util/VectCpuStrategies.java +++ b/client/src/main/java/util/VectCpuStrategies.java @@ -3,6 +3,7 @@ import java.util.*; import game.Suit; +import game.VectropyBidAction; import object.Bid; import object.ChallengeBid; import object.Player; @@ -60,7 +61,7 @@ private static Bid processBasicTurn(Player opponent, StrategyParams parms) //Get the variables we're interested in var settings = parms.getSettings(); List hand = opponent.getHand(); - VectropyBid lastBid = (VectropyBid)parms.getLastBid(); + VectropyBidAction lastBid = (VectropyBidAction)parms.getLastBid(); double totalCards = parms.getCardsInPlay(); int jokerValue = settings.getJokerValue(); boolean includeMoons = settings.getIncludeMoons(); @@ -79,8 +80,9 @@ private static Bid processBasicTurn(Player opponent, StrategyParams parms) if (totalCards <= 4) { + var bid = VectropyBidAction(); VectropyBid bid = VectropyUtil.getEmptyBid(includeMoons, includeStars); - return bid.incrementSuitAndGet(StrategyUtil.getRandomSuit(includeMoons, includeStars)); + return bid.incrementSuitAndGet(Suit.random(includeMoons, includeStars)); } else { @@ -193,15 +195,15 @@ private static Bid processEvTurn(Player opponent, StrategyParams parms) if (lastBid == null) { Debug.append("Starting this round", logging); - Map hmEvBySuit = getEvMap(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms.getSettings(), parms.getCardsInPlay()); Debug.append("EV HashMap = " + hmEvBySuit, logging); - int clubsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_CLUBS, hmEvBySuit); - int diamondsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_DIAMONDS, hmEvBySuit); - int heartsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_HEARTS, hmEvBySuit); - int moonsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_MOONS, hmEvBySuit); - int spadesBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_SPADES, hmEvBySuit); - int starsBid = getOpeningBidForSuitBasedOnEv(CardsUtil.SUIT_STARS, hmEvBySuit); + int clubsBid = getOpeningBidForSuitBasedOnEv(Suit.Clubs, hmEvBySuit); + int diamondsBid = getOpeningBidForSuitBasedOnEv(Suit.Diamonds, hmEvBySuit); + int heartsBid = getOpeningBidForSuitBasedOnEv(Suit.Hearts, hmEvBySuit); + int moonsBid = getOpeningBidForSuitBasedOnEv(Suit.Moons, hmEvBySuit); + int spadesBid = getOpeningBidForSuitBasedOnEv(Suit.Spades, hmEvBySuit); + int starsBid = getOpeningBidForSuitBasedOnEv(Suit.Stars, hmEvBySuit); VectropyBid bid = new VectropyBid(clubsBid, diamondsBid, heartsBid, moonsBid, spadesBid, starsBid, includeMoons, includeStars); @@ -210,14 +212,14 @@ private static Bid processEvTurn(Player opponent, StrategyParams parms) if (newTotal == 0) { int choice = coin.nextInt(10); - int suit = -1; + Suit suit = null; if (choice < 6) { suit = VectropyUtil.getSuitWithHighestValue(hmEvBySuit); } else { - suit = StrategyUtil.getRandomSuit(includeMoons, includeStars); + suit = Suit.random(includeMoons, includeStars); } bid = bid.incrementSuitAndGet(suit); @@ -228,7 +230,7 @@ private static Bid processEvTurn(Player opponent, StrategyParams parms) else { hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - Map hmEvBySuit = getEvMap(hand, parms); + Map hmEvBySuit = getEvMap(hand, parms.getSettings(), parms.getCardsInPlay()); Debug.append("EV HashMap = " + hmEvBySuit, logging); double[] diffVector = VectropyUtil.getEvDifferenceVector(lastBid, hmEvBySuit, includeMoons, includeStars); @@ -261,7 +263,7 @@ else if (VectropyUtil.shouldAutoChallengeForMultipleSuitsOverEv(diffVector)) } } - private static int getOpeningBidForSuitBasedOnEv(int suit, HashMap hmEvBySuit) + private static int getOpeningBidForSuitBasedOnEv(Suit suit, Map hmEvBySuit) { double ev = hmEvBySuit.get(suit); int evFloor = (int)Math.floor(ev); diff --git a/core/src/main/java/util/VectropyUtil.java b/core/src/main/java/util/VectropyUtil.java index cb8d803f..e9e00dd1 100644 --- a/core/src/main/java/util/VectropyUtil.java +++ b/core/src/main/java/util/VectropyUtil.java @@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.prefs.Preferences; +import game.Suit; import object.VectropyBid; public class VectropyUtil @@ -263,15 +264,15 @@ public static VectropyBid getEmptyBid(boolean includeMoons, boolean includeStars /** * EV Strategy */ - public static double[] getEvDifferenceVector(VectropyBid bid, HashMap hmEvBySuit, + public static double[] getEvDifferenceVector(VectropyBid bid, Map hmEvBySuit, boolean includeMoons, boolean includeStars) { - double clubsCount = hmEvBySuit.get(CardsUtil.SUIT_CLUBS) - bid.getClubs(); - double diamondsCount = hmEvBySuit.get(CardsUtil.SUIT_DIAMONDS) - bid.getDiamonds(); - double heartsCount = hmEvBySuit.get(CardsUtil.SUIT_HEARTS) - bid.getHearts(); - double moonsCount = hmEvBySuit.get(CardsUtil.SUIT_MOONS) - bid.getMoons(); - double spadesCount = hmEvBySuit.get(CardsUtil.SUIT_SPADES) - bid.getSpades(); - double starsCount = hmEvBySuit.get(CardsUtil.SUIT_STARS) - bid.getStars(); + double clubsCount = hmEvBySuit.get(Suit.Clubs) - bid.getClubs(); + double diamondsCount = hmEvBySuit.get(Suit.Diamonds) - bid.getDiamonds(); + double heartsCount = hmEvBySuit.get(Suit.Hearts) - bid.getHearts(); + double moonsCount = hmEvBySuit.get(Suit.Moons) - bid.getMoons(); + double spadesCount = hmEvBySuit.get(Suit.Spades) - bid.getSpades(); + double starsCount = hmEvBySuit.get(Suit.Stars) - bid.getStars(); if (includeMoons && includeStars) { @@ -344,20 +345,20 @@ public static boolean shouldAutoChallengeForMultipleSuitsOverEv(double[] evDiffV return numberOfSuitsOverEv >= 2; } - public static int getSuitWithHighestValue(HashMap hmEvBySuit) + public static Suit getSuitWithHighestValue(Map hmEvBySuit) { - int highestSuit = -1; + Suit highestSuit = null; double highestValue = -1; - Iterator> it = hmEvBySuit.entrySet().iterator(); + Iterator> it = hmEvBySuit.entrySet().iterator(); for (; it.hasNext();) { - Map.Entry entry = it.next(); + Map.Entry entry = it.next(); double ev = entry.getValue(); if (ev > highestValue) { highestValue = ev; - highestSuit = entry.getKey().intValue(); + highestSuit = entry.getKey(); } }