-
Notifications
You must be signed in to change notification settings - Fork 0
/
dominator-scout.sml
executable file
·239 lines (207 loc) · 8.81 KB
/
dominator-scout.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
(* The purpose of the scout is to get us into the long game.
What we do is build up an option to either
- heal our slot 255 a lot
- attack their slot 0 a lot
Both of these strategies sacrifice the 0 and 1 slots.
The idea behind healing 255 a lot is that it's often the
target of super-short fixed strategies, because it's the
easiest slot to name from the opponent's context. These
fixed strategies usually have fixed attack values, so if
we heal the slot, they probably don't work.
The idea behind attacking slot 0 is that it's often part
of naive programs, setup phases, or super-short fixed
strategies. If it's dead, these often fail.
When this option is ready, we decide to take the disruptive
aggressive strategy or the disruptive conservative strategy.
How?
- If slot 0 doesn't have anything in it, then we turtle.
The scout should be done in the first 1000 (?) moves,
and then never runs again.
*)
structure Scout :> DOMINATOR =
struct
structure GS = GameState
datatype src = datatype Kompiler.src
structure EP = EmitProgram
infix 9 --
val op -- = Apply
val $ = Var
fun \ x exp = Lambda (x, exp)
infixr 1 `
fun a ` b = a b
val lastmsg = ref ""
val eprint =
fn s => if s = !lastmsg
then ()
else (eprint ("[SCOUT] " ^ s ^ "\n"); lastmsg := s)
fun create () =
let
fun prelude (enemy : int) : LTG.turn list =
Macros.fastnum 2 enemy @ (* load enemy slot number into our slot 2 *)
Macros.fastnum 0 8192 (* slot 0 holds 8192 (amt. of damage) *)
fun proceed (action : LTG.card) : LTG.turn list =
Macros.fastload 1 action @
Macros.apply_slot_to_int 1 0 (* mine1 *) @
Macros.apply_slot_to_slot 1 2 @
Macros.apply_slot_to_slot 1 0 @ (* executes action mine1 enemy 8192 *)
Macros.fastload 1 action @
Macros.apply_slot_to_int 1 1 (* mine2 *) @
Macros.apply_slot_to_slot 1 2 @
Macros.apply_slot_to_slot 1 0 (* executes action mine2 enemy 8192 *)
(*
Macros.fastload 1 Zombie @
Macros.apply_slot_to_slot 1 2 @
Macros.apply_slot_to_int 1 0 (* executes Zombie enemy 0 *)
*)
fun opening_program slot =
let
(* too slow... *)
val option =
\"card" `
((\"twofivefive" `
\"eightoneninetwo" `
(* Use the card from slot 0 to 255, for 8192 damage/healing
(returns the identity) *)
($"card" -- Card LTG.Zero -- $"twofivefive" -- $"eightoneninetwo") --
(* Then again from slot 1 to 255. *)
($"card" -- (Card LTG.Succ -- Card LTG.Zero) -- $"twofivefive" -- $"eightoneninetwo"))
-- Int 255 -- Int 8192)
(* Must be reserved! *)
val slotwith255 = 0
val slotwith8190 = 1
val slotwithcard = 2
val get255 = (Card LTG.Get -- Int slotwith255)
val get8190 = (Card LTG.Get -- Int slotwith8190)
val getcard = (Card LTG.Get -- Int slotwithcard)
(* This is the current favorite.
TODO: If it looks like slot 0 is not being used, then apply
Heal instead. *)
val fastoption =
Macros.fastnum 0 255 @
Macros.fastnum 1 0 @
[Macros.L LTG.Get 1] @ (* Loads the 255 from slot 0 *)
(* Macros.succ 1 @ *)
Macros.rep 5 (Macros.dbl 1) @ (* double until we have 8190 *)
(* XXX make this decision dynamically *)
Macros.fastload 2 LTG.Attack @
Kompiler.compile
((getcard -- Int 0 -- get255 -- get8190) --
(getcard -- Int 1 -- get255 -- get8190)) slot
(* Still too slow. *)
val turtle =
(\"twofivefive" `
\"eightoneninetwo" `
(Card LTG.Help -- Card LTG.Zero -- $"twofivefive" -- $"eightoneninetwo") --
(* Then again from slot 1 to 255. *)
(Card LTG.Help -- (Card LTG.Succ -- Card LTG.Zero) -- $"twofivefive" -- $"eightoneninetwo"))
-- Int 255 -- Int 8192
(* For doubleheal, should probably attempt revive if the
slot dies... (this happens with gwillen's fastest_doubleshot). *)
val doubleheal = prelude 255 @ proceed LTG.Help
val doubletap = prelude 255 @ proceed LTG.Attack
val prog = fastoption
in
(* eprint ("Opening: " ^ Kompiler.src2str prog);
Kompiler.compile prog slot *)
prog
end handle (e as Kompiler.Kompiler s) =>
let in
eprint ("Kompilation failed: " ^ s ^ "\n");
raise e
end
fun preview dos = ()
datatype scoutmode =
Start
| Loading of LTG.turn list
| Decide
val slot = ref NONE
val mode = ref Start
val REQUIRED_SLOTS = [0, 1, 2]
fun taketurn dos =
let val gs = DOS.gamestate dos
fun utoh () =
let in
eprint "Scout can't run because it can't get slots 0 and 1 and 2 at the very start.";
eprint "This is bad! The scout is intended to run first.";
eprint "Suiciding...";
DOS.kill (DOS.getpid dos);
DOS.Can'tRun
end
in
case !mode of
Start =>
(case (DOS.reserve_fixed_slots dos REQUIRED_SLOTS, DOS.reserve_slot dos) of
(false, NONE) => utoh ()
| (true, NONE) => (app (DOS.release_slot dos) REQUIRED_SLOTS; utoh ())
| (false, SOME s) => (DOS.release_slot dos s; utoh ())
| (true, SOME s) =>
let
val prog = opening_program s
in
slot := SOME s;
eprint ("Opening program length: " ^ Int.toString (length prog) ^
" to be put in slot " ^ Int.toString s);
mode := Loading prog;
taketurn dos
end)
| Loading nil =>
let
in
eprint ("Loading complete.");
mode := Decide;
taketurn dos
end
| Loading (t :: rest) =>
let val myslot = valOf (!slot)
val am = LTG.slotisdead (GS.myside gs) myslot
(* XXX some of these temporaries aren't needed for the
whole program. *)
val a0 = LTG.slotisdead (GS.myside gs) 0
val a1 = LTG.slotisdead (GS.myside gs) 1
val a2 = LTG.slotisdead (GS.myside gs) 2
in
(* XXX actually decide *)
if am orelse a0 orelse a1 orelse a2
then
let in
eprint ("Dead:" ^ (if am then " mine" else "") ^
(if a0 then " 0" else "") ^
(if a1 then " 1" else "") ^
(if a2 then " 2" else "") ^
" with " ^ Int.toString (length rest) ^
" more moves...");
DOS.release_all_slots dos;
DOS.kill (DOS.getpid dos);
DOS.Can'tRun
end
else (mode := Loading rest; DOS.Turn t)
end
| Decide =>
(* XXX this is not neeed any more ... *)
let val myslot = valOf (!slot)
val am = LTG.slotisdead (GS.myside gs) myslot
val a0 = LTG.slotisdead (GS.myside gs) 0
val a1 = LTG.slotisdead (GS.myside gs) 1
in
eprint ("Attacking!");
(* No matter what, we're done. *)
DOS.release_all_slots dos;
DOS.kill (DOS.getpid dos);
(* XXX actually decide *)
if am orelse a0 orelse a1
then
let in
eprint ("Dead:" ^ (if am then " mine" else "") ^
(if a0 then " 0" else "") ^
(if a1 then " 1" else ""));
DOS.Can'tRun
end
else (* DOS.Turn (LTG.RightApply (myslot, LTG.Help)) *)
DOS.Can'tRun
end
end
in
{ preview = preview,
taketurn = taketurn }
end
end