-
Notifications
You must be signed in to change notification settings - Fork 0
/
board-sig.sml
177 lines (147 loc) · 6.14 KB
/
board-sig.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
signature BOARD =
sig
exception Board of string
datatype dir = E | W | SE | SW
datatype turn = CW | CCW
datatype command = D of dir | T of turn
datatype why =
(* Gracefully used all pieces *)
COMPLETE
(* Last move locked a piece, but there's no space
to place the next one. *)
| NO_SPACE
datatype status =
(* Game keeps going *)
CONTINUE
| GAMEOVER of why
(* Bad command? *)
| ERROR
datatype moveresult =
(* scored is the points scored as a result of this move;
lines is the number of lines created;
locked is whether the move caused the piece to be locked;
status tells us whether the game ended, etc. *)
M of { scored: int, lines: int, new_phrases: int,
locked: (int * int * int) option,
status: status }
val dirstring : dir -> string
val dirorder : dir * dir -> order
val turnstring : turn -> string
val turnorder : turn * turn -> order
val commandstring : command -> string
val commandorder : command * command -> order
val statusstring : status -> string
val moveresultstring : moveresult -> string
(* val statusorderorder : status -> order *)
(* The problem is always functional. *)
type problem
(* always functional; this denotes a specific piece that
can occur *)
type piece
(* Give it a json string. Assumes phrases of power from phrases.sml. *)
val fromjson : string -> problem
val fromjsonwithpower : string * string vector -> problem
(* Create a new problem which is like the given one, but with different
phrases of power. Useful for trying out different power phrase sets. *)
val setPower : problem * string vector -> problem
(* mutable version *)
type state
val piece_position : state -> int * int
val piece_angle : state -> int
(* Get the symmetry group of the piece. Only changes when the piece
changes (after lock). The symmetry group is either 1, 2, 3, or 6.
Angles can be considered equivalent modulo the symmetry group
(they produce the same set of members). *)
val piece_symmetry : state -> int
(* legal command characters *)
eqtype legalchar
(* Restart the problem, creating the initial state. Takes the seed
index to use. It's fine to have multiple outstanding states for
the same problem and seed. *)
val reset : problem * int -> state
(* Start the problem with a specific seed, which need not be
in the problem description. *)
val resetwithseed : problem * Word32.word -> state
(* Make an exact copy of the state with a new identity *)
val clone : state -> state
(* Get an arbitrary character for the command,
in case we aren't concerned with spelling
phrases of power. *)
val anychar : command -> legalchar
(* Just check that the char is legal, returning it.
Otherwise, raise an exception. *)
val legalize : char -> legalchar
(* Get the underlying char (always lowercase if a letter) *)
val forgetlegal : legalchar -> char
val getchars : command -> legalchar vector
val charcommand : legalchar -> command
(* All of the meanginful chars *)
val legalchars : legalchar vector
val islegal : char -> bool
val move : state * legalchar -> moveresult
(* Apply the move, updating the state, and then call the continuation
with the result. When the continuation returns, undo the move
(assuming it is the ONLY change to the state) and return whatever
the continuation returned. *)
val move_unwind : state * legalchar * (moveresult -> 'a) -> 'a
(* Apply many moves, updating the state, and then call the
continuation with a boolean, which is whether all moves succeeded
without GAMEOVER, ERROR, or locking. This is just a minimal
amount of feedback and we could do better by accumulating
move_results into one big one. *)
val move_unwind_many :
state * legalchar list * (bool -> 'a) -> 'a
(* Also return a function that undoes the change to the state (assuming
it is the ONLY change); this function should be called at most once. *)
val move_undo : state * legalchar -> { result: moveresult,
undo: unit -> unit }
(* Human-readable for interactivity *)
val toascii : state -> string
val powerinfostring : state -> string
(* Is the cell full/empty? Doesn't count the current active piece.
Anything outside the valid bounds of the board counts as "full." *)
val isfull : state * int * int -> bool
val isempty : state * int * int -> bool
(* Give the number of pieces that are remaining to spawn after the
current one; assumes the state is still in a Continue state. *)
val piecesleft : state -> int
(* Does this cell currently contain a member of the piece?
This does not necessarily include the pivot. *)
val ispiece : state * int * int -> bool
(* Is this the pivot of the current piece? Is not necessarily
a member of the piece. *)
val ispivot : state * int * int -> bool
val pivot : state -> int * int
val id : problem -> int
val size : problem -> int * int
val width : problem -> int
val height : problem -> int
val pieces : problem -> piece vector
val seeds : problem -> Word32.word vector
(* TODO: functional versions *)
(*
val freeze : state -> fstate
val thaw : fstate -> state
*)
val uniformize_coord : (int * int) -> (int * int)
val deuniformize_coord : (int * int) -> (int * int)
(* Count of on-off transitions within each row;
higher values are worse. Assumes nonzero width. *)
val raggedness_heuristic : state -> int
(* Classic from David *)
val simple_heuristic : state -> int
(* Assumed to be states from the same problem.
This is a probabilistic hash set that eventually
just gives false positives for everything!
The states are treated equivalent except for their
filled cells; this even includes the location of the
pivot (the expectation is that this is used for
states that are immediately after locking.) *)
type stateset
(* Makes an empty state set. Built in constant of
15485863 bits is about 2mb. *)
val empty_stateset : unit -> stateset
val insert : stateset -> state -> unit
(* With false positives *)
val contains : stateset -> state -> bool
end