From 67bb761574a6d9a05d8f4c41da33eaca35b8b34d Mon Sep 17 00:00:00 2001 From: luigi-borriello00 Date: Tue, 10 Oct 2023 17:03:23 +0200 Subject: [PATCH] wip(trades-view): implementing trade on click --- .../controllers/game/GameController.scala | 7 +++ .../model/components/ResourceCard.scala | 11 +++++ .../scatan/model/game/ScatanEffects.scala | 14 +++++- .../scala/scatan/model/game/ScatanGame.scala | 6 ++- .../scatan/model/game/ops/TradeOps.scala | 24 +++++----- .../game/components/RightTabComponent.scala | 22 +++++++-- .../scatan/model/game/ops/TradeOpsTest.scala | 47 +++---------------- 7 files changed, 73 insertions(+), 58 deletions(-) diff --git a/src/main/scala/scatan/controllers/game/GameController.scala b/src/main/scala/scatan/controllers/game/GameController.scala index 4fc6b92a..119bed81 100644 --- a/src/main/scala/scatan/controllers/game/GameController.scala +++ b/src/main/scala/scatan/controllers/game/GameController.scala @@ -9,10 +9,12 @@ import scatan.model.map.{RoadSpot, StructureSpot} import scatan.views.game.GameView import scatan.views.game.components.CardContextMap.CardType import scatan.model.map.Hexagon +import scatan.model.components.ResourceType trait GameController extends Controller[ApplicationState]: def onRoadSpot(spot: RoadSpot): Unit def onStructureSpot(spot: StructureSpot): Unit + def onTradeWithBank(offer: ResourceType, request: ResourceType): Unit def nextTurn(): Unit def rollDice(): Unit def clickCard(card: CardType): Unit @@ -71,3 +73,8 @@ private class GameControllerImpl(requirements: Controller.Requirements[GameView, this.model .updateGame(_.buildSettlement(spot)) .onError(view.displayMessage("Cannot build settlement here")) + + override def onTradeWithBank(offer: ResourceType, request: ResourceType): Unit = + this.model + .updateGame(_.tradeWithBank(offer, request)) + .onError(view.displayMessage("Cannot trade this cards with bank" + offer.toString() + request.toString())) diff --git a/src/main/scala/scatan/model/components/ResourceCard.scala b/src/main/scala/scatan/model/components/ResourceCard.scala index e469e707..5d16f159 100644 --- a/src/main/scala/scatan/model/components/ResourceCard.scala +++ b/src/main/scala/scatan/model/components/ResourceCard.scala @@ -9,8 +9,19 @@ enum ResourceType: case Wheat case Rock +object ResourceType: + def withName(name: String): ResourceType = + name match + case "Wood" => Wood + case "Brick" => Brick + case "Sheep" => Sheep + case "Wheat" => Wheat + case "Rock" => Rock + final case class ResourceCard(resourceType: ResourceType) +extension (card: ResourceCard) def *(amount: Int): Seq[ResourceCard] = Seq.fill(amount)(card) + type ResourceCards = Map[ScatanPlayer, Seq[ResourceCard]] object ResourceCards: diff --git a/src/main/scala/scatan/model/game/ScatanEffects.scala b/src/main/scala/scatan/model/game/ScatanEffects.scala index 3214b046..c4522f3c 100644 --- a/src/main/scala/scatan/model/game/ScatanEffects.scala +++ b/src/main/scala/scatan/model/game/ScatanEffects.scala @@ -13,6 +13,9 @@ import scatan.model.game.ops.CardOps.assignResourceCard import scatan.model.game.ops.TradeOps.tradeWithPlayer import scatan.model.game.ops.CardOps.assignResourcesFromNumber import scatan.model.game.ops.RobberOps.moveRobber +import scatan.model.components.ResourceType +import scatan.model.game.ops.TradeOps.tradeWithBank +import scatan.model.components.* object ScatanEffects: @@ -55,7 +58,16 @@ object ScatanEffects: def PlayDevelopmentCardEffect(): Effect[PlayDevelopmentCard.type, ScatanState] = EmptyEffect - def TradeWithBankEffect(): Effect[TradeWithBank.type, ScatanState] = EmptyEffect + def TradeWithBankEffect( + player: ScatanPlayer, + offer: ResourceType, + request: ResourceType + ): Effect[TradeWithBank.type, ScatanState] = (state: ScatanState) => + state.tradeWithBank( + player, + offer, + request + ) def TradeWithPlayerEffect( sender: ScatanPlayer, diff --git a/src/main/scala/scatan/model/game/ScatanGame.scala b/src/main/scala/scatan/model/game/ScatanGame.scala index df4a4c86..eb57376a 100644 --- a/src/main/scala/scatan/model/game/ScatanGame.scala +++ b/src/main/scala/scatan/model/game/ScatanGame.scala @@ -12,6 +12,7 @@ import scatan.model.map.{Hexagon, RoadSpot, StructureSpot} import scala.util.Random import scatan.model.components.ResourceCard +import scatan.model.components.ResourceType /** The status of a game of Scatan. It contains all the data without any possible action. * @param game @@ -78,7 +79,10 @@ private trait ScatanGameActions extends ScatanGameStatus: play(ScatanActions.BuyDevelopmentCard)(using BuyDevelopmentCardEffect(game.turn.player, game.turn.number)) def playDevelopmentCard: Option[ScatanGame] = ??? - def tradeWithBank: Option[ScatanGame] = ??? + + def tradeWithBank(offer: ResourceType, request: ResourceType): Option[ScatanGame] = + play(ScatanActions.TradeWithBank)(using TradeWithBankEffect(game.turn.player, offer, request)) + def tradeWithPlayer( receiver: ScatanPlayer, senderTradeCards: Seq[ResourceCard], diff --git a/src/main/scala/scatan/model/game/ops/TradeOps.scala b/src/main/scala/scatan/model/game/ops/TradeOps.scala index d1ad5d74..542e0ff5 100644 --- a/src/main/scala/scatan/model/game/ops/TradeOps.scala +++ b/src/main/scala/scatan/model/game/ops/TradeOps.scala @@ -6,6 +6,7 @@ import scatan.model.components.ResourceCard import scatan.model.game.ops.CardOps.removeResourceCard import scatan.model.game.ops.CardOps.assignResourceCard import scatan.views.game.components.ContextMap.resources +import scatan.model.components.ResourceType object TradeOps: val tradeWithBankRequiredCards = 4 @@ -34,23 +35,24 @@ object TradeOps: * * @param player, * the player that will trade with the bank - * @param playerCards, - * the cards that the player will give to the bank - * @param bankCard, - * the card that the bank will give to the player + * @param playerCardsType, + * the cards type that the player will give to the bank + * @param bankCardType, + * the card type that the bank will give to the player * @return * Some(state) if the trade is allowed, None otherwise */ def tradeWithBank( player: ScatanPlayer, - playerCards: Seq[ResourceCard], - bankCard: ResourceCard + playerCardsType: ResourceType, + bankCardType: ResourceType ): Option[ScatanState] = - if playerCards.sizeIs == tradeWithBankRequiredCards && playerCards.forall( - _.resourceType == playerCards.head.resourceType - ) + println("" + state.resourceCards(player).count(_.resourceType == playerCardsType)) + if state.resourceCards(player).count(_.resourceType == playerCardsType) >= tradeWithBankRequiredCards then - playerCards + state + .resourceCards(player) + // FIX ME: This is a hack to remove 4 cards of the same type .foldLeft(Option(state))((state, card) => state.flatMap(_.removeResourceCard(player, card))) - .flatMap(_.assignResourceCard(player, bankCard)) + .flatMap(_.assignResourceCard(player, ResourceCard(bankCardType))) else None diff --git a/src/main/scala/scatan/views/game/components/RightTabComponent.scala b/src/main/scala/scatan/views/game/components/RightTabComponent.scala index bfe480ae..6fbbc59d 100644 --- a/src/main/scala/scatan/views/game/components/RightTabComponent.scala +++ b/src/main/scala/scatan/views/game/components/RightTabComponent.scala @@ -14,6 +14,9 @@ import scatan.views.utils.TypeUtils.InputSource object RightTabComponent: + val tradeOffer: Var[ResourceType] = Var(ResourceType.values.head) + val tradeRequest: Var[ResourceType] = Var(ResourceType.values.head) + def rightTabCssClass: String = "game-view-right-tab" def tradeComponent: DisplayableSource[Element] = @@ -50,10 +53,20 @@ object RightTabComponent: h3("Bank:"), div( "Trade four of ", - resourceTypeChoiceComponent, + resourceTypeChoiceComponent(tradeOffer), " for one of ", - resourceTypeChoiceComponent, - button(className := "trade-bank-button", "Trade") + resourceTypeChoiceComponent(tradeRequest), + button( + className := "trade-bank-button", + onClick --> (_ => + println("" + tradeOffer.now() + tradeRequest.now()) + gameController.onTradeWithBank( + tradeOffer.now(), + tradeRequest.now() + ) + ), + "Trade" + ) ) ) @@ -87,10 +100,11 @@ object RightTabComponent: ) ) - private def resourceTypeChoiceComponent: InputSource[Element] = + private def resourceTypeChoiceComponent(changing: Var[ResourceType]): InputSource[Element] = div( className := "game-view-resource-type-choice", select( + onChange.mapToValue.map(ResourceType.withName(_)) --> changing, className := "game-view-resource-type-choice-select", // for each type of resource add an option for resource <- ResourceType.values diff --git a/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala b/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala index 6eb479ad..fd04a91c 100644 --- a/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala @@ -87,22 +87,18 @@ class TradeOpsTest extends BaseScatanStateTest: val stateWithTrade = state.tradeWithBank( player, - Seq( - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood) - ), - ResourceCard(ResourceType.Brick) + ResourceType.Wood, + ResourceType.Brick ) stateWithTrade match case Some(state) => state.resourceCards(player) should contain(ResourceCard(ResourceType.Brick)) + state.resourceCards(player) should have size 1 case None => fail("Trade not allowed") case None => fail("Resources not assigned") } - it should "not allow to trade with bank if the player hasn't the cards" in { + it should "not allow to trade with bank if player doesn't have four identical cards" in { val state = ScatanState(threePlayers) val player = threePlayers.head val stateWithResourceAssigned = state @@ -114,39 +110,8 @@ class TradeOpsTest extends BaseScatanStateTest: val stateWithTrade = state.tradeWithBank( player, - Seq( - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood) - ), - ResourceCard(ResourceType.Brick) - ) - stateWithTrade should be(None) - case None => fail("Resources not assigned") - } - - it should "not allow to trade with bank if the offer not contains four identical cards" in { - val state = ScatanState(threePlayers) - val player = threePlayers.head - val stateWithResourceAssigned = state - .assignResourceCard(player, ResourceCard(ResourceType.Wood)) - .flatMap(_.assignResourceCard(player, ResourceCard(ResourceType.Wood))) - .flatMap(_.assignResourceCard(player, ResourceCard(ResourceType.Wood))) - .flatMap(_.assignResourceCard(player, ResourceCard(ResourceType.Wood))) - .flatMap(_.assignResourceCard(player, ResourceCard(ResourceType.Brick))) - stateWithResourceAssigned match - case Some(state) => - val stateWithTrade = - state.tradeWithBank( - player, - Seq( - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Wood), - ResourceCard(ResourceType.Brick) - ), - ResourceCard(ResourceType.Brick) + ResourceType.Wood, + ResourceType.Brick ) stateWithTrade should be(None) case None => fail("Resources not assigned")