-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.sml
239 lines (198 loc) · 7.33 KB
/
game.sml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
structure Card = struct
(* Cards are atomic values *)
datatype card =
I
| zero
| succ
| dbl
| get
| put
| S
| K
| inc
| dec
| attack
| help
| copy
| revive
| zombie
(* Terms are partially applied terms and effects *)
datatype term =
Card of card
| Int of IntInf.int
| put1 of term
| S1 of term
| S2 of term * term
| K1 of term
| attack1 of term
| attack2 of term * term
| help1 of term
| help2 of term * term
| zombie1 of term
end
(* Primitive immutable playing field *)
structure Field :> sig
type vitality = IntInf.int
type field
exception OutOfRange
val empty: field
(* There are 256 slots, numbered 0-255 *)
val get: field -> int -> Card.term * vitality
val put: field -> int -> Card.term * vitality -> field
(* The term component of a slot. NONE if out of range. *)
val getT: field -> int -> Card.term option
val putT: field -> int -> Card.term -> field
(* The vitality component of a slot. NONE if out of range *)
val getV: field -> int -> vitality option
val putV: field -> int -> vitality -> field
end = struct
structure Map =
SplayMapFn(struct type ord_key = int val compare = Int.compare end)
type vitality = IntInf.int
type field = (Card.term * vitality) Map.map
exception OutOfRange
val empty = Map.empty
fun get (m: field) x =
if x > 255 orelse x < 0 then raise OutOfRange
else case Map.find (m, x) of
NONE => (Card.Card Card.I, 10000)
| SOME (term, vit) => (term, vit)
fun put m x (term, vit:vitality) =
if x > 255 orelse x < 0 then raise OutOfRange
else Map.insert (m, x, (term, vit))
fun getT m x = SOME (#1 (get m x)) handle OutOfRange => NONE
fun getV m x = SOME (#2 (get m x)) handle OutOfRange => NONE
fun putT m x term = put m x (term, #2 (get m x))
fun putV m x vit = put m x (#1 (get m x), vit)
end
structure Play:> sig
type state = {proponent: Field.field, opponent: Field.field}
val apply: bool -> state * Card.term * Card.term -> state * Card.term option
end = struct
open Card
infix >>=
fun (state, x) >>= f =
case x of
NONE => (state, NONE)
| SOME x => f (state, x)
type state = {proponent: Field.field, opponent: Field.field}
fun w_proponent (state: state) proponent =
{proponent = proponent, opponent = #opponent state}
fun w_opponent (state: state) opponent =
{proponent = #proponent state, opponent = opponent}
fun getInt (Int i) = SOME i
| getInt (Card zero) = SOME 0
| getInt _ = NONE
local
fun getIndex' flip x =
case getInt x of
NONE => NONE
| SOME x =>
if x < 0 orelse x > 255
then NONE
else if flip
then SOME (255 - IntInf.toInt x)
else SOME (IntInf.toInt x)
in
val getIndex = getIndex' false
val getIndexFLIP = getIndex' true
end
fun deIntInf x =
if x > 255 orelse x < 0 then NONE else SOME (IntInf.toInt x)
val VIT_MAX: IntInf.int = 65535
fun doInc (amount: IntInf.int) (vit: IntInf.int) =
if vit > 0 andalso vit < VIT_MAX
then if vit + amount > VIT_MAX then VIT_MAX else vit + amount
else vit
fun doDec (amount: IntInf.int) (vit: IntInf.int) =
if vit > 0 andalso vit < VIT_MAX
then if vit - amount < 0 then 0 else vit - amount
else vit
fun apply isZombie (state, function, arg) =
case function of
Card I => (state, SOME arg)
| Int _ => (state, NONE)
| Card succ =>
(state, getInt arg) >>= (fn (state, x) =>
(state, SOME (Int (x + 1))))
| Card dbl =>
(state, getInt arg) >>= (fn (state, x) =>
(state, SOME (Int (x * 2))))
| Card get =>
(state, getIndex arg) >>= (fn (state, x) =>
(state, Field.getT (#proponent state) x))
| Card put => (state, SOME (put1 arg))
| put1 _ => (state, SOME arg)
| Card S => (state, SOME (S1 arg))
| S1 term => (state, SOME (S2 (term, arg)))
| S2 (f, g) =>
apply isZombie (state, f, arg) >>= (fn (state, h) =>
apply isZombie (state, g, arg) >>= (fn (state, y) =>
apply isZombie (state, h, y)))
| Card K => (state, SOME (K1 arg))
| K1 term => (state, SOME term)
| Card inc =>
(state, getIndex arg) >>= (fn (state, x) =>
(state, Field.getV (#proponent state) x) >>= (fn (state, vit) =>
(w_proponent state
(Field.putV (#proponent state) x
(if isZombie then doDec 1 vit else doInc 1 vit)),
SOME (Card I))))
| Card dec =>
(state, getIndex arg) >>= (fn (state, x) =>
(state, Field.getV (#opponent state) (255-x)) >>= (fn (state, vit) =>
(w_opponent state
(Field.putV (#opponent state) x
(if isZombie then doInc 1 vit else doDec 1 vit)),
SOME (Card I))))
| Card attack => (state, SOME (attack1 arg))
| attack1 term1 => (state, SOME (attack2 (term1, arg)))
| attack2 (i, j) =>
(state, getIndex i) >>= (fn (state, attacker) =>
(state, Field.getV (#proponent state) attacker) >>= (fn (state, vita) =>
(state, getInt arg) >>= (fn (state, damage) =>
if damage > vita then (state, NONE)
else (w_proponent state
(Field.putV (#proponent state) attacker (vita - damage)),
getIndexFLIP j) >>= (fn (state, defender) =>
(state, Field.getV (#opponent state) defender) >>= (fn (state, vitd) =>
(w_opponent state
(Field.putV (#opponent state) defender
(if isZombie
then doInc ((damage * 9) div 10) vitd
else doDec ((damage * 9) div 10) vitd)),
SOME (Card I)))))))
| Card help => (state, SOME (help1 arg))
| help1 term1 => (state, SOME (help2 (term1, arg)))
| help2 (i, j) =>
(state, getIndex i) >>= (fn (state, doctor) =>
(state, Field.getV (#proponent state) doctor) >>= (fn (state, vitd) =>
(state, getInt arg) >>= (fn (state, undamage) =>
if undamage > vitd then (state, NONE)
else (w_proponent state
(Field.putV (#proponent state) doctor (vitd - undamage)),
getIndex j) >>= (fn (state, patient) =>
(state, Field.getV (#proponent state) patient) >>= (fn (state, vitp) =>
(w_proponent state
(Field.putV (#proponent state) patient
(if isZombie
then doDec ((undamage * 11) div 10) vitp
else doInc ((undamage * 11) div 10) vitp)),
SOME (Card I)))))))
| Card copy =>
(state, getIndex arg) >>= (fn (state, x) =>
(state, Field.getT (#opponent state) x))
| Card revive =>
(state, getIndex arg) >>= (fn (state, x) =>
(state, Field.getV (#proponent state) x) >>= (fn (state, vit) =>
if vit > 0 then (state, SOME (Card I))
else (w_proponent state (Field.putV (#proponent state) x 1),
SOME (Card I))))
| Card zombie => (state, SOME (zombie1 arg))
| zombie1 i =>
(state, getIndexFLIP arg) >>= (fn (state, corpse) =>
(state, Field.getV (#opponent state) corpse) >>= (fn (state, vit) =>
if vit > 0 then (state, NONE)
else (w_opponent state (Field.put (#opponent state) corpse (arg, ~1)),
SOME (Card I))))
end