Skip to content

Commit

Permalink
Add ArchUnit and rename interfaces to standardize architecture #320
Browse files Browse the repository at this point in the history
  • Loading branch information
Zomis committed Nov 2, 2022
1 parent 0c6dee6 commit 5284575
Show file tree
Hide file tree
Showing 41 changed files with 296 additions and 177 deletions.
66 changes: 66 additions & 0 deletions games-dsl/common/src/main/kotlin/net/zomis/games/api/Scopes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package net.zomis.games.api

import net.zomis.games.PlayerEliminationsRead
import net.zomis.games.PlayerEliminationsWrite
import net.zomis.games.dsl.GameConfig
import net.zomis.games.dsl.impl.Game

interface Scope
interface PrimitiveScope: Scope
interface CompoundScope: Scope
interface UsageScope: Scope

interface GameModelScope<T: Any>: PrimitiveScope {
val model: T
}
interface PlayerCountScope: PrimitiveScope {
val playerCount: Int
val playerIndices get() = (0 until playerCount)
}
interface GameScope<T: Any>: PrimitiveScope {
val game: Game<T>
}
interface ReplayableScope: PrimitiveScope {
val replayable: net.zomis.games.dsl.ReplayStateI
}
interface EventScope<E>: PrimitiveScope {
val event: E
}
interface ValueScope<V>: PrimitiveScope {
val value: V
}
interface EliminationsScope: PrimitiveScope {
val eliminations: PlayerEliminationsRead
}
interface MutableEliminationsScope: PrimitiveScope {
val eliminations: PlayerEliminationsWrite
}
interface ActionScope<A: Any>: PrimitiveScope {
val action: A
}
interface ConfigScope: PrimitiveScope {
fun <C: Any> config(config: GameConfig<C>): C
}
interface PlayerIndexScope: PrimitiveScope {
val playerIndex: Int
}
/*
interface LogScope<T: Any>: PrimitiveScope {
suspend fun log(logging: LoggingScope<T>.() -> String)
suspend fun logSecret(player: PlayerIndex, logging: LoggingScope<T>.() -> String): LogSecretScope<T>
}
*/

/*
Categories:
- Events / Triggers
- Rules
- Actions
- ActionsChosen
- Game setup
- GameFlow
- GameFlowStep
- Metrics
- Scorers
*/
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package net.zomis.games.common.rules

import net.zomis.games.WinResult
import net.zomis.games.dsl.flow.GameFlowRules
import net.zomis.games.dsl.flow.GameFlowRulesScope
import net.zomis.games.dsl.rulebased.GameCommonRule
import net.zomis.games.dsl.rulebased.GameRules
import net.zomis.games.dsl.rulebased.GameRulesScope

