-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feature-build-position-checks
- Loading branch information
Showing
40 changed files
with
658 additions
and
450 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,87 @@ | ||
package scatan.lib.game.dsl | ||
|
||
/** 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 | ||
import scatan.lib.game.{GameStatus, Rules} | ||
|
||
private val typedDSL = new TypedGameDSL[State, PhaseType, StepType, ActionType, Player] {} | ||
object GameDSL: | ||
import GameDSLDomain.{*, given} | ||
import PropertiesDSL.{*, given} | ||
|
||
export typedDSL.{isOver as _, winner as _, *} | ||
export ops.GameCtxOps.* | ||
export ops.PlayersCtxOps.* | ||
export ops.PhaseCtxOps.* | ||
export ops.StepCtxOps.* | ||
|
||
/** 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 | ||
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 | ||
) | ||
).toSeq | ||
require(ruless.sizeIs == 1, "Invalid rules") | ||
ruless.headOption.get |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package scatan.lib.game.dsl | ||
|
||
private object GameDSLDomain: | ||
|
||
import PropertiesDSL.* | ||
export Factories.given | ||
|
||
/** 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](), | ||
winner: OptionalProperty[State => Option[Player]] = OptionalProperty[State => Option[Player]](), | ||
initialPhase: OptionalProperty[P] = OptionalProperty[P](), | ||
stateFactory: OptionalProperty[Seq[Player] => State] = OptionalProperty[Seq[Player] => State]() | ||
) | ||
|
||
/** The players context is used to define the players info of the game. | ||
*/ | ||
case class PlayersCtx(allowedSizes: OptionalProperty[Seq[Int]] = OptionalProperty[Seq[Int]]()) | ||
|
||
/** 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](), | ||
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]]() | ||
) | ||
|
||
/** 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] |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package scatan.lib.game.dsl | ||
|
||
import scala.annotation.targetName | ||
|
||
object PropertiesDSL: | ||
|
||
// Properties | ||
|
||
/** An updatable property. | ||
* @tparam P | ||
* the type of the property | ||
*/ | ||
sealed trait UpdatableProperty[P]: | ||
def apply(newValue: P): Unit | ||
|
||
/** 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) | ||
|
||
/** 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 | ||
def apply(optionalProperty: OptionalProperty[P]): Iterable[P] = | ||
optionalProperty.value.toList | ||
|
||
given [P]: Conversion[SequenceProperty[P], Iterable[P]] with | ||
def apply(sequenceProperty: SequenceProperty[P]): Iterable[P] = | ||
sequenceProperty.value | ||
|
||
// Setter | ||
|
||
/** 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[UpdatableProperty[P], PropertySetter[P]] = PropertySetter(_) | ||
|
||
// Updater | ||
|
||
/** 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 | ||
|
||
/** 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 | ||
|
||
/** 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 |
Oops, something went wrong.