ohmu
·
2026-09-07
1#include <u.h>
2#include <libc.h>
3#include <draw.h>
4
5#include "sokoban.h"
6
7static int dirlist[] = { Up, Down, Left, Right, Up, Down, Left, Right, };
8static int ndir = 4;
9
10static Point
11dir2point(int dir)
12{
13 switch(dir) {
14 case Up:
15 return Pt(0, -1);
16 case Down:
17 return Pt(0, 1);
18 case Left:
19 return Pt(-1, 0);
20 case Right:
21 return Pt(1, 0);
22 }
23 return Pt(0,0);
24}
25
26int
27validpush(Point g, Step *s, Point *t)
28{
29 int i;
30 Point m;
31
32 if (s == nil)
33 return 0;
34
35 m = dir2point(s->dir);
36
37 // first test for Cargo next to us (in direction dir)
38 if (s->count > 0) {
39 g = addpt(g, m);
40 if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
41 return 0;
42 switch (level.board[g.x][g.y]) {
43 case Wall:
44 case Empty:
45 case Goal:
46 return 0;
47 }
48 }
49 // then test for enough free space for us _and_ Cargo
50 for (i=0; i < s->count; i++) {
51 g = addpt(g, m);
52 if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
53 return 0;
54 switch (level.board[g.x][g.y]) {
55 case Wall:
56 case Cargo:
57 case GoalCargo:
58 return 0;
59 }
60 }
61 if (t != nil)
62 *t = g;
63 return 1;
64}
65
66int
67isvalid(Point s, Route* r, int (*isallowed)(Point, Step*, Point*))
68{
69 Point m;
70 Step *p;
71
72 if (r == nil)
73 return 0;
74
75 m = s;
76 for (p = r->step; p < r->step + r->nstep; p++)
77 if (!isallowed(m, p, &m))
78 return 0;
79 return 1;
80}
81
82static Walk*
83newwalk(void)
84{
85 Walk *w;
86
87 w = mallocz(sizeof *w, 1);
88 if (w == nil)
89 sysfatal("cannot allocate walk");
90 return w;
91}
92
93static void
94resetwalk(Walk *w)
95{
96 Route **p;
97
98 if (w == nil || w->route == nil)
99 return;
100
101 for (p=w->route; p < w->route + w->nroute; p++)
102 freeroute(*p);
103 w->nroute = 0;
104}
105
106static void
107freewalk(Walk *w)
108{
109 if (w == nil)
110 return;
111
112 resetwalk(w);
113 if(w->route)
114 free(w->route);
115 free(w);
116}
117
118static void
119addroute(Walk *w, Route *r)
120{
121 if (w == nil || r == nil)
122 return;
123
124 if (w->beyond < w->nroute+1) {
125 w->beyond = w->nroute+1;
126 w->route = realloc(w->route, sizeof(Route*)*w->beyond);
127 }
128 if (w->route == nil)
129 sysfatal("cannot allocate route in addroute");
130 w->route[w->nroute] = r;
131 w->nroute++;
132}
133
134void
135freeroute(Route *r)
136{
137 if (r == nil)
138 return;
139 free(r->step);
140 free(r);
141}
142
143Route*
144extend(Route *rr, int dir, int count, Point dest)
145{
146 Route *r;
147
148 r = mallocz(sizeof *r, 1);
149 if (r == nil)
150 sysfatal("cannot allocate route in extend");
151 r->dest = dest;
152
153 if (count > 0) {
154 r->nstep = 1;
155 if (rr != nil)
156 r->nstep += rr->nstep;
157
158 r->step = malloc(sizeof(Step)*r->nstep);
159 if (r->step == nil)
160 sysfatal("cannot allocate step in extend");
161
162 if (rr != nil)
163 memmove(r->step, rr->step, sizeof(Step)*rr->nstep);
164
165 r->step[r->nstep-1].dir = dir;
166 r->step[r->nstep-1].count = count;
167 }
168 return r;
169}
170
171static Step*
172laststep(Route*r)
173{
174 if (r != nil && r->nstep > 0)
175 return &r->step[r->nstep-1];
176 return nil;
177}
178
179static int*
180startwithdirfromroute(Route *r, int* dl, int n)
181{
182 Step *s;
183 int *p;
184
185 if (r == nil || dl == nil)
186 return dl;
187
188 s = laststep(r);
189 if (s == nil || s->count == 0)
190 return dl;
191
192 for (p=dl; p < dl + n; p++)
193 if (*p == s->dir)
194 break;
195 return p;
196}
197
198static Route*
199bfstrydir(Route *r, int dir, Visited *v)
200{
201 Point m, p, n;
202
203 if (r == nil)
204 return nil;
205
206 m = r->dest;
207 p = dir2point(dir);
208 n = addpt(m, p);
209
210 if (ptinrect(n, Rpt(Pt(0,0), level.max)) && v->board[n.x][n.y] == 0) {
211 v->board[n.x][n.y] = 1;
212 switch (level.board[n.x][n.y]) {
213 case Empty:
214 case Goal:
215 return extend(r, dir, 1, n);
216 }
217 }
218 return nil;
219}
220
221static Route*
222bfs(Point src, Point dst, Visited *v)
223{
224 Walk *cur, *new, *tmp;
225 Route *r, **p;
226 int progress, *dirs, *dirp;
227 Point n;
228
229 if (v == nil)
230 return nil;
231
232 cur = newwalk();
233 new = newwalk();
234 v->board[src.x][src.y] = 1;
235 r = extend(nil, 0, 0, src);
236 if (eqpt(src, dst)) {
237 freewalk(cur);
238 freewalk(new);
239 return r;
240 }
241 addroute(cur, r);
242 progress = 1;
243 while (progress) {
244 progress = 0;
245 for (p=cur->route; p < cur->route + cur->nroute; p++) {
246 dirs = startwithdirfromroute(*p, dirlist, ndir);
247 for (dirp=dirs; dirp < dirs + ndir; dirp++) {
248 r = bfstrydir(*p, *dirp, v);
249 if (r != nil) {
250 progress = 1;
251 n = r->dest;
252 if (eqpt(n, dst)) {
253 freewalk(cur);
254 freewalk(new);
255 return(r);
256 }
257 addroute(new, r);
258 }
259 }
260 }
261 resetwalk(cur);
262 tmp = cur;
263 cur = new;
264 new = tmp;
265 }
266 freewalk(cur);
267 freewalk(new);
268 return nil;
269}
270
271Route*
272findroute(Point src, Point dst)
273{
274 Visited v;
275 Route* r;
276
277 memset(&v, 0, sizeof(Visited));
278 r = bfs(src, dst, &v);
279 return r;
280}
281