class GameDslRuleCreator<T: Any>(private val dsl: GameRules<T>): RuleCreator<T> {
class GameDslRuleCreator<T: Any>(private val dsl: GameRulesScope<T>): RuleCreator<T> {
override fun rule(name: String, rule: GameCommonRule<T>.() -> Unit) {
dsl.rule(name, rule)
}
}

class GameFlowRuleCreator<T: Any>(private val dsl: GameFlowRules<T>): RuleCreator<T> {
class GameFlowRuleCreator<T: Any>(private val dsl: GameFlowRulesScope<T>): RuleCreator<T> {
override fun rule(name: String, rule: GameCommonRule<T>.() -> Unit) {
dsl.rule(name, rule)
}
Expand All @@ -34,4 +34,4 @@ class Rules<T: Any>(private val ruleCreator: RuleCreator<T>) {
val players get() = PlayerRules(ruleCreator)
}

val <T: Any> GameRules<T>.rules get() = Rules(GameDslRuleCreator(this))
val <T: Any> GameRulesScope<T>.rules get() = Rules(GameDslRuleCreator(this))
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.zomis.games.cards

import net.zomis.games.dsl.Replayable
import net.zomis.games.dsl.ReplayableScope
import net.zomis.games.dsl.ReplayStateI

enum class DeckDirection {
TOP, BOTTOM
Expand Down Expand Up @@ -110,12 +110,12 @@ class CardZone<T>(val cards: MutableList<T> = mutableListOf()): CardZoneI<T> {
}
}

fun random(replayable: ReplayableScope, count: Int, stateKey: String, matcher: (T) -> String): Sequence<Card<T>> {
fun random(replayable: ReplayStateI, count: Int, stateKey: String, matcher: (T) -> String): Sequence<Card<T>> {
if (count == 0) return emptySequence()
require(count <= this.size) { "Requesting more cards $count than what exists in zone $size" }
return replayable.randomFromList(stateKey, this.cards, count, matcher).asSequence().map { card(it) }
}
fun randomWithRefill(refill: CardZone<T>, replayable: ReplayableScope, count: Int, stateKey: String, matcher: (T) -> String): Sequence<Card<T>> {
fun randomWithRefill(refill: CardZone<T>, replayable: ReplayStateI, count: Int, stateKey: String, matcher: (T) -> String): Sequence<Card<T>> {
if (count == 0) return emptySequence()
if (count <= this.size) {
return random(replayable, count, stateKey, matcher)
Expand All @@ -130,6 +130,6 @@ class CardZone<T>(val cards: MutableList<T> = mutableListOf()): CardZoneI<T> {
override fun isNotEmpty(): Boolean = !isEmpty()

}
fun <T: Replayable> CardZone<T>.random(replayable: ReplayableScope, count: Int, stateKey: String): Sequence<Card<T>> {
fun <T: Replayable> CardZone<T>.random(replayable: ReplayStateI, count: Int, stateKey: String): Sequence<Card<T>> {
return this.random(replayable, count, stateKey) { it.toStateString() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import net.zomis.games.cards.Card
import net.zomis.games.cards.CardZone
import net.zomis.games.cards.CardZoneI
import net.zomis.games.cards.DeckDirection
import net.zomis.games.dsl.ReplayableScope
import net.zomis.games.dsl.ReplayStateI

/**
* A class that keeps track of cards. Like CardZone but may remember which card is the 5th from top etc.
Expand All @@ -22,11 +22,11 @@ class SemiKnownCardZone<T: Any>(cards: List<T> = emptyList(), val matcher: (T) -
return cards.toList().withIndex().filter { it.value != null }.map { IndexedValue(it.index, it.value!!) }
}

fun top(replayable: ReplayableScope, stateKey: String, count: Int): List<Card<T>> {
fun top(replayable: ReplayStateI, stateKey: String, count: Int): List<Card<T>> {
return learnCards(0 until count, replayable, stateKey)
}

private fun learnCards(range: IntRange, replayable: ReplayableScope, stateKey: String): List<Card<T>> {
private fun learnCards(range: IntRange, replayable: ReplayStateI, stateKey: String): List<Card<T>> {
require(range.first >= 0 && range.last < size) { "Requested cards $range is outside of zone size $indices" }
val unknownCount = cards.slice(range).count { it == null }
val newLearntCards = this.randomFromUnknown(replayable, unknownCount, stateKey).toList()
Expand Down Expand Up @@ -57,7 +57,7 @@ class SemiKnownCardZone<T: Any>(cards: List<T> = emptyList(), val matcher: (T) -
cards.add(card)
}

fun deal(replayable: ReplayableScope, stateKey: String, count: Int, destinations: List<CardZone<T>>) {
fun deal(replayable: ReplayStateI, stateKey: String, count: Int, destinations: List<CardZone<T>>) {
learnCards(0 until count, replayable, stateKey).forEachIndexed { index, card ->
card.moveTo(destinations[index % destinations.size])
}
Expand All @@ -84,7 +84,7 @@ class SemiKnownCardZone<T: Any>(cards: List<T> = emptyList(), val matcher: (T) -
return Card(this, index, value)
}

private fun randomFromUnknown(replayable: ReplayableScope, count: Int, stateKey: String): List<T> {
private fun randomFromUnknown(replayable: ReplayStateI, count: Int, stateKey: String): List<T> {
require(count <= this.unassignedCards.size) { "Requesting more cards $count than are unknown in zone $this" }
return replayable.randomFromList(stateKey, this.unassignedCards, count) { matcher.invoke(it) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package net.zomis.games.context

import net.zomis.games.PlayerEliminationsWrite
import net.zomis.games.api.ConfigScope
import net.zomis.games.api.EventScope
import net.zomis.games.api.UsageScope
import net.zomis.games.api.ValueScope
import net.zomis.games.cards.CardZone
import net.zomis.games.dsl.*
import net.zomis.games.dsl.flow.ActionDefinition
Expand Down Expand Up @@ -35,12 +39,8 @@ class DynamicValueDelegate<E>(val function: () -> E): ReadOnlyProperty<Entity?,
override fun getValue(thisRef: Entity?, property: KProperty<*>): E = function.invoke()
}

interface HandlerScope<E, T> {
interface HandlerScope<E, T>: UsageScope, ValueScope<E>, EventScope<T>, ConfigScope, net.zomis.games.api.ReplayableScope {
// Do not mark with @GameMarker as it should be allowed to reference event from nested calls.
val replayable: ReplayableScope
val value: E
val event: T
fun <E: Any> config(config: GameConfig<E>): E
}

typealias ViewFunction = (ViewScope<Any>) -> Any?
Expand Down Expand Up @@ -230,7 +230,7 @@ class EventListener(
if (this.event == event) {
val value = getter.invoke()
val result = this.handler.invoke(object : HandlerScope<Any, Any> {
override val replayable: ReplayableScope get() = c.replayable
override val replayable: ReplayStateI get() = c.replayable
override val value: Any get() = value
override val event: Any get() = eventValue
override fun <E : Any> config(config: GameConfig<E>): E {
Expand Down Expand Up @@ -265,7 +265,7 @@ class GameCreatorContext<T: ContextHolder>(val gameName: String, val function: G
return config
}

fun toDsl(): GameDsl<T>.() -> Unit {
fun toDsl(): GameDslScope<T>.() -> Unit {
this.function.invoke(this)
return {
for (config in this@GameCreatorContext.configs) {
Expand Down Expand Up @@ -299,7 +299,7 @@ class GameCreatorContext<T: ContextHolder>(val gameName: String, val function: G
}
}
@GameMarker
interface GameCreatorContextScope<T: Any> {
interface GameCreatorContextScope<T: Any>: UsageScope {
fun players(players: IntRange)
fun init(function: ContextHolder.() -> T)
fun gameFlow(function: suspend GameFlowScope<T>.() -> Unit)
Expand Down
33 changes: 12 additions & 21 deletions games-dsl/common/src/main/kotlin/net/zomis/games/dsl/DslAPI.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package net.zomis.games.dsl

import net.zomis.games.PlayerEliminationsRead
import net.zomis.games.PlayerEliminationsWrite
import net.zomis.games.dsl.flow.GameFlowRules
import net.zomis.games.api.*
import net.zomis.games.dsl.flow.GameFlowRulesScope
import net.zomis.games.dsl.flow.GameFlowScope
import net.zomis.games.dsl.impl.GameAI
import net.zomis.games.dsl.impl.GameAIScope
Expand All @@ -14,24 +13,16 @@ data class ActionableOption(val actionType: String, val parameter: Any, val disp
interface GameSerializable {
fun serialize(): Any
}
interface EventTools {
val eliminations: PlayerEliminationsWrite
val replayable: ReplayableScope
fun <E: Any> config(gameConfig: GameConfig<E>): E
}
interface GameUtils {
val eliminations: PlayerEliminationsRead
val replayable: ReplayableScope
fun <E: Any> config(gameConfig: GameConfig<E>): E
}
interface EventTools : CompoundScope, MutableEliminationsScope, ReplayableScope, ConfigScope
interface GameUtils : CompoundScope, EliminationsScope, ReplayableScope, ConfigScope

class GameSpec<T : Any>(val name: String, val dsl: GameDsl<T>.() -> Unit) {
operator fun invoke(context: GameDsl<T>) = dsl(context)
class GameSpec<T : Any>(val name: String, val dsl: GameDslScope<T>.() -> Unit) {
operator fun invoke(context: GameDslScope<T>) = dsl(context)
}
typealias GameTestDsl<T> = suspend GameTest<T>.() -> Unit
typealias GameModelDsl<T, C> = GameModel<T, C>.() -> Unit
typealias GameActionRulesDsl<T> = GameActionRules<T>.() -> Unit
typealias GameFlowRulesDsl<T> = GameFlowRules<T>.() -> Unit
typealias GameTestDsl<T> = suspend GameTestScope<T>.() -> Unit
typealias GameModelDsl<T, C> = GameModelScope<T, C>.() -> Unit
typealias GameActionRulesDsl<T> = GameActionRulesScope<T>.() -> Unit
typealias GameFlowRulesDsl<T> = GameFlowRulesScope<T>.() -> Unit
typealias GameFlowDsl<T> = suspend GameFlowScope<T>.() -> Unit

interface GameConfig<E: Any> {
Expand Down Expand Up @@ -72,7 +63,7 @@ class GameConfigs(val configs: List<GameConfig<Any>>) {
}

@GameMarker
interface GameDsl<T : Any> {
interface GameDslScope<T : Any> : UsageScope {
var useRandomAI: Boolean
@Deprecated("use GameConfig class")
fun <C : Any> setup(configClass: KClass<C>, modelDsl: GameModelDsl<T, C>)
Expand All @@ -87,7 +78,7 @@ interface GameDsl<T : Any> {
}

class GameCreator<T : Any>(val modelClass: KClass<T>) {
fun game(name: String, dsl: GameDsl<T>.() -> Unit): GameSpec<T> = GameSpec(name, dsl)
fun game(name: String, dsl: GameDslScope<T>.() -> Unit): GameSpec<T> = GameSpec(name, dsl)
fun <A: Any> action(name: String, parameterType: KClass<A>): GameActionCreator<T, A>
= GameActionCreator(name, parameterType, parameterType, {it}, {it as A})
fun singleAction(name: String) = this.action(name, Unit::class)
Expand Down
Loading

0 comments on commit 5284575

Please sign in to comment.