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) }
0 commit comments