diff --git a/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceEntry.kt b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceEntry.kt new file mode 100644 index 00000000..85a09c6b --- /dev/null +++ b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceEntry.kt @@ -0,0 +1,38 @@ +package net.zomis.games.components.resources + +interface ResourceEntry { + val resource: GameResource + val value: Int + operator fun component1(): GameResource = resource + operator fun component2(): Int = value +} +interface MutableResourceEntry: ResourceEntry { + override var value: Int + operator fun plusAssign(value: Int) + operator fun minusAssign(value: Int) + operator fun timesAssign(value: Int) + fun coerceAtMost(value: Int) + fun coerceAtLeast(value: Int) +} + +class ResourceEntryImpl(override val resource: GameResource, override var value: Int): MutableResourceEntry { + override fun plusAssign(value: Int) { + this.value += value + } + + override fun minusAssign(value: Int) { + this.value -= value + } + + override fun timesAssign(value: Int) { + this.value *= value + } + + override fun coerceAtMost(value: Int) { + this.value = this.value.coerceAtMost(value) + } + + override fun coerceAtLeast(value: Int) { + this.value = this.value.coerceAtLeast(value) + } +} diff --git a/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceListener.kt b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceListener.kt new file mode 100644 index 00000000..bed751e3 --- /dev/null +++ b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceListener.kt @@ -0,0 +1,14 @@ +package net.zomis.games.components.resources + +// Triggers: "When you lose more than 3 gold", get 1 gold. +// "When you gain gold", draw a card. +// "When you gain gold", also gain 2 trees. + +interface ResourceListener { + fun onChange(resource: GameResource, oldValue: Int, newValue: Int): Int = newValue + fun keepListener(): Boolean = true +} + +interface ListenableResourceMap { + fun addListener(listener: ResourceListener) +} diff --git a/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceMap.kt b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceMap.kt new file mode 100644 index 00000000..7537aee8 --- /dev/null +++ b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceMap.kt @@ -0,0 +1,96 @@ +package net.zomis.games.components.resources + +interface ResourceMap { + // val owner: T // TODO: Does this make any sense? Considering the messes of generics that would occur below? What would the concrete use-case be? + operator fun get(resource: GameResource): Int? + fun getOrDefault(resource: GameResource): Int + fun getValue(resource: GameResource): Int + operator fun plus(other: ResourceMap): ResourceMap + operator fun minus(other: ResourceMap): ResourceMap + operator fun times(value: Int): ResourceMap + fun has(resource: GameResource, value: Int): Boolean + + fun has(resources: ResourceMap): Boolean + fun resources(): Set + + fun entries(): Set + fun merge(other: ResourceMap, merger: (owned: Int?, otherValue: Int?) -> Int): ResourceMap + fun map(function: (ResourceEntry) -> Pair): ResourceMap + fun filter(function: (ResourceEntry) -> Boolean): ResourceMap + fun fold(initial: R, operation: (acc: R, ResourceEntry) -> R): R + + operator fun unaryMinus(): ResourceMap +} + +interface MutableResourceMap: ResourceMap { + operator fun set(resource: GameResource, value: Int) + operator fun plusAssign(other: ResourceMap) + operator fun minusAssign(other: ResourceMap) + operator fun timesAssign(value: Int) + fun clear(resource: GameResource) + + /** + * Moves all resources that are possible to move. Ignores any modifiers on this object. + */ + fun payTo(resources: ResourceMap, destination: ResourceMap): Boolean +} + +// move (values) to (other) +// "For the next 5 turns, you always have one wildcard to spend" --> How to move it? Probably don't force it to be moved + +// Resource aliases (not immediate problem) + +// Dynamic retrieval, modifiers. + +// See Splendor Money and Caravan in Spice Road + +class ResourceMapImpl( + private val resources: MutableMap = mutableMapOf() +): ResourceMap { + + override fun get(resource: GameResource): Int? = resources[resource]?.value + override fun getOrDefault(resource: GameResource): Int = resources[resource]?.value ?: resource.defaultValue() + override fun getValue(resource: GameResource): Int = resources.getValue(resource).value + + override fun plus(other: ResourceMap): ResourceMap = this.merge(other) { owned, otherValue -> + (owned ?: 0) + (otherValue ?: 0) + } + + override fun minus(other: ResourceMap): ResourceMap = this.merge(other) { owned, otherValue -> + (owned ?: 0) - (otherValue ?: 0) + } + + override fun times(value: Int): ResourceMap = this.map { it.resource to it.value * value } + + override fun has(resource: GameResource, value: Int): Boolean = this.getOrDefault(resource) >= value + + override fun has(resources: ResourceMap): Boolean = this.resources.all { it.value.value >= resources.getOrDefault(it.key) } + + override fun resources(): Set = this.resources.keys.toSet() + override fun entries(): Set = this.resources.values.toSet() + + override fun merge(other: ResourceMap, merger: (owned: Int?, otherValue: Int?) -> Int): ResourceMap { + val allResources = this.resources() + other.resources() + return ResourceMapImpl(allResources.associateWith { resource -> + ResourceEntryImpl(resource, merger.invoke(this[resource], other[resource])) + }.toMutableMap()) + } + + override fun map(function: (ResourceEntry) -> Pair): ResourceMap { + return ResourceMapImpl(this.resources.entries.associate { entry -> + function.invoke(entry.value).let { it.first to ResourceEntryImpl(it.first, it.second) } + }.toMutableMap()) + } + + override fun filter(function: (ResourceEntry) -> Boolean): ResourceMap { + return ResourceMapImpl(this.resources.filter { function.invoke(it.value) }.toMutableMap()) + } + + override fun fold(initial: R, operation: (acc: R, ResourceEntry) -> R): R { + return entries().fold(initial, operation) + } + + override fun unaryMinus(): ResourceMap = this.map { it.resource to -it.value } + +} + diff --git a/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceModifiers.kt b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceModifiers.kt new file mode 100644 index 00000000..ac001fe1 --- /dev/null +++ b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/ResourceModifiers.kt @@ -0,0 +1,15 @@ +package net.zomis.games.components.resources + +interface ResourceModifier { + val resource: GameResource + val priority: Int + val description: Any? + val activeCondition: () -> Boolean + val modifier: (Int) -> Int +} + +interface ModifiableResourceMap { + fun hasModified(resources: ResourceMap): Boolean + fun hasModified(resource: GameResource, value: Int): Boolean + fun getModified(resource: GameResource): Int +} diff --git a/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/Resources.kt b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/Resources.kt new file mode 100644 index 00000000..dbfe2e2d --- /dev/null +++ b/games-dsl/common/src/main/kotlin/net/zomis/games/components/resources/Resources.kt @@ -0,0 +1,11 @@ +package net.zomis.games.components.resources + +interface GameResource { + fun defaultValue(): Int = 0 + fun toResourceMap(value: Int = 1): ResourceMap = TODO() + operator fun plus(other: GameResource): ResourceMap = this.toResourceMap().plus(other.toResourceMap()) + operator fun times(value: Int): ResourceMap = this.toResourceMap(value) +} + +object Resources { +} \ No newline at end of file