-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer-ripens.sml
executable file
·131 lines (108 loc) · 3.68 KB
/
player-ripens.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
structure Ripens :> CORO_LAYER =
struct
structure GS = GameState
datatype src = datatype Kompiler.src
datatype return = RETURN of (GS.gamestate * (LTG.turn -> return))
fun eprint s = TextIO.output (TextIO.stdErr, s)
(* Hard to kill. *)
val ATTACK_SLOT = 0
datatype mode =
FindTarget
(* Building the attack program for the given
cell. *)
| Emit of int * LTG.turn list
(* Keep attacking this slot until it's dead,
then find a new target. *)
| Attacking of int
val compare_scores = ListUtil.bysecond Real.compare
infix 9 --
val op -- = Apply
val $ = Var
fun \ x exp = Lambda (x, exp)
infixr 1 `
fun a ` b = a b
(* Takes an expression of object type unit.
Wraps in a function that ignores its single argument,
evaluates the expression, and then returns itself.
fun f _ = (exp; f)
*)
fun returnself src =
let val recursive =
\"self" `
\"unused" `
Card LTG.Put -- src -- $"self"
(* CBV fix-point operator. *)
val minifix =
\"x" ` $"f" -- (\"y" ` $"x" -- $"x" -- $"y")
val fix = \"f" ` minifix -- minifix
in
Apply (fix, recursive)
end
(* Makes a program that attacks the target slot index,
and then returns itself (so it sticks around). *)
fun attackprogram target =
let
(* XXX should dec as much as we can in this turn,
without exceeding 1000. *)
(* Numbers are reversed when attacking opponent. *)
val revtarget = 255 - target
val dec =
(\"f" ` $"f" -- ($"f" -- ($"f" -- ($"f" -- $"f")))) --
(\"_" ` Card LTG.Dec -- Int revtarget)
val prog = returnself dec
in
Kompiler.compile prog ATTACK_SLOT
end handle (e as Kompiler.Kompiler s) =>
let in
eprint ("Kompilation failed: " ^ s ^ "\n");
raise e
end
val n = ref 0
(* emit : LTG.turn list -> (GS.gamestate * (LTG.turn -> return))
* -> (GS.gamestate * (LTG.turn -> return))
**)
fun emit nil (gs, return) = (gs, return)
| emit (m::ms) (gs, return) =
let
val RETURN (gs, return) = return m
in
emit ms (gs, return)
end
fun algorithm (gs, return) =
let
val theirside = GS.theirside gs
(* Find the highest-value slot in the
opponent's state, according to the
stats *)
val stats = GS.theirstats gs
val slots = List.tabulate (256, fn i =>
(i, GS.scoreopponentslot gs i))
(* Maybe should have a lower bound on what it will
consider valuable, and just heal/revive if there
are no current high-value targets. *)
val (best, _) = ListUtil.max compare_scores slots
val prog = attackprogram best
(* val () = eprint ("Program: " ^ LTG.turns2str prog ^ "\n") *)
val () = eprint ("Attack Program size: " ^ Int.toString (length prog) ^ "\n")
val (gs, return) = emit prog (gs, return)
fun attack (gs, return) =
let
val theirside = GS.theirside gs
val health = Array.sub (#2 theirside, best)
in
if health <= 0
then (eprint ("Success! Killed slot " ^
Int.toString best ^ "\n");
(gs, return))
else let
val RETURN (gs, return) = return (LTG.RightApply (ATTACK_SLOT, LTG.I))
in
attack (gs, return)
end
end
val (gs, return) = attack (gs, return)
in
algorithm (gs, return)
end
end
structure Player = LayerFn(CoroLayerFn(Ripens))