From e180257fab90ca58585292844d1885dc6b9ef0ec Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Sun, 8 Oct 2023 19:09:09 +0200 Subject: [PATCH 01/19] fix(scatan-state): ambiguos import --- src/main/scala/scatan/model/game/ScatanState.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/scatan/model/game/ScatanState.scala b/src/main/scala/scatan/model/game/ScatanState.scala index a988e3a3..1044d22c 100644 --- a/src/main/scala/scatan/model/game/ScatanState.scala +++ b/src/main/scala/scatan/model/game/ScatanState.scala @@ -7,6 +7,7 @@ import scatan.model.components.DevelopmentType.Knight import scatan.model.game.config.ScatanPlayer import scatan.model.map.* import scala.collection.mutable.ListMap +import scatan.model.components.UnproductiveTerrain.Desert /** Represents the state of a Scatan game. * @@ -50,7 +51,7 @@ object ScatanState: def apply(players: Seq[ScatanPlayer], developmentCardsDeck: DevelopmentCardsDeck): ScatanState = require(players.sizeIs >= 3 && players.sizeIs <= 4, "The number of players must be between 3 and 4") val gameMap = GameMap() - val desertHexagon = gameMap.tiles.find(gameMap.toContent(_).terrain == UnproductiveTerrain.Desert).get + val desertHexagon = gameMap.tiles.find(gameMap.toContent(_).terrain == Desert).get ScatanState( players, GameMap(), From 7636f9b9a78f04957e40f3c94a608df903540af6 Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Mon, 9 Oct 2023 15:07:06 +0200 Subject: [PATCH 02/19] refactor: extract type as context function --- src/main/scala/scatan/views/utils/TypeUtils.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/scala/scatan/views/utils/TypeUtils.scala diff --git a/src/main/scala/scatan/views/utils/TypeUtils.scala b/src/main/scala/scatan/views/utils/TypeUtils.scala new file mode 100644 index 00000000..e19b4b43 --- /dev/null +++ b/src/main/scala/scatan/views/utils/TypeUtils.scala @@ -0,0 +1,14 @@ +package scatan.views.utils + +import scatan.model.ApplicationState +import scatan.controllers.game.GameController +import scatan.model.game.ScatanState +import com.raquo.airstream.core.Signal + +object TypeUtils: + + type Displayable[T] = Signal[ApplicationState] ?=> T + type InputSource[T] = GameController ?=> T + type DisplayableSource[T] = Displayable[InputSource[T]] + type StateKnoledge[T] = ScatanState ?=> T + type InputSourceWithState[T] = InputSource[StateKnoledge[T]] From 45a88044566871d5c07e04838648075a027ede27 Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Mon, 9 Oct 2023 15:20:00 +0200 Subject: [PATCH 03/19] refactor(utils): add method for common summoned types --- src/main/scala/scatan/views/utils/TypeUtils.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/scala/scatan/views/utils/TypeUtils.scala b/src/main/scala/scatan/views/utils/TypeUtils.scala index e19b4b43..bf74ba1b 100644 --- a/src/main/scala/scatan/views/utils/TypeUtils.scala +++ b/src/main/scala/scatan/views/utils/TypeUtils.scala @@ -12,3 +12,10 @@ object TypeUtils: type DisplayableSource[T] = Displayable[InputSource[T]] type StateKnoledge[T] = ScatanState ?=> T type InputSourceWithState[T] = InputSource[StateKnoledge[T]] + + private[views] def reactiveState(using Signal[ApplicationState]): Signal[ApplicationState] = + summon[Signal[ApplicationState]] + private[views] def gameController(using GameController): GameController = + summon[GameController] + private[views] def state(using ScatanState): ScatanState = + summon[ScatanState] From 464ba40cce8dbca7e4317ceabed419d5ebfa23cf Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Mon, 9 Oct 2023 15:26:48 +0200 Subject: [PATCH 04/19] refactor: move file --- src/main/scala/scatan/views/{ => utils}/Coordinate.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/scala/scatan/views/{ => utils}/Coordinate.scala (98%) diff --git a/src/main/scala/scatan/views/Coordinate.scala b/src/main/scala/scatan/views/utils/Coordinate.scala similarity index 98% rename from src/main/scala/scatan/views/Coordinate.scala rename to src/main/scala/scatan/views/utils/Coordinate.scala index efe0c006..8365348a 100644 --- a/src/main/scala/scatan/views/Coordinate.scala +++ b/src/main/scala/scatan/views/utils/Coordinate.scala @@ -1,4 +1,4 @@ -package scatan.views +package scatan.views.utils import scatan.model.map.{Hexagon, StructureSpot} From 8eca18c0679abb9c0d8ae3d771fcc64a4a0d31e1 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Mon, 9 Oct 2023 16:15:45 +0200 Subject: [PATCH 05/19] chore: moved dsl to old package to integrate the new one --- .../scala/scatan/lib/game/dsl/{ => old}/GameDSL.scala | 3 ++- .../scatan/lib/game/dsl/{ => old}/PhaseDSLOps.scala | 6 +++--- .../scatan/lib/game/dsl/{ => old}/PhasesDSLOps.scala | 4 ++-- .../scatan/lib/game/dsl/{ => old}/PlayersDSLOps.scala | 4 ++-- .../scatan/lib/game/dsl/{ => old}/TurnDSLOps.scala | 2 +- .../scatan/lib/game/dsl/{ => old}/TypedGameDSL.scala | 7 ++++--- src/main/scala/scatan/model/game/ScatanDSL.scala | 10 +++++----- src/test/scala/scatan/lib/game/EmptyDomain.scala | 10 +++++----- 8 files changed, 24 insertions(+), 22 deletions(-) rename src/main/scala/scatan/lib/game/dsl/{ => old}/GameDSL.scala (94%) rename src/main/scala/scatan/lib/game/dsl/{ => old}/PhaseDSLOps.scala (91%) rename src/main/scala/scatan/lib/game/dsl/{ => old}/PhasesDSLOps.scala (89%) rename src/main/scala/scatan/lib/game/dsl/{ => old}/PlayersDSLOps.scala (91%) rename src/main/scala/scatan/lib/game/dsl/{ => old}/TurnDSLOps.scala (98%) rename src/main/scala/scatan/lib/game/dsl/{ => old}/TypedGameDSL.scala (89%) diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala similarity index 94% rename from src/main/scala/scatan/lib/game/dsl/GameDSL.scala rename to src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala index 1eb68934..b3adb805 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala @@ -1,6 +1,7 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old /** A type-safe DSL for defining games. + * * @tparam Player * The type of player in the game. * @tparam State diff --git a/src/main/scala/scatan/lib/game/dsl/PhaseDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala similarity index 91% rename from src/main/scala/scatan/lib/game/dsl/PhaseDSLOps.scala rename to src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala index c8f8dc9f..023355f1 100644 --- a/src/main/scala/scatan/lib/game/dsl/PhaseDSLOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala @@ -1,8 +1,8 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old import scatan.lib.game.GameStatus -import scatan.lib.game.dsl.TurnDSLOps.TurnDSLContext -import scatan.lib.game.ops.RulesOps.{withActions, withOnEnter} +import scatan.lib.game.dsl.old.TurnDSLOps.TurnDSLContext +import scatan.lib.game.ops.RulesOps.* /** Operations for defining phases and steps in a game. */ diff --git a/src/main/scala/scatan/lib/game/dsl/PhasesDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala similarity index 89% rename from src/main/scala/scatan/lib/game/dsl/PhasesDSLOps.scala rename to src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala index a7f41510..a3d58595 100644 --- a/src/main/scala/scatan/lib/game/dsl/PhasesDSLOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala @@ -1,6 +1,6 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old -import scatan.lib.game.dsl.PhaseDSLOps.PhaseDSLContext +import scatan.lib.game.dsl.old.PhaseDSLOps.PhaseDSLContext /** Operations for defining phases in a game. */ diff --git a/src/main/scala/scatan/lib/game/dsl/PlayersDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala similarity index 91% rename from src/main/scala/scatan/lib/game/dsl/PlayersDSLOps.scala rename to src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala index ea3330a2..fc9b311c 100644 --- a/src/main/scala/scatan/lib/game/dsl/PlayersDSLOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala @@ -1,6 +1,6 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old -import scatan.lib.game.ops.RulesOps.withAllowedPlayersSizes +import scatan.lib.game.ops.RulesOps.* /** Operations for the players DSL. */ diff --git a/src/main/scala/scatan/lib/game/dsl/TurnDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala similarity index 98% rename from src/main/scala/scatan/lib/game/dsl/TurnDSLOps.scala rename to src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala index 9504da4c..47f4a933 100644 --- a/src/main/scala/scatan/lib/game/dsl/TurnDSLOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala @@ -1,4 +1,4 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old import scatan.lib.game.ops.RulesOps.* diff --git a/src/main/scala/scatan/lib/game/dsl/TypedGameDSL.scala b/src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala similarity index 89% rename from src/main/scala/scatan/lib/game/dsl/TypedGameDSL.scala rename to src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala index f794d06b..7e66308c 100644 --- a/src/main/scala/scatan/lib/game/dsl/TypedGameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala @@ -1,11 +1,12 @@ -package scatan.lib.game.dsl +package scatan.lib.game.dsl.old import scatan.lib.game.Rules -import scatan.lib.game.dsl.PhasesDSLOps.PhasesDSLContext -import scatan.lib.game.dsl.PlayersDSLOps.PlayersDSLContext +import scatan.lib.game.dsl.old.PhasesDSLOps.PhasesDSLContext +import scatan.lib.game.dsl.old.PlayersDSLOps.PlayersDSLContext import scatan.lib.game.ops.RulesOps.* /** A DSL for defining a game. + * * @tparam State * The type of the game state. * @tparam PhaseType diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index 0a0ccd9f..db2dfef2 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -1,10 +1,10 @@ package scatan.model.game -import scatan.lib.game.dsl.PhaseDSLOps.* -import scatan.lib.game.dsl.PhasesDSLOps.* -import scatan.lib.game.dsl.PlayersDSLOps.* -import scatan.lib.game.dsl.TurnDSLOps.* -import scatan.lib.game.dsl.{GameDSL, PhaseDSLOps, PhasesDSLOps} +import scatan.lib.game.dsl.old.{GameDSL, PhaseDSLOps, PhasesDSLOps} +import scatan.lib.game.dsl.old.PhaseDSLOps.* +import scatan.lib.game.dsl.old.PhasesDSLOps.* +import scatan.lib.game.dsl.old.PlayersDSLOps.* +import scatan.lib.game.dsl.old.TurnDSLOps.* import scatan.model.game.config.{ScatanActions, ScatanPhases, ScatanPlayer, ScatanSteps} import scatan.model.game.ops.CardOps.assignResourcesAfterInitialPlacement import scatan.model.game.ops.ScoreOps.* diff --git a/src/test/scala/scatan/lib/game/EmptyDomain.scala b/src/test/scala/scatan/lib/game/EmptyDomain.scala index 7b75561f..393011a9 100644 --- a/src/test/scala/scatan/lib/game/EmptyDomain.scala +++ b/src/test/scala/scatan/lib/game/EmptyDomain.scala @@ -3,11 +3,11 @@ package scatan.lib.game import scatan.lib.game.EmptyDomain.MyPhases import scatan.lib.game.EmptyDomain.MyPhases.* import scatan.lib.game.EmptyDomain.Steps.Initial -import scatan.lib.game.dsl.PhaseDSLOps.{Turn, When} -import scatan.lib.game.dsl.PhasesDSLOps.On -import scatan.lib.game.dsl.PlayersDSLOps.canBe -import scatan.lib.game.dsl.TurnDSLOps.* -import scatan.lib.game.dsl.{GameDSL, PhaseDSLOps, TurnDSLOps} +import scatan.lib.game.dsl.old.{GameDSL, PhaseDSLOps, TurnDSLOps} +import scatan.lib.game.dsl.old.PhaseDSLOps.{Turn, When} +import scatan.lib.game.dsl.old.PhasesDSLOps.On +import scatan.lib.game.dsl.old.PlayersDSLOps.canBe +import scatan.lib.game.dsl.old.TurnDSLOps.* import scatan.lib.game.ops.Effect object EmptyDomain: From fcfae61fb3f126bee42ffa2dab33a764b0f1efdb Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Mon, 9 Oct 2023 16:19:19 +0200 Subject: [PATCH 06/19] chore: added the new dsl --- .../scala/scatan/lib/game/dsl/GameDSL.scala | 12 +++++ .../scatan/lib/game/dsl/GameDSLDomain.scala | 21 ++++++++ .../scatan/lib/game/dsl/PropertiesDSL.scala | 54 +++++++++++++++++++ .../scatan/lib/game/dsl/ops/GameCtxOps.scala | 15 ++++++ .../scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 11 ++++ .../lib/game/dsl/ops/PhasesCtxOps.scala | 8 +++ .../lib/game/dsl/ops/PlayersCtxOps.scala | 8 +++ 7 files changed, 129 insertions(+) create mode 100644 src/main/scala/scatan/lib/game/dsl/GameDSL.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala new file mode 100644 index 00000000..a4fea4ca --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -0,0 +1,12 @@ +package scatan.lib.game.dsl + +object GameDSL: + import GameDSLDomain.* + import PropertiesDSL.{*, given} + + export ops.GameCtxOps.* + export ops.PlayersCtxOps.* + export ops.PhasesCtxOps.* + export ops.PhaseCtxOps.* + + def Game[State, P, S, A, Player]: PropertyBuilder[GameCtx[State, P, S, A, Player]] = PropertyBuilder() diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala new file mode 100644 index 00000000..e454319c --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala @@ -0,0 +1,21 @@ +package scatan.lib.game.dsl + +private object GameDSLDomain: + + import PropertiesDSL.* + + class GameCtx[State, P, S, A, Player]: + val phases: OptionalProperty[PhasesCtx[P, S, A]] = property + val players: OptionalProperty[PlayersCtx] = property + val winner: OptionalProperty[State => Boolean] = property + val initialPhase: OptionalProperty[P] = property + + class PlayersCtx: + var allowedSizes: OptionalProperty[Seq[Int]] = property + + class PhasesCtx[P, S, A]: + var phase: SequenceProperty[PhaseCtx[P, S, A]] = property + + class PhaseCtx[P, S, A]: + var phase: OptionalProperty[P] = property + var when: SequenceProperty[(A, S)] = property diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala new file mode 100644 index 00000000..d3cfdbb6 --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -0,0 +1,54 @@ +package scatan.lib.game.dsl + +import scala.annotation.targetName +import scala.reflect.ClassTag + +object PropertiesDSL: + + // Properties + + sealed trait Property[P]: + def apply(newValue: P): Unit + + final class OptionalProperty[P] extends Property[P]: + var value: Option[P] = None + override def apply(newValue: P): Unit = value = Some(newValue) + + final class SequenceProperty[P] extends Property[P]: + var value: Seq[P] = Seq.empty + override def apply(newValue: P): Unit = value = value :+ newValue + + // Setter + + class PropertySetter[P](property: Property[P]): + @targetName("set") + def :=(value: P): Unit = property(value) + + given [P]: Conversion[Property[P], PropertySetter[P]] = PropertySetter(_) + + // Updater + + private type Updater[P] = P ?=> Unit + + class PropertyUpdater[P: ClassTag](property: Property[P]): + def apply(updater: Updater[P]): Unit = + val prop = summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] + updater(using prop) + property(prop) + + given [P: ClassTag]: Conversion[Property[P], PropertyUpdater[P]] = PropertyUpdater(_) + + // Builder + + class PropertyBuilder[P: ClassTag]: + def apply(updater: Updater[P]): P = + val ctx = summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] + updater(using ctx) + ctx + + // Utils + + def property[P <: Property[?]](using ClassTag[P]): P = + summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] + + type Contexted[Ctx, P] = Ctx ?=> P diff --git a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala new file mode 100644 index 00000000..b964a1f6 --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala @@ -0,0 +1,15 @@ +package scatan.lib.game.dsl.ops + +import scatan.lib.game.dsl.PropertiesDSL.* +import scatan.lib.game.dsl.GameDSLDomain.* + +object GameCtxOps: + + def winner[S]: Contexted[GameCtx[S, ?, ?, ?, ?], PropertySetter[S => Boolean]] = + ctx ?=> ctx.winner + + def phases[P, S, A]: Contexted[GameCtx[?, P, S, A, ?], PropertyUpdater[PhasesCtx[P, S, A]]] = + ctx ?=> ctx.phases + + def players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyUpdater[PlayersCtx]] = + ctx ?=> ctx.players diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala new file mode 100644 index 00000000..4ad81c70 --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -0,0 +1,11 @@ +package scatan.lib.game.dsl.ops + +import scatan.lib.game.dsl.PropertiesDSL.* +import scatan.lib.game.dsl.GameDSLDomain.* + +object PhaseCtxOps: + def Phase[P, S, A]: Contexted[PhaseCtx[P, S, A], PropertySetter[P]] = + ctx ?=> ctx.phase + + def when[P, S, A]: Contexted[PhaseCtx[P, S, A], PropertySetter[(A, S)]] = + ctx ?=> ctx.when \ No newline at end of file diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala new file mode 100644 index 00000000..2c657247 --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala @@ -0,0 +1,8 @@ +package scatan.lib.game.dsl.ops + +import scatan.lib.game.dsl.PropertiesDSL.* +import scatan.lib.game.dsl.GameDSLDomain.* + +object PhasesCtxOps: + def phase[P, S, A]: Contexted[PhasesCtx[P, S, A], PropertyUpdater[PhaseCtx[P, S, A]]] = + ctx ?=> ctx.phase \ No newline at end of file diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala new file mode 100644 index 00000000..016026a0 --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala @@ -0,0 +1,8 @@ +package scatan.lib.game.dsl.ops + +import scatan.lib.game.dsl.PropertiesDSL.* +import scatan.lib.game.dsl.GameDSLDomain.* + +object PlayersCtxOps: + def canBe: Contexted[PlayersCtx, PropertySetter[Seq[Int]]] = + ctx ?=> ctx.allowedSizes From 6e414b697d4538b964a9b38ff961fac5c44275ec Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Mon, 9 Oct 2023 17:21:01 +0200 Subject: [PATCH 07/19] refactor(components): use context function as components types --- .../game/components/CardsComponent.scala | 8 +- .../game/components/GameMapComponent.scala | 107 ++++++++---------- .../game/components/LeftTabComponent.scala | 24 ++-- 3 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/main/scala/scatan/views/game/components/CardsComponent.scala b/src/main/scala/scatan/views/game/components/CardsComponent.scala index 2be73c09..73aa420c 100644 --- a/src/main/scala/scatan/views/game/components/CardsComponent.scala +++ b/src/main/scala/scatan/views/game/components/CardsComponent.scala @@ -13,6 +13,8 @@ import scatan.views.game.components.CardContextMap.cardImages import scatan.views.game.components.CardContextMap.countCardOf import scatan.views.game.components.CardContextMap.CardType import scatan.controllers.game.GameController +import scatan.views.utils.TypeUtils.{DisplayableSource, Displayable} +import scatan.views.utils.TypeUtils.{reactiveState, gameController, state} object CardContextMap: extension (state: ScatanState) @@ -38,16 +40,14 @@ object CardContextMap: ) object CardsComponent: - def cardsComponent(using reactiveState: Signal[ApplicationState])(using gameController: GameController): Element = + def cardsComponent: DisplayableSource[Element] = div( cls := "game-view-card-container", cardCountComponent(cardImages.collect { case (k: ResourceType, v) => (k, v) }), cardCountComponent(cardImages.collect { case (k: DevelopmentType, v) => (k, v) }) ) - private def cardCountComponent(using reactiveState: Signal[ApplicationState])(using gameController: GameController)( - cards: Map[CardType, String] - ): Element = + private def cardCountComponent(cards: Map[CardType, String]): DisplayableSource[Element] = div( cls := "game-view-child-container", for (cardType, path) <- cards.toList diff --git a/src/main/scala/scatan/views/game/components/GameMapComponent.scala b/src/main/scala/scatan/views/game/components/GameMapComponent.scala index f1566810..107b0ec9 100644 --- a/src/main/scala/scatan/views/game/components/GameMapComponent.scala +++ b/src/main/scala/scatan/views/game/components/GameMapComponent.scala @@ -9,9 +9,13 @@ import scatan.model.game.ScatanState import scatan.model.game.config.ScatanPlayer import scatan.model.map.{Hexagon, StructureSpot, TileContent} import scatan.model.{ApplicationState, GameMap} -import scatan.views.Coordinates -import scatan.views.Coordinates.* +import scatan.model.components.AssignedBuildings +import scatan.views.utils.Coordinates +import scatan.views.utils.Coordinates.* import scatan.views.game.components.ContextMap.{viewBuildingType, viewPlayer, toImgId} +import scatan.model.map.{RoadSpot, Spot} +import scatan.views.utils.TypeUtils.{DisplayableSource, InputSourceWithState, InputSource, StateKnoledge} +import scatan.views.utils.TypeUtils.{gameController, state, reactiveState} object ContextMap: @@ -39,7 +43,6 @@ object ContextMap: viewPlayers = viewPlayers + (player -> viewPlayer) viewPlayer - extension (state: ScatanState) def gameMap: GameMap = state.gameMap extension (info: AssignmentInfo) def viewPlayer: String = updateAndGetPlayer(info.player) def viewBuildingType: String = buildings(info.buildingType) @@ -62,7 +65,7 @@ object GameMapComponent: yield s"$x,$y").mkString(" ") private val layersToCanvasSize: Int => Int = x => (2 * x * hexSize) + 50 - def mapComponent(using reactiveState: Signal[ApplicationState])(using gameController: GameController): Element = + def mapComponent: DisplayableSource[Element] = div( className := "game-view-game-tab", child <-- reactiveState @@ -70,34 +73,30 @@ object GameMapComponent: (for game <- state.game state = game.state - yield getHexagonalMap(state)).getOrElse(div("No game")) + yield + given ScatanState = state + getHexagonalMap + ).getOrElse(div("No game")) ) ) - private def getHexagonalMap(state: ScatanState)(using gameController: GameController): Element = - val gameMap = state.gameMap + private def gameMap(using ScatanState): GameMap = state.gameMap + private def contentOf(hex: Hexagon)(using ScatanState): TileContent = state.gameMap.toContent(hex) + private def robberPlacement(using ScatanState): Hexagon = summon[ScatanState].robberPlacement + private def assignmentInfoOf(spot: Spot)(using ScatanState): Option[AssignmentInfo] = + summon[ScatanState].assignedBuildings.get(spot) + + private def getHexagonalMap: InputSourceWithState[Element] = val canvasSize = layersToCanvasSize(gameMap.totalLayers) svg.svg( svgImages, svg.viewBox := s"-${canvasSize} -${canvasSize} ${2 * canvasSize} ${2 * canvasSize}", - for - hex <- gameMap.tiles.toList - content = gameMap.toContent(hex) - hasRobber = state.robberPlacement == hex - yield svgHexagonWithNumber(hex, content, hasRobber, () => gameController.placeRobber(hex)), - for - road <- gameMap.edges.toList - spot1Coordinates <- road._1.coordinates - spot2Coordinates <- road._2.coordinates - player = state.assignedBuildings.get(road).map(_.viewPlayer) - yield svgRoad(spot1Coordinates, spot2Coordinates, player, () => gameController.onRoadSpot(road)), - for - spot <- gameMap.nodes.toList - coordinates <- spot.coordinates - assignmentInfo = state.assignedBuildings.get(spot) - player = assignmentInfo.map(_.viewPlayer) - buildingType = assignmentInfo.map(_.viewBuildingType) - yield svgSpot(coordinates, player, buildingType, () => gameController.onStructureSpot(spot)) + for hex <- gameMap.tiles.toList + yield svgHexagonWithNumber(hex), + for road <- gameMap.edges.toList + yield svgRoad(road), + for spot <- gameMap.nodes.toList + yield svgSpot(spot) ) /** A svg hexagon. @@ -107,23 +106,18 @@ object GameMapComponent: * @return * the svg hexagon. */ - private def svgHexagonWithNumber( - hex: Hexagon, - tileContent: TileContent, - hasRobber: Boolean, - onPlaceRobber: () => Unit - ): Element = + private def svgHexagonWithNumber(hex: Hexagon): InputSourceWithState[Element] = val Coordinates(x, y) = hex.center svg.g( svg.transform := s"translate($x, $y)", svg.polygon( svg.points := svgCornersPoints, svg.cls := "hexagon", - svg.fill := s"url(#${tileContent.terrain.toImgId})" + svg.fill := s"url(#${contentOf(hex).terrain.toImgId})" ), - tileContent.terrain match + contentOf(hex).terrain match case Sea => "" - case _ => circularNumberWithRobber(tileContent.number, hasRobber, onPlaceRobber) + case _ => circularNumberWithRobber(hex) ) /** A svg circular number @@ -131,7 +125,7 @@ object GameMapComponent: * the number to display * @return */ - private def circularNumberWithRobber(number: Option[Int], hasRobber: Boolean, onPlaceRobber: () => Unit): Element = + private def circularNumberWithRobber(hex: Hexagon): InputSourceWithState[Element] = svg.g( svg.circle( svg.cx := "0", @@ -144,10 +138,12 @@ object GameMapComponent: svg.y := "0", svg.fontSize := s"$radius", svg.className := "hexagon-center-number", - number.map(_.toString).getOrElse("") + contentOf(hex).number.map(_.toString).getOrElse("") ), - onClick --> (_ => onPlaceRobber()), - if hasRobber then robberCross else "" + onClick --> (_ => gameController.placeRobber(hex)), + if robberPlacement == hex + then robberCross + else "" ) private def robberCross: Element = @@ -175,23 +171,19 @@ object GameMapComponent: * @return * the road graphic */ - private def svgRoad( - spot1: Coordinates, - spot2: Coordinates, - withPlayer: Option[String], - onRoadClick: () => Unit - ): Element = - val Coordinates(x1, y1) = spot1 - val Coordinates(x2, y2) = spot2 + private def svgRoad(road: RoadSpot): InputSourceWithState[Element] = + val Coordinates(x1, y1) = road._1.coordinates.get + val Coordinates(x2, y2) = road._2.coordinates.get + val player = assignmentInfoOf(road).map(_.viewPlayer) svg.g( svg.line( svg.x1 := s"${x1}", svg.y1 := s"${y1}", svg.x2 := s"${x2}", svg.y2 := s"${y2}", - svg.className := s"road ${withPlayer.getOrElse("")}" + svg.className := s"road ${player.getOrElse("")}" ), - withPlayer match + player match case Some(_) => "" case _ => svg.circle( @@ -199,7 +191,7 @@ object GameMapComponent: svg.cy := s"${y1 + (y2 - y1) / 2}", svg.className := "road-center", svg.r := s"$radius", - onClick --> (_ => onRoadClick()) + onClick --> (_ => gameController.onRoadSpot(road)) ) ) @@ -211,27 +203,24 @@ object GameMapComponent: * @return * the spot graphic */ - private def svgSpot( - coordinate: Coordinates, - withPlayer: Option[String], - withType: Option[String], - onSpotClick: () => Unit - ): Element = - val Coordinates(x, y) = coordinate + private def svgSpot(structure: StructureSpot): InputSourceWithState[Element] = + val Coordinates(x, y) = structure.coordinates.get + val player = assignmentInfoOf(structure).map(_.viewPlayer) + val structureType = assignmentInfoOf(structure).map(_.viewBuildingType) svg.g( svg.circle( svg.cx := s"${x}", svg.cy := s"${y}", svg.r := s"$radius", - svg.className := s"${withPlayer.getOrElse("spot")}", - onClick --> (_ => onSpotClick()) + svg.className := s"${player.getOrElse("spot")}", + onClick --> (_ => gameController.onStructureSpot(structure)) ), svg.text( svg.x := s"${x}", svg.y := s"${y}", svg.className := "spot-text", svg.fontSize := s"$radius", - s"${withType.getOrElse("")}" + s"${structureType.getOrElse("")}" ) ) diff --git a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala index 093ee6ac..d7d36eb5 100644 --- a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala +++ b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala @@ -6,6 +6,8 @@ import scatan.lib.mvc.ScalaJSView import scatan.model.ApplicationState import scatan.model.game.config.ScatanActions import scatan.views.game.GameView +import scatan.views.utils.TypeUtils.{Displayable, DisplayableSource} +import scatan.views.utils.TypeUtils.{reactiveState, gameController} object LeftTabComponent: @@ -13,31 +15,31 @@ object LeftTabComponent: def leftTabCssClass: String = "game-view-left-tab" - def currentPlayerComponent(using view: Signal[ApplicationState]): Element = + def currentPlayerComponent: Displayable[Element] = div( h2( className := "game-view-player", - child.text <-- view + child.text <-- reactiveState .map("Current Player: " + _.game.map(_.turn.player.name).getOrElse("No player")) ), h2( className := "game-view-phase", - child.text <-- view + child.text <-- reactiveState .map("Phase: " + _.game.map(_.gameStatus.phase.toString).getOrElse("No phase")) ), h2( className := "game-view-step", - child.text <-- view + child.text <-- reactiveState .map("Step: " + _.game.map(_.gameStatus.step.toString).getOrElse("No step")) ) ) - def possibleMovesComponent(using view: Signal[ApplicationState]): Element = + def possibleMovesComponent: Displayable[Element] = div( className := "game-view-moves", "Possible moves:", ul( - children <-- view + children <-- reactiveState .map(state => for move <- state.game.map(_.allowedActions.toSeq).getOrElse(Seq.empty) yield li(cls := "game-view-move", move.toViewAction) @@ -45,22 +47,22 @@ object LeftTabComponent: ) ) - def isActionDisabled(using view: Signal[ApplicationState])(action: ScatanActions): Signal[Boolean] = - view.map(_.game.exists(!_.allowedActions.contains(action))) + def isActionDisabled(action: ScatanActions): Displayable[Signal[Boolean]] = + reactiveState.map(_.game.exists(!_.allowedActions.contains(action))) - def buttonsComponent(using view: Signal[ApplicationState])(using controller: GameController): Element = + def buttonsComponent: DisplayableSource[Element] = div( className := "game-view-buttons", button( className := "game-view-button roll-dice-button", "Roll dice", - onClick --> { _ => controller.rollDice() }, + onClick --> { _ => gameController.rollDice() }, disabled <-- isActionDisabled(ScatanActions.RollDice) ), button( className := "game-view-button end-turn-button", "End Turn", - onClick --> { _ => controller.nextTurn() }, + onClick --> { _ => gameController.nextTurn() }, disabled <-- isActionDisabled(ScatanActions.NextTurn) ) ) From b575081205ff4997b027547a283fcef06d068b4a Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Mon, 9 Oct 2023 17:59:37 +0200 Subject: [PATCH 08/19] chore: added new dsl --- .../scala/scatan/lib/game/dsl/GameDSL.scala | 10 +- .../scatan/lib/game/dsl/GameDSLDomain.scala | 36 +++-- .../scatan/lib/game/dsl/PropertiesDSL.scala | 22 ++- .../scatan/lib/game/dsl/ops/GameCtxOps.scala | 13 +- .../scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 21 ++- .../lib/game/dsl/ops/PhasesCtxOps.scala | 8 -- .../lib/game/dsl/ops/PlayersCtxOps.scala | 2 +- .../scatan/lib/game/dsl/ops/StepCtxOps.scala | 13 ++ .../scala/scatan/model/game/ScatanDSL.scala | 134 ++++++++---------- 9 files changed, 149 insertions(+), 110 deletions(-) delete mode 100644 src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala create mode 100644 src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index a4fea4ca..c79d8b27 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -1,12 +1,20 @@ package scatan.lib.game.dsl +import scatan.lib.game.Rules + object GameDSL: import GameDSLDomain.* import PropertiesDSL.{*, given} export ops.GameCtxOps.* export ops.PlayersCtxOps.* - export ops.PhasesCtxOps.* export ops.PhaseCtxOps.* + export ops.StepCtxOps.* + + given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with + def apply(): GameCtx[State, P, S, A, Player] = GameCtx() def Game[State, P, S, A, Player]: PropertyBuilder[GameCtx[State, P, S, A, Player]] = PropertyBuilder() + + extension [State, P, S, A, Player](game: GameCtx[State, P, S, A, Player]) + def rules: Rules[State, P, S, A, Player] = ??? \ No newline at end of file diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala index e454319c..87d13edb 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala @@ -4,18 +4,34 @@ private object GameDSLDomain: import PropertiesDSL.* + given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with + override def apply(): GameCtx[State, P, S, A, Player] = new GameCtx[State, P, S, A, Player] class GameCtx[State, P, S, A, Player]: - val phases: OptionalProperty[PhasesCtx[P, S, A]] = property - val players: OptionalProperty[PlayersCtx] = property - val winner: OptionalProperty[State => Boolean] = property - val initialPhase: OptionalProperty[P] = property + val phases: SequenceProperty[PhaseCtx[State, P, S, A, Player]] = SequenceProperty() + val players: OptionalProperty[PlayersCtx] = OptionalProperty() + val winner: OptionalProperty[State => Option[Player]] = OptionalProperty() + val initialPhase: OptionalProperty[P] = OptionalProperty() + val stateFactory: OptionalProperty[Seq[Player] => State] = OptionalProperty() + given Factory[PlayersCtx] with + override def apply(): PlayersCtx = new PlayersCtx class PlayersCtx: - var allowedSizes: OptionalProperty[Seq[Int]] = property + var allowedSizes: OptionalProperty[Seq[Int]] = OptionalProperty() - class PhasesCtx[P, S, A]: - var phase: SequenceProperty[PhaseCtx[P, S, A]] = property + given [State, Phase, Step, Action, Player]: Factory[PhaseCtx[State, Phase, Step, Action, Player]] with + override def apply(): PhaseCtx[State, Phase, Step, Action, Player] = new PhaseCtx[State, Phase, Step, Action, Player] + class PhaseCtx[State, Phase, Step, Action, Player]: + var phase: OptionalProperty[Phase] = OptionalProperty() + var initialStep: OptionalProperty[Step] = OptionalProperty() + var endingStep: OptionalProperty[Step] = OptionalProperty() + var nextPhase: OptionalProperty[Phase] = OptionalProperty() + var onEnter: OptionalProperty[State => State] = OptionalProperty() + var step: SequenceProperty[StepCtx[Phase, Step, Action]] = SequenceProperty() + var playerIteratorFactory: OptionalProperty[Seq[Player] => Iterator[Player]] = OptionalProperty() - class PhaseCtx[P, S, A]: - var phase: OptionalProperty[P] = property - var when: SequenceProperty[(A, S)] = property + + given [P, S, A]: Factory[StepCtx[P, S, A]] with + override def apply(): StepCtx[P, S, A] = new StepCtx[P, S, A] + class StepCtx[P, S, A]: + var step: OptionalProperty[S] = OptionalProperty() + var when: SequenceProperty[(A, S)] = SequenceProperty() diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index d3cfdbb6..7ed0c447 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -30,25 +30,23 @@ object PropertiesDSL: private type Updater[P] = P ?=> Unit - class PropertyUpdater[P: ClassTag](property: Property[P]): + trait Factory[P]: + def apply(): P + + class PropertyUpdater[P: Factory](property: Property[P]): def apply(updater: Updater[P]): Unit = - val prop = summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] + val prop = summon[Factory[P]].apply() updater(using prop) property(prop) - given [P: ClassTag]: Conversion[Property[P], PropertyUpdater[P]] = PropertyUpdater(_) + given [P: Factory]: Conversion[Property[P], PropertyUpdater[P]] = PropertyUpdater(_) // Builder - class PropertyBuilder[P: ClassTag]: + class PropertyBuilder[P: Factory]: def apply(updater: Updater[P]): P = - val ctx = summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] - updater(using ctx) - ctx - - // Utils - - def property[P <: Property[?]](using ClassTag[P]): P = - summon[ClassTag[P]].runtimeClass.getConstructor().newInstance().asInstanceOf[P] + val prop = summon[Factory[P]].apply() + updater(using prop) + prop type Contexted[Ctx, P] = Ctx ?=> P diff --git a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala index b964a1f6..c5d7e8a2 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala @@ -5,11 +5,18 @@ import scatan.lib.game.dsl.GameDSLDomain.* object GameCtxOps: - def winner[S]: Contexted[GameCtx[S, ?, ?, ?, ?], PropertySetter[S => Boolean]] = + def WinnerFunction[State, Player]: Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[State => Option[Player]]] = ctx ?=> ctx.winner - def phases[P, S, A]: Contexted[GameCtx[?, P, S, A, ?], PropertyUpdater[PhasesCtx[P, S, A]]] = + def Phase[State, Phase, Step, Action, Player]: Contexted[GameCtx[State, Phase, Step, Action, Player], PropertyUpdater[PhaseCtx[State, Phase, Step, Action, Player]]] = ctx ?=> ctx.phases - def players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyUpdater[PlayersCtx]] = + def Players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyUpdater[PlayersCtx]] = ctx ?=> ctx.players + + def InitialPhase[Phase]: Contexted[GameCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = + ctx ?=> ctx.initialPhase + + def StateFactory[State, Player]: Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[Seq[Player] => State]] = + ctx ?=> ctx.stateFactory + diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala index 4ad81c70..5623f2d8 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -4,8 +4,23 @@ import scatan.lib.game.dsl.PropertiesDSL.* import scatan.lib.game.dsl.GameDSLDomain.* object PhaseCtxOps: - def Phase[P, S, A]: Contexted[PhaseCtx[P, S, A], PropertySetter[P]] = + def PhaseType[Phase]: Contexted[PhaseCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = ctx ?=> ctx.phase - def when[P, S, A]: Contexted[PhaseCtx[P, S, A], PropertySetter[(A, S)]] = - ctx ?=> ctx.when \ No newline at end of file + def InitialStep[Step]: Contexted[PhaseCtx[?, ?, Step, ?, ?], PropertySetter[Step]] = + ctx ?=> ctx.initialStep + + def EndingStep[Step]: Contexted[PhaseCtx[?, ?, Step, ?, ?], PropertySetter[Step]] = + ctx ?=> ctx.endingStep + + def NextPhase[Phase]: Contexted[PhaseCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = + ctx ?=> ctx.nextPhase + + def OnEnter[State]: Contexted[PhaseCtx[State, ?, ?, ?, ?], PropertySetter[State => State]] = + ctx ?=> ctx.onEnter + + def Step[Phase, StepType, Action]: Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = + ctx ?=> ctx.step + + def Iterate[Player]: Contexted[PhaseCtx[?, ?, ?, ?, Player], PropertySetter[Seq[Player] => Iterator[Player]]] = + ctx ?=> ctx.playerIteratorFactory \ No newline at end of file diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala deleted file mode 100644 index 2c657247..00000000 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhasesCtxOps.scala +++ /dev/null @@ -1,8 +0,0 @@ -package scatan.lib.game.dsl.ops - -import scatan.lib.game.dsl.PropertiesDSL.* -import scatan.lib.game.dsl.GameDSLDomain.* - -object PhasesCtxOps: - def phase[P, S, A]: Contexted[PhasesCtx[P, S, A], PropertyUpdater[PhaseCtx[P, S, A]]] = - ctx ?=> ctx.phase \ No newline at end of file diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala index 016026a0..2fd77af6 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala @@ -4,5 +4,5 @@ import scatan.lib.game.dsl.PropertiesDSL.* import scatan.lib.game.dsl.GameDSLDomain.* object PlayersCtxOps: - def canBe: Contexted[PlayersCtx, PropertySetter[Seq[Int]]] = + def CanBe: Contexted[PlayersCtx, PropertySetter[Seq[Int]]] = ctx ?=> ctx.allowedSizes diff --git a/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala new file mode 100644 index 00000000..7ddcc9ea --- /dev/null +++ b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala @@ -0,0 +1,13 @@ +package scatan.lib.game.dsl.ops + +import scatan.lib.game.dsl.GameDSLDomain.* +import scatan.lib.game.dsl.PropertiesDSL.* + +object StepCtxOps: + + def StepType[Step]: Contexted[StepCtx[?, Step, ?], PropertySetter[Step]] = + ctx ?=> ctx.step + + def when[Step, Action]: Contexted[StepCtx[?, Step, Action], PropertySetter[(Action, Step)]] = + ctx ?=> ctx.when + diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index db2dfef2..ddcbf2b6 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -1,91 +1,81 @@ package scatan.model.game -import scatan.lib.game.dsl.old.{GameDSL, PhaseDSLOps, PhasesDSLOps} -import scatan.lib.game.dsl.old.PhaseDSLOps.* -import scatan.lib.game.dsl.old.PhasesDSLOps.* -import scatan.lib.game.dsl.old.PlayersDSLOps.* -import scatan.lib.game.dsl.old.TurnDSLOps.* +import scatan.lib.game.Rules import scatan.model.game.config.{ScatanActions, ScatanPhases, ScatanPlayer, ScatanSteps} import scatan.model.game.ops.CardOps.assignResourcesAfterInitialPlacement -import scatan.model.game.ops.ScoreOps.* +import scatan.model.game.ops.ScoreOps.winner import scala.language.postfixOps -object ScatanDSL extends GameDSL: - override type Player = ScatanPlayer - override type State = ScatanState - override type PhaseType = ScatanPhases - override type StepType = ScatanSteps - override type ActionType = ScatanActions +object ScatanDSL: - import scatan.model.game.config.ScatanSteps.* + import scatan.lib.game.dsl.GameDSL.* + def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten + def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => + (seq ++ seq.reverse).iterator - Players { - canBe(3 to 4) - } + private val game = Game[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] { - StartWithStateFactory(ScatanState(_)) - StartWithPhase(ScatanPhases.Setup) + Players { + CanBe := (3 to 4) + } - Winner(_.winner) + WinnerFunction := winner + InitialPhase := ScatanPhases.Setup + StateFactory := ScatanState.apply - Phases { - On(ScatanPhases.Setup) { + Phase { + PhaseType := ScatanPhases.Setup + InitialStep := ScatanSteps.SetupSettlement + EndingStep := ScatanSteps.ChangingTurn + NextPhase := ScatanPhases.Game + Iterate := OnceAndBack - Turn { - Iterate(circularWithBack) - StartIn(SetupSettlement) - CanEndIn(ChangingTurn) - NextPhase(ScatanPhases.Game) + Step { + StepType := ScatanSteps.SetupSettlement + when := ScatanActions.AssignSettlement -> ScatanSteps.SetupRoad + } + Step { + StepType := ScatanSteps.SetupRoad + when := ScatanActions.AssignRoad -> ScatanSteps.ChangingTurn } - - When(SetupSettlement)( - ScatanActions.AssignSettlement -> SetupRoad - ) - - When(SetupRoad)( - ScatanActions.AssignRoad -> ChangingTurn - ) - - When(ChangingTurn)() } - - On(ScatanPhases.Game) { - - OnEnter((state: ScatanState) => state.assignResourcesAfterInitialPlacement.get) - - Turn { - Iterate(normal) - StartIn(Starting) - CanEndIn(ChangingTurn) + Phase { + PhaseType := ScatanPhases.Game + InitialStep := ScatanSteps.Starting + EndingStep := ScatanSteps.ChangingTurn + OnEnter := { (state: ScatanState) => state.assignResourcesAfterInitialPlacement.get } + Iterate := Circular + + Step { + StepType := ScatanSteps.Starting + when := ScatanActions.RollDice -> ScatanSteps.Playing + when := ScatanActions.RollSeven -> ScatanSteps.PlaceRobber + when := ScatanActions.PlayDevelopmentCard -> ScatanSteps.Starting + } + Step { + StepType := ScatanSteps.PlaceRobber + when := ScatanActions.PlaceRobber -> ScatanSteps.StealCard + } + Step { + StepType := ScatanSteps.StealCard + when := ScatanActions.StoleCard -> ScatanSteps.Playing + } + Step { + StepType := ScatanSteps.Playing + when := ScatanActions.BuildSettlement -> ScatanSteps.Playing + when := ScatanActions.BuildRoad -> ScatanSteps.Playing + when := ScatanActions.BuildCity -> ScatanSteps.Playing + when := ScatanActions.BuyDevelopmentCard -> ScatanSteps.Playing + when := ScatanActions.PlayDevelopmentCard -> ScatanSteps.Playing + when := ScatanActions.TradeWithBank -> ScatanSteps.Playing + when := ScatanActions.TradeWithPlayer -> ScatanSteps.Playing + when := ScatanActions.NextTurn -> ScatanSteps.ChangingTurn } - - When(Starting)( - ScatanActions.RollDice -> Playing, - ScatanActions.RollSeven -> PlaceRobber, - ScatanActions.PlayDevelopmentCard -> Starting - ) - - When(PlaceRobber)( - ScatanActions.PlaceRobber -> StealCard - ) - - When(StealCard)( - ScatanActions.StoleCard -> Playing - ) - - When(Playing)( - ScatanActions.BuildSettlement -> Playing, - ScatanActions.BuildRoad -> Playing, - ScatanActions.BuildCity -> Playing, - ScatanActions.BuyDevelopmentCard -> Playing, - ScatanActions.PlayDevelopmentCard -> Playing, - ScatanActions.TradeWithBank -> Playing, - ScatanActions.TradeWithPlayer -> Playing, - ScatanActions.NextTurn -> ChangingTurn - ) - - When(ChangingTurn)() } + } + + val rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = + game.rules From cf692801703ac0704f4c7e424c14b383cb32a0e7 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Mon, 9 Oct 2023 18:18:42 +0200 Subject: [PATCH 09/19] chore: refactor and added foreach to property --- .../scatan/lib/game/dsl/GameDSLDomain.scala | 43 ++++++++++--------- .../scatan/lib/game/dsl/PropertiesDSL.scala | 9 ++-- .../scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 2 +- .../scala/scatan/model/game/ScatanDSL.scala | 14 +++++- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala index 87d13edb..6829e45d 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala @@ -6,32 +6,35 @@ private object GameDSLDomain: given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with override def apply(): GameCtx[State, P, S, A, Player] = new GameCtx[State, P, S, A, Player] - class GameCtx[State, P, S, A, Player]: - val phases: SequenceProperty[PhaseCtx[State, P, S, A, Player]] = SequenceProperty() - val players: OptionalProperty[PlayersCtx] = OptionalProperty() - val winner: OptionalProperty[State => Option[Player]] = OptionalProperty() - val initialPhase: OptionalProperty[P] = OptionalProperty() - val stateFactory: OptionalProperty[Seq[Player] => State] = OptionalProperty() + case class GameCtx[State, P, S, A, Player]( + phases: SequenceProperty[PhaseCtx[State, P, S, A, Player]] = SequenceProperty[PhaseCtx[State, P, S, A, Player]](), + players: OptionalProperty[PlayersCtx] = OptionalProperty[PlayersCtx](), + winner: OptionalProperty[State => Option[Player]] = OptionalProperty[State => Option[Player]](), + initialPhase: OptionalProperty[P] = OptionalProperty[P](), + stateFactory: OptionalProperty[Seq[Player] => State] = OptionalProperty[Seq[Player] => State]() + ) given Factory[PlayersCtx] with override def apply(): PlayersCtx = new PlayersCtx - class PlayersCtx: - var allowedSizes: OptionalProperty[Seq[Int]] = OptionalProperty() + case class PlayersCtx(allowedSizes: OptionalProperty[Seq[Int]] = OptionalProperty[Seq[Int]]()) given [State, Phase, Step, Action, Player]: Factory[PhaseCtx[State, Phase, Step, Action, Player]] with - override def apply(): PhaseCtx[State, Phase, Step, Action, Player] = new PhaseCtx[State, Phase, Step, Action, Player] - class PhaseCtx[State, Phase, Step, Action, Player]: - var phase: OptionalProperty[Phase] = OptionalProperty() - var initialStep: OptionalProperty[Step] = OptionalProperty() - var endingStep: OptionalProperty[Step] = OptionalProperty() - var nextPhase: OptionalProperty[Phase] = OptionalProperty() - var onEnter: OptionalProperty[State => State] = OptionalProperty() - var step: SequenceProperty[StepCtx[Phase, Step, Action]] = SequenceProperty() - var playerIteratorFactory: OptionalProperty[Seq[Player] => Iterator[Player]] = OptionalProperty() + override def apply(): PhaseCtx[State, Phase, Step, Action, Player] = PhaseCtx() + case class PhaseCtx[State, Phase, Step, Action, Player]( + phase: OptionalProperty[Phase] = OptionalProperty[Phase](), + initialStep: OptionalProperty[Step] = OptionalProperty[Step](), + endingStep: OptionalProperty[Step] = OptionalProperty[Step](), + nextPhase: OptionalProperty[Phase] = OptionalProperty[Phase](), + onEnter: OptionalProperty[State => State] = OptionalProperty[State => State](), + steps: SequenceProperty[StepCtx[Phase, Step, Action]] = SequenceProperty[StepCtx[Phase, Step, Action]](), + playerIteratorFactory: OptionalProperty[Seq[Player] => Iterator[Player]] = + OptionalProperty[Seq[Player] => Iterator[Player]]() + ) given [P, S, A]: Factory[StepCtx[P, S, A]] with override def apply(): StepCtx[P, S, A] = new StepCtx[P, S, A] - class StepCtx[P, S, A]: - var step: OptionalProperty[S] = OptionalProperty() - var when: SequenceProperty[(A, S)] = SequenceProperty() + case class StepCtx[P, S, A]( + step: OptionalProperty[S] = OptionalProperty[S](), + when: SequenceProperty[(A, S)] = SequenceProperty[(A, S)]() + ) diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index 7ed0c447..c05ba3a5 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -10,13 +10,14 @@ object PropertiesDSL: sealed trait Property[P]: def apply(newValue: P): Unit - final class OptionalProperty[P] extends Property[P]: - var value: Option[P] = None + final case class OptionalProperty[P](var value: Option[P] = None) extends Property[P]: override def apply(newValue: P): Unit = value = Some(newValue) + def foreach(f: P => Unit): Unit = value.foreach(f) - final class SequenceProperty[P] extends Property[P]: - var value: Seq[P] = Seq.empty + + final case class SequenceProperty[P](var value: Seq[P] = Seq.empty[P]) extends Property[P]: override def apply(newValue: P): Unit = value = value :+ newValue + def foreach(f: P => Unit): Unit = value.foreach(f) // Setter diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala index 5623f2d8..906760dd 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -20,7 +20,7 @@ object PhaseCtxOps: ctx ?=> ctx.onEnter def Step[Phase, StepType, Action]: Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = - ctx ?=> ctx.step + ctx ?=> ctx.steps def Iterate[Player]: Contexted[PhaseCtx[?, ?, ?, ?, Player], PropertySetter[Seq[Player] => Iterator[Player]]] = ctx ?=> ctx.playerIteratorFactory \ No newline at end of file diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index ddcbf2b6..8d3b698a 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -7,8 +7,18 @@ import scatan.model.game.ops.ScoreOps.winner import scala.language.postfixOps + object ScatanDSL: + @main def main(): Unit = + for + phase <- game.phases + _ <- { println(phase); Some(())} + step <- phase.steps + _ <- { println(step); Some(())} + do () + + import scatan.lib.game.dsl.GameDSL.* def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => @@ -77,5 +87,7 @@ object ScatanDSL: } - val rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = + + + def rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = game.rules From d65a08e04f863ceb4e9bfbd4e864cee25dc46a23 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 10:31:01 +0200 Subject: [PATCH 10/19] feat: created rulesOf to map GameCtx to Rules --- src/main/scala/scatan/lib/game/Rules.scala | 10 +-- .../scala/scatan/lib/game/dsl/GameDSL.scala | 64 ++++++++++++++++++- .../scatan/lib/game/dsl/PropertiesDSL.scala | 17 ++++- .../scala/scatan/model/game/ScatanDSL.scala | 16 ++--- 4 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/main/scala/scatan/lib/game/Rules.scala b/src/main/scala/scatan/lib/game/Rules.scala index 2e16a682..8d37b1b4 100644 --- a/src/main/scala/scatan/lib/game/Rules.scala +++ b/src/main/scala/scatan/lib/game/Rules.scala @@ -29,16 +29,16 @@ package scatan.lib.game * player of the game */ final case class Rules[State, P, S, A, Player]( + allowedPlayersSizes: Set[Int], startingStateFactory: Seq[Player] => State, startingPhase: P, startingSteps: Map[P, S], - actions: Map[GameStatus[P, S], Map[A, S]], - allowedPlayersSizes: Set[Int], - phaseTurnIteratorFactories: Map[P, Seq[Player] => Iterator[Player]], - nextPhase: Map[P, P] = Map.empty[P, P], endingSteps: Map[P, S], winnerFunction: State => Option[Player], - initialAction: Map[P, State => State] + initialAction: Map[P, State => State], + phaseTurnIteratorFactories: Map[P, Seq[Player] => Iterator[Player]], + nextPhase: Map[P, P] = Map.empty[P, P], + actions: Map[GameStatus[P, S], Map[A, S]], ): def valid: Boolean = startingStateFactory != null && diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index c79d8b27..7de79b53 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -1,6 +1,6 @@ package scatan.lib.game.dsl -import scatan.lib.game.Rules +import scatan.lib.game.{GameStatus, Rules} object GameDSL: import GameDSLDomain.* @@ -11,10 +11,68 @@ object GameDSL: export ops.PhaseCtxOps.* export ops.StepCtxOps.* - given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with def apply(): GameCtx[State, P, S, A, Player] = GameCtx() def Game[State, P, S, A, Player]: PropertyBuilder[GameCtx[State, P, S, A, Player]] = PropertyBuilder() extension [State, P, S, A, Player](game: GameCtx[State, P, S, A, Player]) - def rules: Rules[State, P, S, A, Player] = ??? \ No newline at end of file + def rules: Rules[State, P, S, A, Player] = + val ruless: Seq[Rules[State, P, S, A, Player]] = for + startingStateFactory <- game.stateFactory + startingPhase <- game.initialPhase + winner <- game.winner + playersCtx <- game.players + allowedSizes <- playersCtx.allowedSizes + yield + val startingSteps: Map[P, S] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + startingStep <- phaseCtx.initialStep + yield phase -> startingStep).toMap + val endingSteps: Map[P, S] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + endingStep <- phaseCtx.endingStep + yield phase -> endingStep).toMap + val initialActions: Map[P, State => State] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + initialAction <- phaseCtx.onEnter + yield phase -> initialAction).toMap + val phaseTurnPlayerIteratorFactories: Map[P, Seq[Player] => Iterator[Player]] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + phaseTurnPlayerIteratorFactory <- phaseCtx.playerIteratorFactory + yield phase -> phaseTurnPlayerIteratorFactory).toMap + val nextPhases: Map[P, P] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + nextPhase <- phaseCtx.nextPhase + yield phase -> nextPhase).toMap + val actions: Map[GameStatus[P, S], Map[A, S]] = (for + phaseCtx <- game.phases + phase <- phaseCtx.phase + stepCtx <- phaseCtx.steps + step <- stepCtx.step + yield + GameStatus(phase, step) -> + (for + when <- stepCtx.when + yield when).toMap + ).toMap + Rules( + allowedPlayersSizes = allowedSizes.toSet, + startingStateFactory = startingStateFactory, + startingPhase = startingPhase, + startingSteps = startingSteps, + endingSteps = endingSteps, + winnerFunction = winner, + initialAction = initialActions, + phaseTurnIteratorFactories = phaseTurnPlayerIteratorFactories, + nextPhase = nextPhases, + actions = actions + ) + require(ruless.sizeIs == 1, "Invalid rules") + ruless.headOption.get + + diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index c05ba3a5..23980e4a 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -12,12 +12,23 @@ object PropertiesDSL: final case class OptionalProperty[P](var value: Option[P] = None) extends Property[P]: override def apply(newValue: P): Unit = value = Some(newValue) - def foreach(f: P => Unit): Unit = value.foreach(f) - final case class SequenceProperty[P](var value: Seq[P] = Seq.empty[P]) extends Property[P]: override def apply(newValue: P): Unit = value = value :+ newValue - def foreach(f: P => Unit): Unit = value.foreach(f) + + class MonadProperty[P <: IterableOnce[T], T](elem: P): + def foreach(f: T => Unit): Unit = elem.iterator.foreach(f) + def map[R](f: T => R): Seq[R] = elem.iterator.map(f).toSeq + def flatMap[R](f: T => Seq[R]): Seq[R] = elem.iterator.flatMap(f).toSeq + + given [P]: Conversion[OptionalProperty[P], MonadProperty[Option[P], P]] with + def apply(optionalProperty: OptionalProperty[P]): MonadProperty[Option[P], P] = + new MonadProperty(optionalProperty.value) + + given [P]: Conversion[SequenceProperty[P], MonadProperty[Seq[P], P]] with + def apply(sequenceProperty: SequenceProperty[P]): MonadProperty[Seq[P], P] = + new MonadProperty(sequenceProperty.value) + // Setter diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index 8d3b698a..d368cb20 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -10,15 +10,6 @@ import scala.language.postfixOps object ScatanDSL: - @main def main(): Unit = - for - phase <- game.phases - _ <- { println(phase); Some(())} - step <- phase.steps - _ <- { println(step); Some(())} - do () - - import scatan.lib.game.dsl.GameDSL.* def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => @@ -87,7 +78,10 @@ object ScatanDSL: } - - def rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = game.rules + + +object Test: + @main def test(): Unit = + ScatanDSL.rules From 5f028d71017d463863986de92349304d139b5680 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 10:31:34 +0200 Subject: [PATCH 11/19] chore: formatted --- src/main/scala/scatan/lib/game/Rules.scala | 2 +- src/main/scala/scatan/lib/game/dsl/GameDSL.scala | 11 +++-------- .../scala/scatan/lib/game/dsl/PropertiesDSL.scala | 1 - .../scala/scatan/lib/game/dsl/ops/GameCtxOps.scala | 8 +++++--- .../scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 5 +++-- .../scala/scatan/lib/game/dsl/ops/StepCtxOps.scala | 1 - src/main/scala/scatan/model/game/ScatanDSL.scala | 5 +---- 7 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/main/scala/scatan/lib/game/Rules.scala b/src/main/scala/scatan/lib/game/Rules.scala index 8d37b1b4..f8accef6 100644 --- a/src/main/scala/scatan/lib/game/Rules.scala +++ b/src/main/scala/scatan/lib/game/Rules.scala @@ -38,7 +38,7 @@ final case class Rules[State, P, S, A, Player]( initialAction: Map[P, State => State], phaseTurnIteratorFactories: Map[P, Seq[Player] => Iterator[Player]], nextPhase: Map[P, P] = Map.empty[P, P], - actions: Map[GameStatus[P, S], Map[A, S]], + actions: Map[GameStatus[P, S], Map[A, S]] ): def valid: Boolean = startingStateFactory != null && diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index 7de79b53..9a13805a 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -54,12 +54,9 @@ object GameDSL: phase <- phaseCtx.phase stepCtx <- phaseCtx.steps step <- stepCtx.step - yield - GameStatus(phase, step) -> - (for - when <- stepCtx.when - yield when).toMap - ).toMap + yield GameStatus(phase, step) -> + (for when <- stepCtx.when + yield when).toMap).toMap Rules( allowedPlayersSizes = allowedSizes.toSet, startingStateFactory = startingStateFactory, @@ -74,5 +71,3 @@ object GameDSL: ) require(ruless.sizeIs == 1, "Invalid rules") ruless.headOption.get - - diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index 23980e4a..a8d3ccc5 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -29,7 +29,6 @@ object PropertiesDSL: def apply(sequenceProperty: SequenceProperty[P]): MonadProperty[Seq[P], P] = new MonadProperty(sequenceProperty.value) - // Setter class PropertySetter[P](property: Property[P]): diff --git a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala index c5d7e8a2..0394eabc 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala @@ -5,10 +5,13 @@ import scatan.lib.game.dsl.GameDSLDomain.* object GameCtxOps: - def WinnerFunction[State, Player]: Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[State => Option[Player]]] = + def WinnerFunction[State, Player] + : Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[State => Option[Player]]] = ctx ?=> ctx.winner - def Phase[State, Phase, Step, Action, Player]: Contexted[GameCtx[State, Phase, Step, Action, Player], PropertyUpdater[PhaseCtx[State, Phase, Step, Action, Player]]] = + def Phase[State, Phase, Step, Action, Player]: Contexted[GameCtx[State, Phase, Step, Action, Player], PropertyUpdater[ + PhaseCtx[State, Phase, Step, Action, Player] + ]] = ctx ?=> ctx.phases def Players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyUpdater[PlayersCtx]] = @@ -19,4 +22,3 @@ object GameCtxOps: def StateFactory[State, Player]: Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[Seq[Player] => State]] = ctx ?=> ctx.stateFactory - diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala index 906760dd..d51c3ff8 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -19,8 +19,9 @@ object PhaseCtxOps: def OnEnter[State]: Contexted[PhaseCtx[State, ?, ?, ?, ?], PropertySetter[State => State]] = ctx ?=> ctx.onEnter - def Step[Phase, StepType, Action]: Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = + def Step[Phase, StepType, Action] + : Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = ctx ?=> ctx.steps def Iterate[Player]: Contexted[PhaseCtx[?, ?, ?, ?, Player], PropertySetter[Seq[Player] => Iterator[Player]]] = - ctx ?=> ctx.playerIteratorFactory \ No newline at end of file + ctx ?=> ctx.playerIteratorFactory diff --git a/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala index 7ddcc9ea..defc1349 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala @@ -10,4 +10,3 @@ object StepCtxOps: def when[Step, Action]: Contexted[StepCtx[?, Step, Action], PropertySetter[(Action, Step)]] = ctx ?=> ctx.when - diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index d368cb20..ca547be2 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -7,13 +7,11 @@ import scatan.model.game.ops.ScoreOps.winner import scala.language.postfixOps - object ScatanDSL: import scatan.lib.game.dsl.GameDSL.* def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten - def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => - (seq ++ seq.reverse).iterator + def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => (seq ++ seq.reverse).iterator private val game = Game[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] { @@ -81,7 +79,6 @@ object ScatanDSL: def rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = game.rules - object Test: @main def test(): Unit = ScatanDSL.rules From 4eb452022ce411676237f84ef386edaa6b6dea1f Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 10:49:38 +0200 Subject: [PATCH 12/19] feat: cleanup --- .../scatan/lib/game/dsl/PropertiesDSL.scala | 1 - .../scatan/lib/game/dsl/old/GameDSL.scala | 25 ------ .../scatan/lib/game/dsl/old/PhaseDSLOps.scala | 38 --------- .../lib/game/dsl/old/PhasesDSLOps.scala | 21 ----- .../lib/game/dsl/old/PlayersDSLOps.scala | 25 ------ .../scatan/lib/game/dsl/old/TurnDSLOps.scala | 36 -------- .../lib/game/dsl/old/TypedGameDSL.scala | 41 --------- .../scatan/lib/game/dsl/ops/GameCtxOps.scala | 2 +- .../scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 7 +- .../lib/game/dsl/ops/PlayersCtxOps.scala | 2 +- .../scatan/model/components/Building.scala | 1 + .../scala/scatan/model/game/ScatanDSL.scala | 10 +-- .../scatan/model/game/ScatanEffects.scala | 12 +-- .../scala/scatan/model/game/ScatanGame.scala | 2 +- .../scala/scatan/model/game/ScatanState.scala | 1 + .../scatan/model/game/ops/AwardOps.scala | 8 +- .../scatan/model/game/ops/BuildingOps.scala | 2 +- .../scala/scatan/model/game/ops/CardOps.scala | 5 +- .../scatan/model/game/ops/TradeOps.scala | 5 +- .../game/components/CardsComponent.scala | 16 ++-- .../game/components/GameMapComponent.scala | 2 +- .../scala/scatan/lib/game/EmptyDomain.scala | 83 +++++++------------ .../scatan/model/ScatanEffectsTest.scala | 8 +- .../scatan/model/game/ops/AwardOpsTest.scala | 5 +- .../model/game/ops/BuildingOpsTest.scala | 8 +- .../model/game/ops/DevCardOpsTest.scala | 10 +-- .../model/game/ops/ResCardOpsTest.scala | 8 +- .../scatan/model/game/ops/RobberOpsTest.scala | 7 +- .../scatan/model/game/ops/ScoreOpsTest.scala | 6 +- .../scatan/model/game/ops/TradeOpsTest.scala | 6 +- 30 files changed, 82 insertions(+), 321 deletions(-) delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala delete mode 100644 src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index a8d3ccc5..1703224d 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -1,7 +1,6 @@ package scatan.lib.game.dsl import scala.annotation.targetName -import scala.reflect.ClassTag object PropertiesDSL: diff --git a/src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala deleted file mode 100644 index b3adb805..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/GameDSL.scala +++ /dev/null @@ -1,25 +0,0 @@ -package scatan.lib.game.dsl.old - -/** A type-safe DSL for defining games. - * - * @tparam Player - * The type of player in the game. - * @tparam State - * The type of the game state. - * @tparam PhaseType - * The type of the phase of the game. - * @tparam StepType - * The type of the step of the game. - * @tparam ActionType - * The type of the action of the game. - */ -trait GameDSL: - type Player - type State - type PhaseType - type StepType - type ActionType - - private val typedDSL = new TypedGameDSL[State, PhaseType, StepType, ActionType, Player] {} - - export typedDSL.{isOver as _, winner as _, *} diff --git a/src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala deleted file mode 100644 index 023355f1..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/PhaseDSLOps.scala +++ /dev/null @@ -1,38 +0,0 @@ -package scatan.lib.game.dsl.old - -import scatan.lib.game.GameStatus -import scatan.lib.game.dsl.old.TurnDSLOps.TurnDSLContext -import scatan.lib.game.ops.RulesOps.* - -/** Operations for defining phases and steps in a game. - */ -object PhaseDSLOps: - case class PhaseDSLContext[State, PhaseType, StepType, ActionType, Player](phase: PhaseType)(using - val dsl: TypedGameDSL[State, PhaseType, StepType, ActionType, Player] - ): - def addStepToPhase(phase: PhaseType, step: StepType, actions: Map[ActionType, StepType]): Unit = - val status = GameStatus(phase, step) - dsl.rules = dsl.rules.withActions(status, actions) - def addOnEnterToPhase(phase: PhaseType, onEnter: State => State): Unit = - dsl.rules = dsl.rules.withOnEnter(phase, onEnter) - - def OnEnter[State, PhaseType, StepType, ActionType, Player]( - onEnter: State => State - )(using - phaseDSLContext: PhaseDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - phaseDSLContext.addOnEnterToPhase(phaseDSLContext.phase, onEnter) - - def Turn[State, PhaseType, StepType, ActionType, Player]( - init: TurnDSLContext[State, PhaseType, StepType, ActionType, Player] ?=> Unit - )(using - phaseDSLContext: PhaseDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - given TurnDSLContext[State, PhaseType, StepType, ActionType, Player] = - TurnDSLContext[State, PhaseType, StepType, ActionType, Player](phaseDSLContext.phase)(using phaseDSLContext.dsl) - init - - def When[State, PhaseType, StepType, ActionType, Player](step: StepType)(init: (ActionType, StepType)*)(using - phaseDSLContext: PhaseDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - phaseDSLContext.addStepToPhase(phaseDSLContext.phase, step, init.toMap) diff --git a/src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala deleted file mode 100644 index a3d58595..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/PhasesDSLOps.scala +++ /dev/null @@ -1,21 +0,0 @@ -package scatan.lib.game.dsl.old - -import scatan.lib.game.dsl.old.PhaseDSLOps.PhaseDSLContext - -/** Operations for defining phases in a game. - */ -object PhasesDSLOps: - case class PhasesDSLContext[State, PhaseType, StepType, ActionType, Player]()(using - val dsl: TypedGameDSL[State, PhaseType, StepType, ActionType, Player] - ) - - def On[State, PhaseType, StepType, ActionType, Player](using - phasesDSLContext: PhasesDSLContext[State, PhaseType, StepType, ActionType, Player] - )( - phase: PhaseType - )( - init: PhaseDSLContext[State, PhaseType, StepType, ActionType, Player] ?=> Unit - ): Unit = - given context: PhaseDSLContext[State, PhaseType, StepType, ActionType, Player] = - PhaseDSLContext[State, PhaseType, StepType, ActionType, Player](phase)(using phasesDSLContext.dsl) - init(using context) diff --git a/src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala deleted file mode 100644 index fc9b311c..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/PlayersDSLOps.scala +++ /dev/null @@ -1,25 +0,0 @@ -package scatan.lib.game.dsl.old - -import scatan.lib.game.ops.RulesOps.* - -/** Operations for the players DSL. - */ -object PlayersDSLOps: - case class PlayersDSLContext[State, PhaseType, StepType, ActionType, Player]()(using - val dsl: TypedGameDSL[State, PhaseType, StepType, ActionType, Player] - ) - - def canBe[State, PhaseType, StepType, ActionType, Player](sizes: Set[Int])(using - playersDSLContext: PlayersDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - playersDSLContext.dsl.rules = playersDSLContext.dsl.rules.withAllowedPlayersSizes(sizes) - - def canBe[State, PhaseType, StepType, ActionType, Player](sizes: Int*)(using - playersDSLContext: PlayersDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - canBe(sizes.toSet) - - def canBe[State, PhaseType, StepType, ActionType, Player](sizes: Range)(using - playersDSLContext: PlayersDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - canBe(sizes.toSet) diff --git a/src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala b/src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala deleted file mode 100644 index 47f4a933..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/TurnDSLOps.scala +++ /dev/null @@ -1,36 +0,0 @@ -package scatan.lib.game.dsl.old - -import scatan.lib.game.ops.RulesOps.* - -/** Operations for defining the rules of a turn-based game. - */ -object TurnDSLOps: - case class TurnDSLContext[State, PhaseType, StepType, ActionType, Player](phase: PhaseType)(using - val dsl: TypedGameDSL[State, PhaseType, StepType, ActionType, Player] - ) - - def StartIn[State, PhaseType, StepType, ActionType, Player](step: StepType)(using - turnDSLContext: TurnDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - turnDSLContext.dsl.rules = turnDSLContext.dsl.rules.withStartingStep(turnDSLContext.phase, step) - - def CanEndIn[State, PhaseType, StepType, ActionType, Player]( - step: StepType - )(using turnDSLContext: TurnDSLContext[State, PhaseType, StepType, ActionType, Player]): Unit = - turnDSLContext.dsl.rules = turnDSLContext.dsl.rules.withEndingStep(turnDSLContext.phase, step) - - def Iterate[State, PhaseType, StepType, ActionType, Player](factory: Seq[Player] => Iterator[Player])(using - turnDSLContext: TurnDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - turnDSLContext.dsl.rules = turnDSLContext.dsl.rules.withPhaseTurnIteratorFactory(turnDSLContext.phase, factory) - - def NextPhase[State, PhaseType, StepType, ActionType, Player](phase: PhaseType)(using - turnDSLContext: TurnDSLContext[State, PhaseType, StepType, ActionType, Player] - ): Unit = - turnDSLContext.dsl.rules = turnDSLContext.dsl.rules.withNextPhase(turnDSLContext.phase, phase) - - def once[Player] = (players: Seq[Player]) => players.iterator - def normal[Player] = (players: Seq[Player]) => Iterator.continually(players).flatten - def reverse[Player] = (players: Seq[Player]) => Iterator.continually(players.reverse).flatten - def random[Player] = (players: Seq[Player]) => Iterator.continually(scala.util.Random.shuffle(players)).flatten - def circularWithBack[Player] = (players: Seq[Player]) => (players ++ players.reverse).iterator diff --git a/src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala b/src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala deleted file mode 100644 index 7e66308c..00000000 --- a/src/main/scala/scatan/lib/game/dsl/old/TypedGameDSL.scala +++ /dev/null @@ -1,41 +0,0 @@ -package scatan.lib.game.dsl.old - -import scatan.lib.game.Rules -import scatan.lib.game.dsl.old.PhasesDSLOps.PhasesDSLContext -import scatan.lib.game.dsl.old.PlayersDSLOps.PlayersDSLContext -import scatan.lib.game.ops.RulesOps.* - -/** A DSL for defining a game. - * - * @tparam State - * The type of the game state. - * @tparam PhaseType - * The type of the game phase. - * @tparam StepType - * The type of the game step. - * @tparam ActionType - * The type of the game action. - * @tparam Player - * The type of the player. - */ -trait TypedGameDSL[State, PhaseType, StepType, ActionType, Player]: - var rules: Rules[State, PhaseType, StepType, ActionType, Player] = Rules.empty - given TypedGameDSL[State, PhaseType, StepType, ActionType, Player] = this - - def Players(init: PlayersDSLContext[State, PhaseType, StepType, ActionType, Player] ?=> Unit): Unit = - given PlayersDSLContext[State, PhaseType, StepType, ActionType, Player] = PlayersDSLContext() - init - - def Phases(init: PhasesDSLContext[State, PhaseType, StepType, ActionType, Player] ?=> Unit): Unit = - given PhasesDSLContext[State, PhaseType, StepType, ActionType, Player] = - PhasesDSLContext[State, PhaseType, StepType, ActionType, Player]() - init - - def Winner(winner: State => Option[Player]): Unit = - rules = rules.withWinnerFunction(winner) - - def StartWithPhase(phase: PhaseType): Unit = - rules = rules.withStartingPhase(phase) - - def StartWithStateFactory(stateFactory: Seq[Player] => State): Unit = - rules = rules.withStartingStateFactory(stateFactory) diff --git a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala index 0394eabc..bb0e4622 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala @@ -1,7 +1,7 @@ package scatan.lib.game.dsl.ops -import scatan.lib.game.dsl.PropertiesDSL.* import scatan.lib.game.dsl.GameDSLDomain.* +import scatan.lib.game.dsl.PropertiesDSL.* object GameCtxOps: diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala index d51c3ff8..88283f20 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -1,7 +1,7 @@ package scatan.lib.game.dsl.ops -import scatan.lib.game.dsl.PropertiesDSL.* import scatan.lib.game.dsl.GameDSLDomain.* +import scatan.lib.game.dsl.PropertiesDSL.* object PhaseCtxOps: def PhaseType[Phase]: Contexted[PhaseCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = @@ -23,5 +23,10 @@ object PhaseCtxOps: : Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = ctx ?=> ctx.steps + object Iterations: + def Once[X]: Seq[X] => Iterator[X] = _.iterator + def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten + def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => (seq ++ seq.reverse).iterator + def Iterate[Player]: Contexted[PhaseCtx[?, ?, ?, ?, Player], PropertySetter[Seq[Player] => Iterator[Player]]] = ctx ?=> ctx.playerIteratorFactory diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala index 2fd77af6..583154f9 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala @@ -1,7 +1,7 @@ package scatan.lib.game.dsl.ops -import scatan.lib.game.dsl.PropertiesDSL.* import scatan.lib.game.dsl.GameDSLDomain.* +import scatan.lib.game.dsl.PropertiesDSL.* object PlayersCtxOps: def CanBe: Contexted[PlayersCtx, PropertySetter[Seq[Int]]] = diff --git a/src/main/scala/scatan/model/components/Building.scala b/src/main/scala/scatan/model/components/Building.scala index 59212ffa..483bbdfc 100644 --- a/src/main/scala/scatan/model/components/Building.scala +++ b/src/main/scala/scatan/model/components/Building.scala @@ -5,6 +5,7 @@ import scatan.model.components.BuildingType.* import scatan.model.components.ResourceType.* import scatan.model.game.config.ScatanPlayer import scatan.model.map.{RoadSpot, Spot, StructureSpot} + import scala.collection.immutable.ListMap type ResourceCost = (ResourceType, Int) diff --git a/src/main/scala/scatan/model/game/ScatanDSL.scala b/src/main/scala/scatan/model/game/ScatanDSL.scala index ca547be2..0f7798ea 100644 --- a/src/main/scala/scatan/model/game/ScatanDSL.scala +++ b/src/main/scala/scatan/model/game/ScatanDSL.scala @@ -10,8 +10,6 @@ import scala.language.postfixOps object ScatanDSL: import scatan.lib.game.dsl.GameDSL.* - def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten - def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => (seq ++ seq.reverse).iterator private val game = Game[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] { @@ -28,7 +26,7 @@ object ScatanDSL: InitialStep := ScatanSteps.SetupSettlement EndingStep := ScatanSteps.ChangingTurn NextPhase := ScatanPhases.Game - Iterate := OnceAndBack + Iterate := Iterations.OnceAndBack Step { StepType := ScatanSteps.SetupSettlement @@ -45,7 +43,7 @@ object ScatanDSL: InitialStep := ScatanSteps.Starting EndingStep := ScatanSteps.ChangingTurn OnEnter := { (state: ScatanState) => state.assignResourcesAfterInitialPlacement.get } - Iterate := Circular + Iterate := Iterations.Circular Step { StepType := ScatanSteps.Starting @@ -78,7 +76,3 @@ object ScatanDSL: def rules: Rules[ScatanState, ScatanPhases, ScatanSteps, ScatanActions, ScatanPlayer] = game.rules - -object Test: - @main def test(): Unit = - ScatanDSL.rules diff --git a/src/main/scala/scatan/model/game/ScatanEffects.scala b/src/main/scala/scatan/model/game/ScatanEffects.scala index 3214b046..871c0204 100644 --- a/src/main/scala/scatan/model/game/ScatanEffects.scala +++ b/src/main/scala/scatan/model/game/ScatanEffects.scala @@ -1,18 +1,14 @@ package scatan.model.game import scatan.lib.game.ops.Effect -import scatan.model.components.BuildingType +import scatan.model.components.{BuildingType, ResourceCard} import scatan.model.game.config.ScatanActions.* import scatan.model.game.config.ScatanPlayer import scatan.model.game.ops.BuildingOps.{assignBuilding, build} -import scatan.model.game.ops.CardOps.buyDevelopmentCard -import scatan.model.map.{Hexagon, RoadSpot, StructureSpot} -import scatan.model.components.ResourceCard -import scatan.model.game.ops.CardOps.removeResourceCard -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.CardOps.{assignResourceCard, assignResourcesFromNumber, buyDevelopmentCard, removeResourceCard} import scatan.model.game.ops.RobberOps.moveRobber +import scatan.model.game.ops.TradeOps.tradeWithPlayer +import scatan.model.map.{Hexagon, RoadSpot, StructureSpot} object ScatanEffects: diff --git a/src/main/scala/scatan/model/game/ScatanGame.scala b/src/main/scala/scatan/model/game/ScatanGame.scala index 5245d5fb..2fb66336 100644 --- a/src/main/scala/scatan/model/game/ScatanGame.scala +++ b/src/main/scala/scatan/model/game/ScatanGame.scala @@ -5,13 +5,13 @@ import scatan.lib.game.ops.GamePlayOps.{allowedActions, play} import scatan.lib.game.ops.GameTurnOps.nextTurn import scatan.lib.game.ops.GameWinOps.{isOver, winner} import scatan.lib.game.{Game, GameStatus, Turn} +import scatan.model.components.ResourceCard import scatan.model.game.ScatanEffects.* import scatan.model.game.config.ScatanActions.* import scatan.model.game.config.{ScatanActions, ScatanPhases, ScatanPlayer, ScatanSteps} import scatan.model.map.{Hexagon, RoadSpot, StructureSpot} import scala.util.Random -import scatan.model.components.ResourceCard /** The status of a game of Scatan. It contains all the data without any possible action. * @param game diff --git a/src/main/scala/scatan/model/game/ScatanState.scala b/src/main/scala/scatan/model/game/ScatanState.scala index a988e3a3..4fde7ebb 100644 --- a/src/main/scala/scatan/model/game/ScatanState.scala +++ b/src/main/scala/scatan/model/game/ScatanState.scala @@ -6,6 +6,7 @@ import scatan.model.components.AssignedBuildingsAdapter.asPlayerMap import scatan.model.components.DevelopmentType.Knight import scatan.model.game.config.ScatanPlayer import scatan.model.map.* + import scala.collection.mutable.ListMap /** Represents the state of a Scatan game. diff --git a/src/main/scala/scatan/model/game/ops/AwardOps.scala b/src/main/scala/scatan/model/game/ops/AwardOps.scala index aa42c1c0..eaade073 100644 --- a/src/main/scala/scatan/model/game/ops/AwardOps.scala +++ b/src/main/scala/scatan/model/game/ops/AwardOps.scala @@ -1,13 +1,9 @@ package scatan.model.game.ops +import scatan.model.components.AssignedBuildingsAdapter.asPlayerMap +import scatan.model.components.* import scatan.model.game.ScatanState import scatan.model.game.config.ScatanPlayer -import scatan.model.components.Awards -import scatan.model.components.Award -import scatan.model.components.AwardType -import scatan.model.components.DevelopmentType -import scatan.model.components.AssignedBuildingsAdapter.asPlayerMap -import scatan.model.components.BuildingType object AwardOps: diff --git a/src/main/scala/scatan/model/game/ops/BuildingOps.scala b/src/main/scala/scatan/model/game/ops/BuildingOps.scala index 45dea02d..2940686d 100644 --- a/src/main/scala/scatan/model/game/ops/BuildingOps.scala +++ b/src/main/scala/scatan/model/game/ops/BuildingOps.scala @@ -3,8 +3,8 @@ package scatan.model.game.ops import scatan.model.components.{AssignedBuildings, AssignmentInfo, BuildingType, Cost} import scatan.model.game.ScatanState import scatan.model.game.config.ScatanPlayer -import scatan.model.game.ops.EmptySpotsOps.{emptyRoadSpot, emptyStructureSpot} import scatan.model.game.ops.AwardOps.* +import scatan.model.game.ops.EmptySpotsOps.{emptyRoadSpot, emptyStructureSpot} import scatan.model.map.{RoadSpot, Spot, StructureSpot} object BuildingOps: diff --git a/src/main/scala/scatan/model/game/ops/CardOps.scala b/src/main/scala/scatan/model/game/ops/CardOps.scala index d19b78b5..e3d76c31 100644 --- a/src/main/scala/scatan/model/game/ops/CardOps.scala +++ b/src/main/scala/scatan/model/game/ops/CardOps.scala @@ -1,11 +1,10 @@ package scatan.model.game.ops -import scatan.model.components.{BuildingType, DevelopmentCard, ResourceCard, ResourceType} +import scatan.model.components.* import scatan.model.game.ScatanState import scatan.model.game.config.ScatanPlayer -import scatan.model.map.{Hexagon, RoadSpot, StructureSpot, TileContent} import scatan.model.game.ops.AwardOps.* -import scatan.model.components.AssignedBuildings +import scatan.model.map.{Hexagon, RoadSpot, StructureSpot, TileContent} object CardOps: diff --git a/src/main/scala/scatan/model/game/ops/TradeOps.scala b/src/main/scala/scatan/model/game/ops/TradeOps.scala index 5b97a932..754a2438 100644 --- a/src/main/scala/scatan/model/game/ops/TradeOps.scala +++ b/src/main/scala/scatan/model/game/ops/TradeOps.scala @@ -1,10 +1,9 @@ package scatan.model.game.ops +import scatan.model.components.ResourceCard import scatan.model.game.ScatanState import scatan.model.game.config.ScatanPlayer -import scatan.model.components.ResourceCard -import scatan.model.game.ops.CardOps.removeResourceCard -import scatan.model.game.ops.CardOps.assignResourceCard +import scatan.model.game.ops.CardOps.{assignResourceCard, removeResourceCard} object TradeOps: extension (state: ScatanState) diff --git a/src/main/scala/scatan/views/game/components/CardsComponent.scala b/src/main/scala/scatan/views/game/components/CardsComponent.scala index 2be73c09..ea9221f1 100644 --- a/src/main/scala/scatan/views/game/components/CardsComponent.scala +++ b/src/main/scala/scatan/views/game/components/CardsComponent.scala @@ -1,18 +1,14 @@ package scatan.views.game.components import com.raquo.laminar.api.L.* -import scatan.model.components.ResourceType -import scatan.model.components.ResourceType.* +import scatan.controllers.game.GameController import scatan.model.ApplicationState -import scatan.model.components.ResourceCard -import scatan.model.game.config.ScatanPlayer -import scatan.model.game.ScatanState -import scatan.model.components.DevelopmentType import scatan.model.components.DevelopmentType.* -import scatan.views.game.components.CardContextMap.cardImages -import scatan.views.game.components.CardContextMap.countCardOf -import scatan.views.game.components.CardContextMap.CardType -import scatan.controllers.game.GameController +import scatan.model.components.{DevelopmentType, ResourceCard, ResourceType} +import scatan.model.components.ResourceType.* +import scatan.model.game.ScatanState +import scatan.model.game.config.ScatanPlayer +import scatan.views.game.components.CardContextMap.{CardType, cardImages, countCardOf} object CardContextMap: extension (state: ScatanState) diff --git a/src/main/scala/scatan/views/game/components/GameMapComponent.scala b/src/main/scala/scatan/views/game/components/GameMapComponent.scala index 8270370f..3e8570a2 100644 --- a/src/main/scala/scatan/views/game/components/GameMapComponent.scala +++ b/src/main/scala/scatan/views/game/components/GameMapComponent.scala @@ -11,7 +11,7 @@ import scatan.model.map.{Hexagon, StructureSpot, TileContent} import scatan.model.{ApplicationState, GameMap} import scatan.views.Coordinates import scatan.views.Coordinates.* -import scatan.views.game.components.ContextMap.{viewBuildingType, viewPlayer, toImgId} +import scatan.views.game.components.ContextMap.{toImgId, viewBuildingType, viewPlayer} object ContextMap: diff --git a/src/test/scala/scatan/lib/game/EmptyDomain.scala b/src/test/scala/scatan/lib/game/EmptyDomain.scala index 393011a9..6469884e 100644 --- a/src/test/scala/scatan/lib/game/EmptyDomain.scala +++ b/src/test/scala/scatan/lib/game/EmptyDomain.scala @@ -1,13 +1,7 @@ package scatan.lib.game -import scatan.lib.game.EmptyDomain.MyPhases -import scatan.lib.game.EmptyDomain.MyPhases.* -import scatan.lib.game.EmptyDomain.Steps.Initial -import scatan.lib.game.dsl.old.{GameDSL, PhaseDSLOps, TurnDSLOps} -import scatan.lib.game.dsl.old.PhaseDSLOps.{Turn, When} -import scatan.lib.game.dsl.old.PhasesDSLOps.On -import scatan.lib.game.dsl.old.PlayersDSLOps.canBe -import scatan.lib.game.dsl.old.TurnDSLOps.* +import scatan.lib.game.dsl.GameDSL +import scatan.lib.game.dsl.GameDSL.rules import scatan.lib.game.ops.Effect object EmptyDomain: @@ -27,54 +21,41 @@ object EmptyDomain: def NextTurnEffect: Effect[Actions.NextTurn.type, State] = (state: State) => Some(state) - object EmptyGameDSL extends GameDSL: - override type State = EmptyDomain.State - override type PhaseType = EmptyDomain.MyPhases - override type StepType = EmptyDomain.Steps - override type ActionType = EmptyDomain.Actions - override type Player = EmptyDomain.Player - - import Steps.* - - import scala.language.postfixOps + def rules = game.rules + import GameDSL.* + private val game = Game[State, MyPhases, Steps, Actions, Player] { Players { - canBe(2 to 4) + CanBe := 2 to 4 } - - StartWithStateFactory((_) => State()) - StartWithPhase(Game) - Winner(_ => None) - - Phases { - On(Game) { - - Turn { - Iterate(once) - StartIn(Initial) - CanEndIn(ChangingTurn) - NextPhase(GameOver) - } - - When(Steps.Initial)( - Actions.StartGame -> Steps.Initial, - Actions.NextTurn -> Steps.ChangingTurn - ) - - When(Steps.ChangingTurn)() - + StateFactory := (_ => State()) + InitialPhase := MyPhases.Game + WinnerFunction := (_ => None) + + Phase { + PhaseType := MyPhases.Game + InitialStep := Steps.Initial + EndingStep := Steps.ChangingTurn + NextPhase := MyPhases.GameOver + Iterate := Iterations.Once + + Step { + StepType := Steps.Initial + when := Actions.StartGame -> Steps.Initial + when := Actions.NextTurn -> Steps.ChangingTurn } - On(GameOver) { - - Turn { - Iterate(once) - StartIn(Initial) - CanEndIn(ChangingTurn) - NextPhase(GameOver) - } - + Step { + StepType := Steps.ChangingTurn } + } - def rules = EmptyGameDSL.rules + Phase { + PhaseType := MyPhases.GameOver + InitialStep := Steps.Initial + EndingStep := Steps.ChangingTurn + NextPhase := MyPhases.GameOver + Iterate := Iterations.Once + } + } diff --git a/src/test/scala/scatan/model/ScatanEffectsTest.scala b/src/test/scala/scatan/model/ScatanEffectsTest.scala index c431e9f7..36ed67c4 100644 --- a/src/test/scala/scatan/model/ScatanEffectsTest.scala +++ b/src/test/scala/scatan/model/ScatanEffectsTest.scala @@ -1,13 +1,11 @@ package scatan.model import scatan.BaseTest -import scatan.model.game.config.ScatanPlayer -import scatan.model.game.ScatanState -import scatan.model.components.ResourceCard -import scatan.model.components.ResourceType +import scatan.model.components.{ResourceCard, ResourceType} import scatan.model.game.ScatanEffects.{NextTurnEffect, PlaceRobberEffect, RollEffect, TradeWithPlayerEffect} +import scatan.model.game.{ScatanGame, ScatanState} +import scatan.model.game.config.ScatanPlayer import scatan.model.game.ops.CardOps.assignResourceCard -import scatan.model.game.ScatanGame class ScatanEffectsTest extends BaseTest: diff --git a/src/test/scala/scatan/model/game/ops/AwardOpsTest.scala b/src/test/scala/scatan/model/game/ops/AwardOpsTest.scala index f37bd2ae..a05a7428 100644 --- a/src/test/scala/scatan/model/game/ops/AwardOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/AwardOpsTest.scala @@ -1,12 +1,11 @@ package scatan.model.game.ops import scatan.model.components.* +import scatan.model.game.{BaseScatanStateTest, ScatanState} +import scatan.model.game.ops.AwardOps.* import scatan.model.game.ops.BuildingOps.assignBuilding import scatan.model.game.ops.CardOps.assignDevelopmentCard import scatan.model.game.ops.EmptySpotsOps.{emptyRoadSpot, emptyStructureSpot} -import scatan.model.game.ops.AwardOps.* -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ScatanState class AwardOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/BuildingOpsTest.scala b/src/test/scala/scatan/model/game/ops/BuildingOpsTest.scala index 6235b28f..4d686e84 100644 --- a/src/test/scala/scatan/model/game/ops/BuildingOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/BuildingOpsTest.scala @@ -1,13 +1,11 @@ package scatan.model.game.ops import scatan.model.components.* +import scatan.model.game.{BaseScatanStateTest, ScatanState} import scatan.model.game.ops.BuildingOps.{assignBuilding, build} import scatan.model.game.ops.CardOps.assignResourceCard -import scatan.model.game.ops.EmptySpotsOps.{emptyStructureSpot, emptyRoadSpot} -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ScatanState -import scatan.model.map.StructureSpot -import scatan.model.map.RoadSpot +import scatan.model.game.ops.EmptySpotsOps.{emptyRoadSpot, emptyStructureSpot} +import scatan.model.map.{RoadSpot, StructureSpot} class BuildingOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala b/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala index 153ba171..37d7c2fa 100644 --- a/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala @@ -2,14 +2,8 @@ package scatan.model.game.ops import scatan.lib.game.Game import scatan.model.components.{DevelopmentCard, DevelopmentType, ResourceCard, ResourceType} -import scatan.model.game.ScatanState -import scatan.model.game.ops.CardOps.{ - assignDevelopmentCard, - assignResourceCard, - buyDevelopmentCard, - consumeDevelopmentCard -} -import scatan.model.game.BaseScatanStateTest +import scatan.model.game.{BaseScatanStateTest, ScatanState} +import scatan.model.game.ops.CardOps.{assignDevelopmentCard, assignResourceCard, buyDevelopmentCard, consumeDevelopmentCard} class DevCardOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala b/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala index a02fcd6a..ce297e03 100644 --- a/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala @@ -1,17 +1,13 @@ package scatan.model.game.ops import scatan.model.components.{BuildingType, ResourceCard, ResourceCards, ResourceType} +import scatan.model.game.{BaseScatanStateTest, ScatanState} import scatan.model.game.ops.BuildingOps.assignBuilding -import scatan.model.game.ops.CardOps.assignResourcesFromNumber +import scatan.model.game.ops.CardOps.{assignResourceCard, assignResourcesAfterInitialPlacement, assignResourcesFromNumber, removeResourceCard} import scatan.model.game.ops.EmptySpotsOps.emptyStructureSpot import scatan.model.map.HexagonInMap.layer import scatan.model.map.{RoadSpot, Spot, StructureSpot} import scatan.utils.UnorderedTriple -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ScatanState -import scatan.model.game.ops.CardOps.assignResourceCard -import scatan.model.game.ops.CardOps.removeResourceCard -import scatan.model.game.ops.CardOps.assignResourcesAfterInitialPlacement class ResCardOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/RobberOpsTest.scala b/src/test/scala/scatan/model/game/ops/RobberOpsTest.scala index 9b05d434..9b53ac66 100644 --- a/src/test/scala/scatan/model/game/ops/RobberOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/RobberOpsTest.scala @@ -1,10 +1,9 @@ package scatan.model.game.ops -import scatan.model.map.Hexagon -import scatan.model.game.ops.RobberOps.moveRobber -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ScatanState import scatan.model.components.UnproductiveTerrain.Desert +import scatan.model.game.{BaseScatanStateTest, ScatanState} +import scatan.model.game.ops.RobberOps.moveRobber +import scatan.model.map.Hexagon class RobberOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/ScoreOpsTest.scala b/src/test/scala/scatan/model/game/ops/ScoreOpsTest.scala index b2fed425..a07bd69d 100644 --- a/src/test/scala/scatan/model/game/ops/ScoreOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/ScoreOpsTest.scala @@ -1,12 +1,10 @@ package scatan.model.game.ops import scatan.model.components.{AssignedBuildings, BuildingType, Scores} -import scatan.model.game.ScatanState +import scatan.model.game.{BaseScatanStateTest, ScatanState} import scatan.model.game.ops.BuildingOps.assignBuilding -import scatan.model.game.ops.EmptySpotsOps.emptyStructureSpot +import scatan.model.game.ops.EmptySpotsOps.{emptyRoadSpot, emptyStructureSpot} import scatan.model.game.ops.ScoreOps.* -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ops.EmptySpotsOps.emptyRoadSpot class ScoreOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala b/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala index ee3e8789..bbe00ea0 100644 --- a/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/TradeOpsTest.scala @@ -1,10 +1,8 @@ package scatan.model.game.ops -import scatan.model.game.BaseScatanStateTest -import scatan.model.game.ScatanState +import scatan.model.components.{ResourceCard, ResourceType} +import scatan.model.game.{BaseScatanStateTest, ScatanState} import scatan.model.game.ops.CardOps.assignResourceCard -import scatan.model.components.ResourceCard -import scatan.model.components.ResourceType import scatan.model.game.ops.TradeOps.tradeWithPlayer class TradeOpsTest extends BaseScatanStateTest: From b7f7e996dc8353d5f0a9b4908abc104a1077dd9d Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 10:52:51 +0200 Subject: [PATCH 13/19] chore: formatted --- src/main/scala/scatan/model/game/ScatanEffects.scala | 7 ++++++- src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala | 7 ++++++- src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/scala/scatan/model/game/ScatanEffects.scala b/src/main/scala/scatan/model/game/ScatanEffects.scala index 871c0204..7cda9630 100644 --- a/src/main/scala/scatan/model/game/ScatanEffects.scala +++ b/src/main/scala/scatan/model/game/ScatanEffects.scala @@ -5,7 +5,12 @@ import scatan.model.components.{BuildingType, ResourceCard} import scatan.model.game.config.ScatanActions.* import scatan.model.game.config.ScatanPlayer import scatan.model.game.ops.BuildingOps.{assignBuilding, build} -import scatan.model.game.ops.CardOps.{assignResourceCard, assignResourcesFromNumber, buyDevelopmentCard, removeResourceCard} +import scatan.model.game.ops.CardOps.{ + assignResourceCard, + assignResourcesFromNumber, + buyDevelopmentCard, + removeResourceCard +} import scatan.model.game.ops.RobberOps.moveRobber import scatan.model.game.ops.TradeOps.tradeWithPlayer import scatan.model.map.{Hexagon, RoadSpot, StructureSpot} diff --git a/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala b/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala index 37d7c2fa..cf2d8407 100644 --- a/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/DevCardOpsTest.scala @@ -3,7 +3,12 @@ package scatan.model.game.ops import scatan.lib.game.Game import scatan.model.components.{DevelopmentCard, DevelopmentType, ResourceCard, ResourceType} import scatan.model.game.{BaseScatanStateTest, ScatanState} -import scatan.model.game.ops.CardOps.{assignDevelopmentCard, assignResourceCard, buyDevelopmentCard, consumeDevelopmentCard} +import scatan.model.game.ops.CardOps.{ + assignDevelopmentCard, + assignResourceCard, + buyDevelopmentCard, + consumeDevelopmentCard +} class DevCardOpsTest extends BaseScatanStateTest: diff --git a/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala b/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala index ce297e03..1b720f20 100644 --- a/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala +++ b/src/test/scala/scatan/model/game/ops/ResCardOpsTest.scala @@ -3,7 +3,12 @@ package scatan.model.game.ops import scatan.model.components.{BuildingType, ResourceCard, ResourceCards, ResourceType} import scatan.model.game.{BaseScatanStateTest, ScatanState} import scatan.model.game.ops.BuildingOps.assignBuilding -import scatan.model.game.ops.CardOps.{assignResourceCard, assignResourcesAfterInitialPlacement, assignResourcesFromNumber, removeResourceCard} +import scatan.model.game.ops.CardOps.{ + assignResourceCard, + assignResourcesAfterInitialPlacement, + assignResourcesFromNumber, + removeResourceCard +} import scatan.model.game.ops.EmptySpotsOps.emptyStructureSpot import scatan.model.map.HexagonInMap.layer import scatan.model.map.{RoadSpot, Spot, StructureSpot} From 1367e7def79d4d366f831c2a766796c0a00f2b93 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 11:01:57 +0200 Subject: [PATCH 14/19] chore: removed unused factory --- src/main/scala/scatan/lib/game/dsl/GameDSL.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index 9a13805a..bc7025f7 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -11,8 +11,6 @@ object GameDSL: export ops.PhaseCtxOps.* export ops.StepCtxOps.* - given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with - def apply(): GameCtx[State, P, S, A, Player] = GameCtx() def Game[State, P, S, A, Player]: PropertyBuilder[GameCtx[State, P, S, A, Player]] = PropertyBuilder() extension [State, P, S, A, Player](game: GameCtx[State, P, S, A, Player]) From 748ce3906ae7072e6a66aac815c2a20a96d8df4b Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 12:20:13 +0200 Subject: [PATCH 15/19] feat: added buy development card button to view --- src/main/scala/scatan/controllers/game/GameController.scala | 6 ++++++ .../scatan/views/game/components/LeftTabComponent.scala | 6 ++++++ style.css | 6 +++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/scala/scatan/controllers/game/GameController.scala b/src/main/scala/scatan/controllers/game/GameController.scala index 4fc6b92a..ded067b8 100644 --- a/src/main/scala/scatan/controllers/game/GameController.scala +++ b/src/main/scala/scatan/controllers/game/GameController.scala @@ -17,6 +17,7 @@ trait GameController extends Controller[ApplicationState]: def rollDice(): Unit def clickCard(card: CardType): Unit def placeRobber(hexagon: Hexagon): Unit + def buyDevelopmentCard(): Unit object GameController: def apply(requirements: Controller.Requirements[GameView, ApplicationState]): GameController = @@ -71,3 +72,8 @@ private class GameControllerImpl(requirements: Controller.Requirements[GameView, this.model .updateGame(_.buildSettlement(spot)) .onError(view.displayMessage("Cannot build settlement here")) + + override def buyDevelopmentCard(): Unit = + this.model + .updateGame(_.buyDevelopmentCard) + .onError(view.displayMessage("Cannot buy development card")) diff --git a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala index d7d36eb5..8e8caa61 100644 --- a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala +++ b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala @@ -59,6 +59,12 @@ object LeftTabComponent: onClick --> { _ => gameController.rollDice() }, disabled <-- isActionDisabled(ScatanActions.RollDice) ), + button( + className := "game-view-button buy-development-card-button", + "Buy Dev. Card", + onClick --> { _ => gameController.buyDevelopmentCard() }, + disabled <-- isActionDisabled(ScatanActions.BuyDevelopmentCard) + ), button( className := "game-view-button end-turn-button", "End Turn", diff --git a/style.css b/style.css index 8d9db771..729aba0c 100644 --- a/style.css +++ b/style.css @@ -209,7 +209,7 @@ body { } .game-view-button { - width: 25%; + width: 33%; height: 3em; font-size: 1em; font-weight: bold; @@ -220,6 +220,10 @@ body { background-color: #b5ffa0; } +.buy-development-card-button { + background-color: #b5b5ff; +} + .end-turn-button { background-color: #fb9b9b; } From fe49b7d4a5f359f5ab63e53d3eb1eafac7145d09 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 13:07:46 +0200 Subject: [PATCH 16/19] feat: Implemented endgame popup that show the winner and restart the game --- .../scala/scatan/views/game/GameView.scala | 7 +++- .../game/components/EndgameComponent.scala | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/scatan/views/game/components/EndgameComponent.scala diff --git a/src/main/scala/scatan/views/game/GameView.scala b/src/main/scala/scatan/views/game/GameView.scala index 0544bb45..30bdb3a9 100644 --- a/src/main/scala/scatan/views/game/GameView.scala +++ b/src/main/scala/scatan/views/game/GameView.scala @@ -1,10 +1,13 @@ package scatan.views.game import com.raquo.laminar.api.L.* +import org.scalajs.dom import scatan.controllers.game.GameController import scatan.lib.mvc.{BaseScalaJSView, View} import scatan.model.ApplicationState -import scatan.views.game.components.{CardsComponent, GameMapComponent, LeftTabComponent} +import scatan.views.game.components.{CardsComponent, EndgameComponent, GameMapComponent, LeftTabComponent} +import scatan.views.utils.TypeUtils +import scatan.views.utils.TypeUtils.{Displayable, DisplayableSource} trait GameView extends View[ApplicationState] @@ -18,8 +21,10 @@ private class ScalaJsGameView(container: String, requirements: View.Requirements given Signal[ApplicationState] = this.reactiveState given GameController = this.controller + override def element: Element = div( + EndgameComponent.endgamePopup, div( className := LeftTabComponent.leftTabCssClass, LeftTabComponent.currentPlayerComponent, diff --git a/src/main/scala/scatan/views/game/components/EndgameComponent.scala b/src/main/scala/scatan/views/game/components/EndgameComponent.scala new file mode 100644 index 00000000..873f0a26 --- /dev/null +++ b/src/main/scala/scatan/views/game/components/EndgameComponent.scala @@ -0,0 +1,32 @@ +package scatan.views.game.components + +import com.raquo.laminar.api.L.* +import org.scalajs.dom +import scatan.views.utils.TypeUtils.{DisplayableSource, reactiveState} + +object EndgameComponent: + def endgamePopup: DisplayableSource[Element] = + val winnerSignal: Signal[Option[String]] = reactiveState.map(_.game.flatMap(_.winner.map(_.name))) + div( + display <-- winnerSignal + .map(_.isDefined) + .map(if _ then "block" else "none"), // Show or hide based on the presence of a winner + position.fixed, + top("50%"), + left("50%"), + transform := "translate(-50%, -50%)", + padding := "20px", + backgroundColor := "white", + boxShadow := "0 2px 10px rgba(0, 0, 0, 0.1)", + borderRadius := "5px", + zIndex := 1000, + child.text <-- winnerSignal.map(_.getOrElse("No winner")), + br(), + button( + "Restart", + onClick --> { _ => + // Close the popup, you can implement your own logic here + dom.window.location.reload() + } + ) + ) From 071f507b3aba56f2687dfdb02361d4851b2c4e0b Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Tue, 10 Oct 2023 14:06:28 +0200 Subject: [PATCH 17/19] feat: added current player score to view --- .../scatan/views/game/components/LeftTabComponent.scala | 8 +++++++- style.css | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala index d7d36eb5..bcb56a38 100644 --- a/src/main/scala/scatan/views/game/components/LeftTabComponent.scala +++ b/src/main/scala/scatan/views/game/components/LeftTabComponent.scala @@ -5,9 +5,10 @@ import scatan.controllers.game.GameController import scatan.lib.mvc.ScalaJSView import scatan.model.ApplicationState import scatan.model.game.config.ScatanActions +import scatan.model.game.ops.ScoreOps.scores import scatan.views.game.GameView import scatan.views.utils.TypeUtils.{Displayable, DisplayableSource} -import scatan.views.utils.TypeUtils.{reactiveState, gameController} +import scatan.views.utils.TypeUtils.{gameController, reactiveState} object LeftTabComponent: @@ -22,6 +23,11 @@ object LeftTabComponent: child.text <-- reactiveState .map("Current Player: " + _.game.map(_.turn.player.name).getOrElse("No player")) ), + h2( + className := "game-view-player-score", + child.text <-- reactiveState + .map("Score: " + _.game.map(game => game.state.scores(game.turn.player)).getOrElse("No score")) + ), h2( className := "game-view-phase", child.text <-- reactiveState diff --git a/style.css b/style.css index 8d9db771..845ff6d5 100644 --- a/style.css +++ b/style.css @@ -195,6 +195,11 @@ body { .game-view-player { font-size: 1.5em; } + +.game-view-player-score { + font-size: 1em; +} + .game-view-phase { font-size: 1em; } From ef0318f4d905e28e9c1ea12712901dd7bea4e5d6 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Wed, 11 Oct 2023 11:13:52 +0200 Subject: [PATCH 18/19] chore: removed monad concept and used iterable --- .../scala/scatan/lib/game/dsl/GameDSL.scala | 4 ++-- .../scatan/lib/game/dsl/PropertiesDSL.scala | 19 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index bc7025f7..552b06a3 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -15,7 +15,7 @@ object GameDSL: extension [State, P, S, A, Player](game: GameCtx[State, P, S, A, Player]) def rules: Rules[State, P, S, A, Player] = - val ruless: Seq[Rules[State, P, S, A, Player]] = for + val ruless: Seq[Rules[State, P, S, A, Player]] = (for startingStateFactory <- game.stateFactory startingPhase <- game.initialPhase winner <- game.winner @@ -66,6 +66,6 @@ object GameDSL: phaseTurnIteratorFactories = phaseTurnPlayerIteratorFactories, nextPhase = nextPhases, actions = actions - ) + )).toSeq require(ruless.sizeIs == 1, "Invalid rules") ruless.headOption.get diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index 1703224d..4f5ec3f8 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -1,5 +1,7 @@ package scatan.lib.game.dsl +import cats.Monad + import scala.annotation.targetName object PropertiesDSL: @@ -15,18 +17,13 @@ object PropertiesDSL: final case class SequenceProperty[P](var value: Seq[P] = Seq.empty[P]) extends Property[P]: override def apply(newValue: P): Unit = value = value :+ newValue - class MonadProperty[P <: IterableOnce[T], T](elem: P): - def foreach(f: T => Unit): Unit = elem.iterator.foreach(f) - def map[R](f: T => R): Seq[R] = elem.iterator.map(f).toSeq - def flatMap[R](f: T => Seq[R]): Seq[R] = elem.iterator.flatMap(f).toSeq - - given [P]: Conversion[OptionalProperty[P], MonadProperty[Option[P], P]] with - def apply(optionalProperty: OptionalProperty[P]): MonadProperty[Option[P], P] = - new MonadProperty(optionalProperty.value) + given [P]: Conversion[OptionalProperty[P], Iterable[P]] with + def apply(optionalProperty: OptionalProperty[P]): Iterable[P] = + optionalProperty.value.toList - given [P]: Conversion[SequenceProperty[P], MonadProperty[Seq[P], P]] with - def apply(sequenceProperty: SequenceProperty[P]): MonadProperty[Seq[P], P] = - new MonadProperty(sequenceProperty.value) + given [P]: Conversion[SequenceProperty[P], Iterable[P]] with + def apply(sequenceProperty: SequenceProperty[P]): Iterable[P] = + sequenceProperty.value // Setter From bea197c37bd51335ac27f51494faa451f57f4187 Mon Sep 17 00:00:00 2001 From: Alessandro Mazzoli Date: Wed, 11 Oct 2023 11:33:22 +0200 Subject: [PATCH 19/19] feat: documented the dsl --- .../scala/scatan/lib/game/dsl/GameDSL.scala | 22 ++++- .../scatan/lib/game/dsl/GameDSLDomain.scala | 32 +++++-- .../scatan/lib/game/dsl/PropertiesDSL.scala | 86 ++++++++++++++----- .../scatan/lib/game/dsl/ops/GameCtxOps.scala | 15 +++- .../scatan/lib/game/dsl/ops/PhaseCtxOps.scala | 28 +++++- .../lib/game/dsl/ops/PlayersCtxOps.scala | 2 + .../scatan/lib/game/dsl/ops/StepCtxOps.scala | 4 + 7 files changed, 153 insertions(+), 36 deletions(-) diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala index 552b06a3..3ed64fa5 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSL.scala @@ -3,7 +3,7 @@ package scatan.lib.game.dsl import scatan.lib.game.{GameStatus, Rules} object GameDSL: - import GameDSLDomain.* + import GameDSLDomain.{*, given} import PropertiesDSL.{*, given} export ops.GameCtxOps.* @@ -11,9 +11,24 @@ object GameDSL: export ops.PhaseCtxOps.* export ops.StepCtxOps.* - def Game[State, P, S, A, Player]: PropertyBuilder[GameCtx[State, P, S, A, Player]] = PropertyBuilder() + /** DSL for defining a game + * @tparam State + * The type of the game state + * @tparam Phase + * The type of the phases + * @tparam Step + * The type of the steps + * @tparam Actions + * The type of the actions + * @tparam Player + * The type of the players + */ + def Game[State, Phase, Step, Actions, Player]: ObjectBuilder[GameCtx[State, Phase, Step, Actions, Player]] = + ObjectBuilder() extension [State, P, S, A, Player](game: GameCtx[State, P, S, A, Player]) + /** Create the rules for the game + */ def rules: Rules[State, P, S, A, Player] = val ruless: Seq[Rules[State, P, S, A, Player]] = (for startingStateFactory <- game.stateFactory @@ -66,6 +81,7 @@ object GameDSL: phaseTurnIteratorFactories = phaseTurnPlayerIteratorFactories, nextPhase = nextPhases, actions = actions - )).toSeq + ) + ).toSeq require(ruless.sizeIs == 1, "Invalid rules") ruless.headOption.get diff --git a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala index 6829e45d..50db1a68 100644 --- a/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala +++ b/src/main/scala/scatan/lib/game/dsl/GameDSLDomain.scala @@ -3,9 +3,10 @@ package scatan.lib.game.dsl private object GameDSLDomain: import PropertiesDSL.* + export Factories.given - given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with - override def apply(): GameCtx[State, P, S, A, Player] = new GameCtx[State, P, S, A, Player] + /** The game context is used to define the game. + */ case class GameCtx[State, P, S, A, Player]( phases: SequenceProperty[PhaseCtx[State, P, S, A, Player]] = SequenceProperty[PhaseCtx[State, P, S, A, Player]](), players: OptionalProperty[PlayersCtx] = OptionalProperty[PlayersCtx](), @@ -14,13 +15,12 @@ private object GameDSLDomain: stateFactory: OptionalProperty[Seq[Player] => State] = OptionalProperty[Seq[Player] => State]() ) - given Factory[PlayersCtx] with - override def apply(): PlayersCtx = new PlayersCtx + /** The players context is used to define the players info of the game. + */ case class PlayersCtx(allowedSizes: OptionalProperty[Seq[Int]] = OptionalProperty[Seq[Int]]()) - given [State, Phase, Step, Action, Player]: Factory[PhaseCtx[State, Phase, Step, Action, Player]] with - override def apply(): PhaseCtx[State, Phase, Step, Action, Player] = PhaseCtx() - + /** The phase context is used to define a phase of the game. + */ case class PhaseCtx[State, Phase, Step, Action, Player]( phase: OptionalProperty[Phase] = OptionalProperty[Phase](), initialStep: OptionalProperty[Step] = OptionalProperty[Step](), @@ -32,9 +32,23 @@ private object GameDSLDomain: OptionalProperty[Seq[Player] => Iterator[Player]]() ) - given [P, S, A]: Factory[StepCtx[P, S, A]] with - override def apply(): StepCtx[P, S, A] = new StepCtx[P, S, A] + /** The step context is used to define a step of the game. + */ case class StepCtx[P, S, A]( step: OptionalProperty[S] = OptionalProperty[S](), when: SequenceProperty[(A, S)] = SequenceProperty[(A, S)]() ) + + private object Factories: + + given [State, P, S, A, Player]: Factory[GameCtx[State, P, S, A, Player]] with + override def apply(): GameCtx[State, P, S, A, Player] = new GameCtx[State, P, S, A, Player] + + given Factory[PlayersCtx] with + override def apply(): PlayersCtx = new PlayersCtx + + given [State, Phase, Step, Action, Player]: Factory[PhaseCtx[State, Phase, Step, Action, Player]] with + override def apply(): PhaseCtx[State, Phase, Step, Action, Player] = PhaseCtx() + + given [P, S, A]: Factory[StepCtx[P, S, A]] with + override def apply(): StepCtx[P, S, A] = new StepCtx[P, S, A] diff --git a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala index 4f5ec3f8..6b8ecc16 100644 --- a/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala +++ b/src/main/scala/scatan/lib/game/dsl/PropertiesDSL.scala @@ -1,20 +1,34 @@ package scatan.lib.game.dsl -import cats.Monad - import scala.annotation.targetName object PropertiesDSL: // Properties - sealed trait Property[P]: + /** An updatable property. + * @tparam P + * the type of the property + */ + sealed trait UpdatableProperty[P]: def apply(newValue: P): Unit - final case class OptionalProperty[P](var value: Option[P] = None) extends Property[P]: + /** An optional property, which can be updated with a new value. + * @param value + * the current value of the property + * @tparam P + * the type of the property + */ + final case class OptionalProperty[P](var value: Option[P] = None) extends UpdatableProperty[P]: override def apply(newValue: P): Unit = value = Some(newValue) - final case class SequenceProperty[P](var value: Seq[P] = Seq.empty[P]) extends Property[P]: + /** A sequence property, in which new values can be added. + * @param value + * the new value to add to the sequence + * @tparam P + * the type of the property + */ + final case class SequenceProperty[P](var value: Seq[P] = Seq.empty[P]) extends UpdatableProperty[P]: override def apply(newValue: P): Unit = value = value :+ newValue given [P]: Conversion[OptionalProperty[P], Iterable[P]] with @@ -27,33 +41,63 @@ object PropertiesDSL: // Setter - class PropertySetter[P](property: Property[P]): + /** A property setter, which can be used to update a property with a new value. + * @param property + * the property to update + * @tparam P + * the type of the property + */ + class PropertySetter[P](property: UpdatableProperty[P]): @targetName("set") def :=(value: P): Unit = property(value) - given [P]: Conversion[Property[P], PropertySetter[P]] = PropertySetter(_) + given [P]: Conversion[UpdatableProperty[P], PropertySetter[P]] = PropertySetter(_) // Updater - private type Updater[P] = P ?=> Unit + /** An object builder, which can be used to update an object. Basically, it is a function which takes an implicit + * parameter of type `P` and returns `Unit`. + */ + private type Builder[P] = P ?=> Unit trait Factory[P]: def apply(): P - class PropertyUpdater[P: Factory](property: Property[P]): - def apply(updater: Updater[P]): Unit = - val prop = summon[Factory[P]].apply() - updater(using prop) - property(prop) - - given [P: Factory]: Conversion[Property[P], PropertyUpdater[P]] = PropertyUpdater(_) + /** A property builder. It creates a new object and build it with the given builder. The property is then updated with + * the built value. + * @param property + * the property to update + * @param factory$P$0 + * the factory to create a new object + * @tparam P + * the type of the property + */ + class PropertyBuilder[P: Factory](property: UpdatableProperty[P]): + def apply(builder: Builder[P]): Unit = + val obj = summon[Factory[P]].apply() + builder(using obj) + property(obj) + + given [P: Factory]: Conversion[UpdatableProperty[P], PropertyBuilder[P]] = PropertyBuilder(_) // Builder - class PropertyBuilder[P: Factory]: - def apply(updater: Updater[P]): P = - val prop = summon[Factory[P]].apply() - updater(using prop) - prop - + /** A property creator. It creates a new object and build it with the given builder. + * @param factory$P$0 + * the factory to create a new object + * @tparam P + * the type of the property + */ + class ObjectBuilder[P: Factory]: + def apply(builder: Builder[P]): P = + val obj = summon[Factory[P]].apply() + builder(using obj) + obj + + /** A contexted function, which impose a given context to the function. + * @tparam Ctx + * the required context + * @tparam P + * the type of the function + */ type Contexted[Ctx, P] = Ctx ?=> P diff --git a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala index bb0e4622..a3a3b210 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/GameCtxOps.scala @@ -5,20 +5,31 @@ import scatan.lib.game.dsl.PropertiesDSL.* object GameCtxOps: + /** Define the winner function, which is a function that takes a game state and returns the winner of the game, if + * any. + */ def WinnerFunction[State, Player] : Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[State => Option[Player]]] = ctx ?=> ctx.winner - def Phase[State, Phase, Step, Action, Player]: Contexted[GameCtx[State, Phase, Step, Action, Player], PropertyUpdater[ + /** Define a phase of the game. + */ + def Phase[State, Phase, Step, Action, Player]: Contexted[GameCtx[State, Phase, Step, Action, Player], PropertyBuilder[ PhaseCtx[State, Phase, Step, Action, Player] ]] = ctx ?=> ctx.phases - def Players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyUpdater[PlayersCtx]] = + /** Define the Players info of the game. + */ + def Players: Contexted[GameCtx[?, ?, ?, ?, ?], PropertyBuilder[PlayersCtx]] = ctx ?=> ctx.players + /** Define the initial phase of the game. + */ def InitialPhase[Phase]: Contexted[GameCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = ctx ?=> ctx.initialPhase + /** Define the initial state factory of the game. + */ def StateFactory[State, Player]: Contexted[GameCtx[State, ?, ?, ?, Player], PropertySetter[Seq[Player] => State]] = ctx ?=> ctx.stateFactory diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala index 88283f20..ea9c91bb 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PhaseCtxOps.scala @@ -4,29 +4,55 @@ import scatan.lib.game.dsl.GameDSLDomain.* import scatan.lib.game.dsl.PropertiesDSL.* object PhaseCtxOps: + + /** Define the phase type for the phase + */ def PhaseType[Phase]: Contexted[PhaseCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = ctx ?=> ctx.phase + /** Define the initial step for the phase + */ def InitialStep[Step]: Contexted[PhaseCtx[?, ?, Step, ?, ?], PropertySetter[Step]] = ctx ?=> ctx.initialStep + /** Define the ending step for the phase + */ def EndingStep[Step]: Contexted[PhaseCtx[?, ?, Step, ?, ?], PropertySetter[Step]] = ctx ?=> ctx.endingStep + /** Define the next phase for the phase + */ def NextPhase[Phase]: Contexted[PhaseCtx[?, Phase, ?, ?, ?], PropertySetter[Phase]] = ctx ?=> ctx.nextPhase + /** Define an action to be executed when the phase is entered The action is a function that takes the current state + * and returns the new state + */ def OnEnter[State]: Contexted[PhaseCtx[State, ?, ?, ?, ?], PropertySetter[State => State]] = ctx ?=> ctx.onEnter + /** Define a step in the phase + */ def Step[Phase, StepType, Action] - : Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyUpdater[StepCtx[Phase, StepType, Action]]] = + : Contexted[PhaseCtx[?, Phase, StepType, Action, ?], PropertyBuilder[StepCtx[Phase, StepType, Action]]] = ctx ?=> ctx.steps + /** Possible Iterators Factory for Players + */ object Iterations: + /** Iterate over the sequence only once + */ def Once[X]: Seq[X] => Iterator[X] = _.iterator + + /** Iterate over the sequence infinitely + */ def Circular[X]: Seq[X] => Iterator[X] = Iterator.continually(_).flatten + + /** Iterate over the sequence once and then back + */ def OnceAndBack[X]: Seq[X] => Iterator[X] = seq => (seq ++ seq.reverse).iterator + /** Define how the players are iterated over in the phase + */ def Iterate[Player]: Contexted[PhaseCtx[?, ?, ?, ?, Player], PropertySetter[Seq[Player] => Iterator[Player]]] = ctx ?=> ctx.playerIteratorFactory diff --git a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala index 583154f9..3f323d0e 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/PlayersCtxOps.scala @@ -4,5 +4,7 @@ import scatan.lib.game.dsl.GameDSLDomain.* import scatan.lib.game.dsl.PropertiesDSL.* object PlayersCtxOps: + /** Define the allowed numbers of players in the game. + */ def CanBe: Contexted[PlayersCtx, PropertySetter[Seq[Int]]] = ctx ?=> ctx.allowedSizes diff --git a/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala index defc1349..fde8a8a7 100644 --- a/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala +++ b/src/main/scala/scatan/lib/game/dsl/ops/StepCtxOps.scala @@ -5,8 +5,12 @@ import scatan.lib.game.dsl.PropertiesDSL.* object StepCtxOps: + /** Define the type of the step + */ def StepType[Step]: Contexted[StepCtx[?, Step, ?], PropertySetter[Step]] = ctx ?=> ctx.step + /** Define a new supported action for the step and the relative step to go to when the action is performed + */ def when[Step, Action]: Contexted[StepCtx[?, Step, Action], PropertySetter[(Action, Step)]] = ctx ?=> ctx.when