Skip to content

Commit b26d243

Browse files
committed
Implement food cost
1 parent 9e905d8 commit b26d243

File tree

5 files changed

+152
-14
lines changed

5 files changed

+152
-14
lines changed

.idea/misc.xml

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.peterfarlow.core
2+
3+
enum class Food(val symbol: String) {
4+
INVERTEBRATE("I"),
5+
SEED("S"),
6+
RODENT("R"),
7+
FISH("F"),
8+
FRUIT("C"),
9+
WILD("W");
10+
11+
companion object {
12+
fun deserialize(symbol: Char): Food = values().first { it.symbol == symbol.toString() }
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.peterfarlow.core
2+
3+
import android.os.Parcelable
4+
import kotlinx.parcelize.Parcelize
5+
import kotlinx.parcelize.RawValue
6+
7+
sealed class FoodCost(open val items: Collection<Food>) : Parcelable {
8+
9+
abstract fun payFoodCost(availableFood: Collection<Food>): FoodCostOutcome
10+
11+
fun canAfford(availableFood: Collection<Food>) = payFoodCost(availableFood).isSuccess
12+
13+
@Parcelize
14+
object JustWild : FoodCost(setOf(Food.WILD)) {
15+
override fun payFoodCost(availableFood: Collection<Food>): FoodCostOutcome {
16+
return if (availableFood.isEmpty()) {
17+
FoodCostOutcome.Failure(this)
18+
} else {
19+
val usedFood = availableFood.toMutableList().apply {
20+
removeFirst()
21+
}
22+
FoodCostOutcome.Success(usedFood)
23+
}
24+
}
25+
26+
override fun toString() = "JustWild"
27+
}
28+
29+
@Parcelize
30+
object None : FoodCost(emptyList()) {
31+
override fun payFoodCost(availableFood: Collection<Food>) =
32+
FoodCostOutcome.Success(emptyList())
33+
34+
override fun toString() = "None"
35+
}
36+
37+
@Parcelize
38+
data class Any(override val items: @RawValue Collection<Food>) : FoodCost(items) {
39+
override fun payFoodCost(availableFood: Collection<Food>): FoodCostOutcome {
40+
if (availableFood.isEmpty()) return FoodCostOutcome.Failure(this)
41+
val remainingFood = availableFood.toMutableList()
42+
for (food in items) {
43+
if (remainingFood.remove(food)) {
44+
return FoodCostOutcome.Success(listOf(food))
45+
}
46+
}
47+
return FoodCostOutcome.Failure(this)
48+
}
49+
}
50+
51+
@Parcelize
52+
data class All(override val items: @RawValue Collection<Food>) : FoodCost(items) {
53+
override fun payFoodCost(availableFood: Collection<Food>): FoodCostOutcome {
54+
if (availableFood.isEmpty()) return FoodCostOutcome.Failure(this)
55+
val remainingFood = availableFood.toMutableList()
56+
val missingFood = mutableListOf<Food>()
57+
val usedFood = mutableListOf<Food>()
58+
val sortedFoodCost = items.sorted() // put the wilds last
59+
for (food in sortedFoodCost) {
60+
if (remainingFood.remove(food)) {
61+
usedFood.add(food)
62+
} else {
63+
if (food == Food.WILD) {
64+
if (remainingFood.isEmpty()) {
65+
missingFood.add(food)
66+
} else {
67+
val first = remainingFood.first()
68+
remainingFood.remove(first)
69+
usedFood.add(first)
70+
}
71+
} else {
72+
missingFood.add(food)
73+
}
74+
}
75+
}
76+
return if (missingFood.isEmpty()) {
77+
FoodCostOutcome.Success(usedFood)
78+
} else {
79+
FoodCostOutcome.Failure(All(missingFood))
80+
}
81+
}
82+
}
83+
}
84+
85+
sealed class FoodCostOutcome {
86+
data class Success(val foodUsed: Collection<Food>) : FoodCostOutcome()
87+
data class Failure(val missingFood: FoodCost) : FoodCostOutcome()
88+
89+
val isSuccess get() = this is Success
90+
}
91+
92+
fun String.toFoodCost(): FoodCost {
93+
val parts = split("-")
94+
check(parts.size == 2)
95+
return when {
96+
parts[0] == "any" -> {
97+
FoodCost.Any(parts[1].toFoodList())
98+
}
99+
parts[0] == "all" -> {
100+
FoodCost.All(parts[1].toFoodList())
101+
}
102+
else -> {
103+
throw IllegalArgumentException()
104+
}
105+
}
106+
}
107+
108+
fun String.toFoodList(): List<Food> = toCharArray().map { Food.deserialize(it) }

core/src/main/java/com/peterfarlow/core/TurnAction.kt

+1-13
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ data class Bird(
1818
val latinName: String = "",
1919
val points: Points = 0,
2020
val habitat: Set<Habitat>,
21-
val foodCosts: Set<FoodCost>,
21+
val foodCost: FoodCost,
2222
val nestType: NestType,
2323
val description: String = "",
2424
val wingspan: Int,
@@ -54,16 +54,6 @@ enum class NestType {
5454
NONE
5555
}
5656

57-
58-
enum class Food {
59-
WORM,
60-
WHEAT,
61-
MOUSE,
62-
FISH,
63-
CHERRY,
64-
WILD
65-
}
66-
6757
enum class FoodDiceFace {
6858
WORM,
6959
WHEAT,
@@ -73,8 +63,6 @@ enum class FoodDiceFace {
7363
WORM_WHEAT
7464
}
7565

76-
typealias FoodCost = Set<Food>
77-
7866
@Parcelize
7967
data class PlayerFoodSupply(
8068
val worms: Int = 0,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.peterfarlow.core
2+
3+
import org.hamcrest.MatcherAssert.assertThat
4+
import org.hamcrest.core.IsEqual.equalTo
5+
import org.junit.Test
6+
7+
class FoodCostTest {
8+
9+
private val testMap: List<Triple<FoodCost, List<Food>, FoodCostOutcome>> = listOf(
10+
Triple("any-ISR".toFoodCost(), "I".toFoodList(), FoodCostOutcome.Success("I".toFoodList())),
11+
Triple("all-RCF".toFoodCost(), "C".toFoodList(), FoodCostOutcome.Failure("all-RF".toFoodCost())),
12+
Triple(FoodCost.None, emptyList(), FoodCostOutcome.Success(emptyList())),
13+
Triple("all-SSS".toFoodCost(), "SS".toFoodList(), FoodCostOutcome.Failure("all-S".toFoodCost())),
14+
Triple("all-RRSWW".toFoodCost(), "FFFFFFFFSRR".toFoodList(), FoodCostOutcome.Success("SRRFF".toFoodList())),
15+
Triple("all-CCSRW".toFoodCost(), "CCCCCCCCCC".toFoodList(), FoodCostOutcome.Failure("all-SR".toFoodCost()))
16+
)
17+
18+
@Test
19+
fun testFood() {
20+
testMap.forEach {
21+
assertThat(
22+
"input ${it.first} ${it.second}",
23+
it.first.payFoodCost(it.second),
24+
equalTo(it.third)
25+
)
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)