-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathauto-maze.sty
258 lines (243 loc) · 13.3 KB
/
auto-maze.sty
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
%% This is file `auto-maze.sty'.
%% Copyright 2021 Daiji SUZUKI
\RequirePackage{expl3, xparse, tikz}
\ProvidesExplPackage {auto-maze} {2021/12/13} {0.2.1}
{Automatic maze creation package}
\int_const:Nn \c__atmz_wall_int { \c_one_int }
\int_const:Nn \c__atmz_road_int { \c_zero_int }
\int_new:N \l__atmz_arg_width_int % arg width
\int_new:N \l__atmz_arg_height_int % arg height
\int_new:N \l__atmz_maze_width_int % ( arg width ) * 2 + 3
\int_new:N \l__atmz_maze_height_int % ( arg height ) * 2 + 3
\prop_new:N \l__atmz_maze_prop % maze data, key( i * width + j ) => ( road, wall )
\int_new:N \l__atmz_loop_i_int % loop i
\int_new:N \l__atmz_loop_j_int % loop j
\int_new:N \l__atmz_x_int % x
\int_new:N \l__atmz_y_int % y
\int_new:N \l__atmz_tmp_int % tmp
\int_new:N \l__atmz_key_int % i * width + j
\int_new:N \l__atmz_rand_int % random int
\seq_new:N \l__atmz_rand_box_seq % randint( 0, 2^18 - 1 ) * 2^12 + cell position
\bool_new:N \l__atmz_loop_i_bool % loop bool 1
\int_new:N \l__atmz_now_int % now cell position
\int_new:N \l__atmz_count_tblr_int % number of possible moves
\NewDocumentCommand \maze { o m m }
{
\IfNoValueF { #1 } { \sys_gset_rand_seed:n { #1 } }
\int_set:Nn \l__atmz_arg_width_int { #2 }
\int_set:Nn \l__atmz_arg_height_int { #3 }
\int_compare:nNnT { \l__atmz_arg_width_int } < { 2 }
{ \int_set:Nn \l__atmz_arg_width_int { 2 } }
\int_compare:nNnT { \l__atmz_arg_height_int } < { 2 }
{ \int_set:Nn \l__atmz_arg_height_int { 2 } }
\int_compare:nNnT { \l__atmz_arg_width_int } > { 64 }
{ \int_set:Nn \l__atmz_arg_width_int { 64 } }
\int_compare:nNnT { \l__atmz_arg_height_int } > { 64 }
{ \int_set:Nn \l__atmz_arg_height_int { 64 } }
\int_set:Nn \l__atmz_maze_width_int { \l__atmz_arg_width_int * 2 + 3 }
\int_set:Nn \l__atmz_maze_height_int { \l__atmz_arg_height_int * 2 + 3 }
% all wall
\int_set:Nn \l__atmz_loop_i_int { 1 }
\int_do_while:nn { \l__atmz_loop_i_int < \l__atmz_maze_height_int - 1 }
{
\int_set:Nn \l__atmz_loop_j_int { 1 }
\int_do_while:nn { \l__atmz_loop_j_int < \l__atmz_maze_width_int - 1 }
{
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_i_int * \l__atmz_maze_width_int + \l__atmz_loop_j_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_wall_int
\int_incr:N \l__atmz_loop_j_int
}
\int_incr:N \l__atmz_loop_i_int
}
% road frame
\int_set:Nn \l__atmz_loop_i_int { 0 }
\int_do_while:nn { \l__atmz_loop_i_int < \l__atmz_maze_height_int }
{
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_i_int * \l__atmz_maze_width_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_road_int
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_i_int * \l__atmz_maze_width_int + \l__atmz_maze_width_int - 1 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_road_int
\int_incr:N \l__atmz_loop_i_int
}
\int_set:Nn \l__atmz_loop_j_int { 0 }
\int_do_while:nn { \l__atmz_loop_j_int < \l__atmz_maze_width_int }
{
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_j_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_road_int
\int_set:Nn \l__atmz_key_int { ( \l__atmz_maze_height_int - 1 ) * \l__atmz_maze_width_int + \l__atmz_loop_j_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_road_int
\int_incr:N \l__atmz_loop_j_int
}
% start
\int_set:Nn \l__atmz_now_int { \fp_eval:n { randint( 0, \l__atmz_arg_width_int * \l__atmz_arg_height_int - 1 ) } }
\int_set:Nn \l__atmz_x_int { ( \int_mod:nn { \l__atmz_now_int } { \l__atmz_arg_width_int } ) * 2 + 2 }
\int_set:Nn \l__atmz_y_int { \fp_eval:n { floor( \l__atmz_now_int / \l__atmz_arg_width_int ) } * 2 + 2 }
\int_set:Nn \l__atmz_key_int { \l__atmz_y_int * \l__atmz_maze_width_int + \l__atmz_x_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_key_int \c__atmz_road_int
% main loop
\bool_set_true:N \l__atmz_loop_i_bool
\bool_do_while:Nn \l__atmz_loop_i_bool
{
% calculate key
\int_set:Nn \l__atmz_x_int { ( \int_mod:nn { \l__atmz_now_int } { \l__atmz_arg_width_int } ) * 2 + 2 }
\int_set:Nn \l__atmz_y_int { \fp_eval:n { floor( \l__atmz_now_int / \l__atmz_arg_width_int ) } * 2 + 2 }
\int_set:Nn \l__atmz_key_int { \l__atmz_y_int * \l__atmz_maze_width_int + \l__atmz_x_int }
% count number of possible moves
\int_set:Nn \l__atmz_count_tblr_int { 0 }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - \l__atmz_maze_width_int * 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{ \int_incr:N \l__atmz_count_tblr_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + \l__atmz_maze_width_int * 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{ \int_incr:N \l__atmz_count_tblr_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{ \int_incr:N \l__atmz_count_tblr_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{ \int_incr:N \l__atmz_count_tblr_int }
% dig
\int_compare:nNnF { \l__atmz_count_tblr_int } = { 0 }
{
\int_set:Nn \l__atmz_rand_int { \fp_eval:n { randint( 1, \l__atmz_count_tblr_int ) } }
\int_compare:nNnT { \l__atmz_rand_int } = { 1 }
{
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - \l__atmz_maze_width_int * 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnTF { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{
\int_set:Nn \l__atmz_now_int { \l__atmz_now_int - \l__atmz_arg_width_int }
\int_set:Nn \l__atmz_rand_int { \fp_eval:n { randint( 0, 262143 ) } * 4096 + \l__atmz_now_int }
\seq_put_right:Nx \l__atmz_rand_box_seq { \int_use:N \l__atmz_rand_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - \l__atmz_maze_width_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - \l__atmz_maze_width_int * 2 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
}
{ \int_incr:N \l__atmz_rand_int }
}
\int_compare:nNnT { \l__atmz_rand_int } = { 2 }
{
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + \l__atmz_maze_width_int * 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnTF { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{
\int_set:Nn \l__atmz_now_int { \l__atmz_now_int + \l__atmz_arg_width_int }
\int_set:Nn \l__atmz_rand_int { \fp_eval:n { randint( 0, 262143 ) } * 4096 + \l__atmz_now_int }
\seq_put_right:Nx \l__atmz_rand_box_seq { \int_use:N \l__atmz_rand_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + \l__atmz_maze_width_int }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + \l__atmz_maze_width_int * 2 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
}
{ \int_incr:N \l__atmz_rand_int }
}
\int_compare:nNnT { \l__atmz_rand_int } = { 3 }
{
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - 2 }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_tmp_int \l__atmz_maze_value_tl
\int_compare:nNnTF { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{
\int_set:Nn \l__atmz_now_int { \l__atmz_now_int - 1 }
\int_set:Nn \l__atmz_rand_int { \fp_eval:n { randint( 0, 262143 ) } * 4096 + \l__atmz_now_int }
\seq_put_right:Nx \l__atmz_rand_box_seq { \int_use:N \l__atmz_rand_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - 1 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int - 2 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
}
{ \int_incr:N \l__atmz_rand_int }
}
\int_compare:nNnT { \l__atmz_rand_int } = { 4 }
{
\int_set:Nn \l__atmz_now_int { \l__atmz_now_int + 1 }
\int_set:Nn \l__atmz_rand_int { \fp_eval:n { randint( 0, 262143 ) } * 4096 + \l__atmz_now_int }
\seq_put_right:Nx \l__atmz_rand_box_seq { \int_use:N \l__atmz_rand_int }
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + 1 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
\int_set:Nn \l__atmz_tmp_int { \l__atmz_key_int + 2 }
\prop_put:NVV \l__atmz_maze_prop \l__atmz_tmp_int \c__atmz_road_int
}
}
% find now
\int_compare:nNnT { \l__atmz_count_tblr_int } = { 0 }
{
\seq_if_empty:NTF \l__atmz_rand_box_seq
{
\bool_set_false:N \l__atmz_loop_i_bool
}
{
\seq_sort:Nn \l__atmz_rand_box_seq
{
\int_compare:nNnTF { ##1 } < { ##2 }
{ \sort_return_swapped: }
{ \sort_return_same: }
}
\seq_pop_left:NN \l__atmz_rand_box_seq \l__atmz_rand_box_left_tl
\int_set:Nn \l__atmz_now_int { \int_mod:nn { \l__atmz_rand_box_left_tl } { 4096 } }
}
}
}
% output
\tl_set:Nn \l__atmz_maze_tl { }
\tl_put_right:Nn \l__atmz_maze_tl { \begin{tikzpicture}[x=8pt, y=8pt, line~width=1pt, line~cap=round] }
\int_set:Nn \l__atmz_loop_i_int { 2 }
\int_do_while:nn { \l__atmz_loop_i_int < \l__atmz_maze_height_int - 2 }
{
\int_compare:nNnTF { \int_mod:nn { \l__atmz_loop_i_int } { 2 } } = { 0 }
{
\int_set:Nn \l__atmz_loop_j_int { 3 }
\int_do_while:nn { \l__atmz_loop_j_int < \l__atmz_maze_width_int - 2 }
{
\int_set:Nn \l__atmz_x_int { \l__atmz_loop_j_int / 2 }
\int_set:Nn \l__atmz_y_int { \l__atmz_loop_i_int / 2 }
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_i_int * \l__atmz_maze_width_int + \l__atmz_loop_j_int }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_key_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{
\tl_put_right:Nx \l__atmz_maze_tl
{
\exp_not:N \draw (\int_use:N \l__atmz_x_int, \int_use:N \l__atmz_y_int)--++(0, 1);
}
}
\int_set:Nn \l__atmz_loop_j_int { \l__atmz_loop_j_int + 2 }
}
}
{
\int_set:Nn \l__atmz_loop_j_int { 0 }
\int_do_while:nn { \l__atmz_loop_j_int < \l__atmz_maze_width_int - 2 }
{
\int_set:Nn \l__atmz_x_int { \l__atmz_loop_j_int / 2 }
\int_set:Nn \l__atmz_y_int { \l__atmz_loop_i_int / 2 }
\int_set:Nn \l__atmz_key_int { \l__atmz_loop_i_int * \l__atmz_maze_width_int + \l__atmz_loop_j_int }
\prop_get:NVN \l__atmz_maze_prop \l__atmz_key_int \l__atmz_maze_value_tl
\int_compare:nNnT { \l__atmz_maze_value_tl } = { \c__atmz_wall_int }
{
\tl_put_right:Nx \l__atmz_maze_tl
{
\exp_not:N \draw (\int_use:N \l__atmz_x_int, \int_use:N \l__atmz_y_int)--++(1, 0);
}
}
\int_set:Nn \l__atmz_loop_j_int { \l__atmz_loop_j_int + 2 }
}
}
\int_incr:N \l__atmz_loop_i_int
}
\int_set:Nn \l__atmz_x_int { ( \l__atmz_maze_width_int - 4 ) / 2 }
\int_set:Nn \l__atmz_y_int { ( \l__atmz_maze_height_int - 4 ) / 2 }
\tl_put_right:Nx \l__atmz_maze_tl
{
\exp_not:N \draw (1, \int_use:N \l__atmz_y_int + 1)--++(\int_use:N \l__atmz_x_int, 0);
\exp_not:N \draw (1, 1)--++(\int_use:N \l__atmz_x_int, 0);
\exp_not:N \draw (1, 1)--++(0, \int_use:N \l__atmz_y_int - 1);
\exp_not:N \draw (\int_use:N \l__atmz_x_int + 1, 2)--++(0, \int_use:N \l__atmz_y_int - 1);
}
\tl_put_right:Nn \l__atmz_maze_tl { \end{tikzpicture} }
\tl_use:N \l__atmz_maze_tl
\prop_clear:N \l__atmz_maze_prop
\seq_clear:N \l__atmz_rand_box_seq
}