summaryrefslogtreecommitdiff
path: root/apps/recorder/sokoban.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/recorder/sokoban.c')
-rw-r--r--apps/recorder/sokoban.c891
1 files changed, 0 insertions, 891 deletions
diff --git a/apps/recorder/sokoban.c b/apps/recorder/sokoban.c
deleted file mode 100644
index d28e32f430..0000000000
--- a/apps/recorder/sokoban.c
+++ /dev/null
@@ -1,891 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Eric Linenberg
11 * February 2003: Robert Hak performs a cleanup/rewrite/feature addition.
12 * Eric smiles. Bjorn cries. Linus say 'huh?'.
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "config.h"
23#include "options.h"
24
25#ifdef USE_GAMES
26
27#include <sprintf.h>
28#include "ctype.h"
29#include "sokoban.h"
30#include "lcd.h"
31#include "button.h"
32#include "kernel.h"
33#include "menu.h"
34#include "screens.h"
35#include "font.h"
36#include "file.h"
37#include "misc.h"
38#include "debug.h"
39
40#ifdef SIMULATOR
41#include <stdio.h>
42#endif
43#include <string.h>
44#include "lang.h"
45#include "sprintf.h"
46
47#define SOKOBAN_TITLE "Sokoban"
48#define SOKOBAN_TITLE_FONT 2
49
50#define LEVELS_FILE "/.rockbox/sokoban/levels.txt"
51
52#define ROWS 16
53#define COLS 20
54#define MAX_UNDOS 5
55
56#define SOKOBAN_LEVEL_SIZE (ROWS*COLS)
57
58static void init_undo(void);
59static void undo(void);
60static void add_undo(int button);
61
62static int get_level(char *level, int level_size);
63static int get_level_count(void);
64static int load_level(void);
65static void draw_level(void);
66
67static void init_boards(void);
68static void update_screen(void);
69static bool sokoban_loop(void);
70
71/* The Location, Undo and LevelInfo structs are OO-flavored.
72 * (oooh!-flavored as Schnueff puts it.) It makes more you have to know,
73 * but the overall data layout becomes more manageable. */
74
75/* We use the same three values in 2 structs. Makeing them a struct
76 * hopefully ensures that if you change things in one, the other changes
77 * as well. */
78struct LevelInfo {
79 short level;
80 short moves;
81 short boxes_to_go;
82};
83
84/* What a given location on the board looks like at a given time */
85struct Location {
86 char spot;
87 short row;
88 short col;
89};
90
91/* A single level of undo. Each undo move can affect upto,
92 * but not more then, 3 spots on the board */
93struct Undo {
94 struct LevelInfo level;
95 struct Location location[3];
96};
97
98/* Our full undo history */
99static struct UndoInfo {
100 short count; /* How many undos are there in history */
101 short current; /* Which history is the current undo */
102 struct Undo history[MAX_UNDOS];
103} undo_info;
104
105/* Our playing board */
106static struct BoardInfo {
107 char board[ROWS][COLS];
108 struct LevelInfo level;
109 struct Location player;
110 int max_level; /* How many levels do we have? */
111 int level_offset; /* Where in the level file is this level */
112 int loaded_level; /* Which level is in memory */
113} current_info;
114
115
116static void init_undo(void)
117{
118 undo_info.count = 0;
119 undo_info.current = 0;
120}
121
122static void undo(void)
123{
124 struct Undo *undo;
125 int i = 0;
126 short row, col;
127
128 if (undo_info.count == 0)
129 return;
130
131 /* Update board info */
132 undo = &undo_info.history[undo_info.current];
133
134 current_info.level = undo->level;
135 current_info.player = undo->location[0];
136
137 row = undo->location[0].row;
138 col = undo->location[0].col;
139 current_info.board[row][col] = '@';
140
141 /* Update the two other possible spots */
142 for (i = 1; i < 3; i++) {
143 if (undo->location[i].spot != '\0') {
144 row = undo->location[i].row;
145 col = undo->location[i].col;
146 current_info.board[row][col] = undo->location[i].spot;
147 undo->location[i].spot = '\0';
148 }
149 }
150
151 /* Remove this undo from the list */
152 if (undo_info.current == 0) {
153 if (undo_info.count > 1)
154 undo_info.current = MAX_UNDOS - 1;
155 } else {
156 undo_info.current--;
157 }
158
159 undo_info.count--;
160
161 return;
162}
163
164static void add_undo(int button)
165{
166 struct Undo *undo;
167 int row, col, i;
168 bool storable;
169
170 if ((button != BUTTON_LEFT) && (button != BUTTON_RIGHT) &&
171 (button != BUTTON_UP) && (button != BUTTON_DOWN))
172 return;
173
174 if (undo_info.count != 0) {
175 if (undo_info.current < (MAX_UNDOS - 1))
176 undo_info.current++;
177 else
178 undo_info.current = 0;
179 }
180
181 /* Make what follows more readable */
182 undo = &undo_info.history[undo_info.current];
183
184 /* Store our level info */
185 undo->level = current_info.level;
186
187 /* Store our player info */
188 undo->location[0] = current_info.player;
189
190 /* Now we need to store upto 2 blocks that may be affected.
191 * If player.spot is NULL, then there is no info stored
192 * for that block */
193
194 row = current_info.player.row;
195 col = current_info.player.col;
196
197 /* This must stay as _1_ because the first block (0) is the player */
198 for (i = 1; i < 3; i++) {
199 storable = true;
200
201 switch (button) {
202 case BUTTON_LEFT:
203 col--;
204 if (col < 0)
205 storable = false;
206 break;
207
208 case BUTTON_RIGHT:
209 col++;
210 if (col >= COLS)
211 storable = false;
212 break;
213
214 case BUTTON_UP:
215 row--;
216 if (row < 0)
217 storable = false;
218 break;
219
220 case BUTTON_DOWN:
221 row++;
222 if (row >= ROWS)
223 storable = false;
224 break;
225
226 default:
227 return;
228 }
229
230 if (storable) {
231 undo->location[i].col = col;
232 undo->location[i].row = row;
233 undo->location[i].spot = current_info.board[row][col];
234 } else {
235 undo->location[i].spot = '\0';
236 }
237 }
238
239 if (undo_info.count < MAX_UNDOS)
240 undo_info.count++;
241}
242
243static void init_boards(void)
244{
245 current_info.level.level = 0;
246 current_info.level.moves = 0;
247 current_info.level.boxes_to_go = 0;
248 current_info.player.row = 0;
249 current_info.player.col = 0;
250 current_info.player.spot = ' ';
251 current_info.max_level = 0;
252 current_info.level_offset = 0;
253 current_info.loaded_level = 0;
254
255 init_undo();
256}
257
258static int get_level_count(void)
259{
260 int fd = 0;
261 int len, lastlen = 0;
262 char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */
263
264 if ((fd = open(LEVELS_FILE, O_RDONLY)) < 0) {
265 splash(0, 0, true, "Unable to open %s", LEVELS_FILE);
266 return -1;
267 }
268
269 while(1) {
270 len = read_line(fd, buffer, sizeof(buffer));
271 if(len <= 0)
272 break;
273
274 /* Two short lines in a row means new level */
275 if(len < 3 && lastlen < 3)
276 current_info.max_level++;
277
278 lastlen = len;
279 }
280
281 DEBUGF("%d levels loaded\n", current_info.max_level);
282 close(fd);
283 return 0;
284}
285
286static int get_level(char *level, int level_size)
287{
288 int fd = 0, i = 0;
289 int nread = 0;
290 int count = 0;
291 int len, lastlen = 0;
292 int level_ct = 1;
293 unsigned char buffer[SOKOBAN_LEVEL_SIZE * 2];
294 bool level_found = false;
295
296 /* open file */
297 if ((fd = open(LEVELS_FILE, O_RDONLY)) < 0)
298 return -1;
299
300 /* Lets not reparse the full file if we can avoid it */
301 if (current_info.loaded_level < current_info.level.level) {
302 lseek(fd, current_info.level_offset, SEEK_SET);
303 level_ct = current_info.loaded_level;
304 }
305
306 if(current_info.level.level > 1) {
307 while(!level_found) {
308 len = read_line(fd, buffer, SOKOBAN_LEVEL_SIZE);
309 if(len <= 0) {
310 close(fd);
311 return -1;
312 }
313
314 /* Two short lines in a row means new level */
315 if(len < 3 && lastlen < 3) {
316 level_ct++;
317 if(level_ct == current_info.level.level)
318 level_found = true;
319 }
320 lastlen = len;
321 }
322 }
323
324 /* Remember the current offset */
325 current_info.level_offset = lseek(fd, 0, SEEK_CUR);
326
327 /* read a full buffer chunk from here */
328 nread = read(fd, buffer, sizeof(buffer)-1);
329 if (nread < 0)
330 return -1;
331 buffer[nread] = 0;
332
333 close(fd);
334
335 /* If we read less then a level, error */
336 if (nread < level_size)
337 return -1;
338
339 /* Load our new level */
340 for(i=0, count=0; (count < nread) && (i<level_size);) {
341 if (buffer[count] != '\n' && buffer[count] != '\r')
342 level[i++] = buffer[count];
343 count++;
344 }
345 level[i] = 0;
346
347 current_info.loaded_level = current_info.level.level;
348 return 0;
349}
350
351/* return non-zero on error */
352static int load_level(void)
353{
354 short c = 0;
355 short r = 0;
356 short i = 0;
357 char level[ROWS*COLS+1];
358 int x = 0;
359
360 current_info.player.spot=' ';
361 current_info.level.boxes_to_go = 0;
362 current_info.level.moves = 0;
363
364 if (get_level(level, sizeof(level)) != 0)
365 return -1;
366
367 i = 0;
368 for (r = 0; r < ROWS; r++) {
369 x++;
370 for (c = 0; c < COLS; c++, i++) {
371 current_info.board[r][c] = level[i];
372
373 if (current_info.board[r][c] == '.')
374 current_info.level.boxes_to_go++;
375
376 else if (current_info.board[r][c] == '@') {
377 current_info.player.row = r;
378 current_info.player.col = c;
379 }
380 }
381 }
382
383 return 0;
384}
385
386static void update_screen(void)
387{
388 short b = 0, c = 0;
389 short rows = 0, cols = 0;
390 char s[25];
391
392 short magnify = 4;
393
394 /* load the board to the screen */
395 for (rows=0 ; rows < ROWS ; rows++) {
396 for (cols = 0 ; cols < COLS ; cols++) {
397 c = cols * magnify;
398 b = rows * magnify;
399
400 switch(current_info.board[rows][cols]) {
401 case 'X': /* black space */
402 lcd_drawrect(c, b, magnify, magnify);
403 lcd_drawrect(c+1, b+1, 2, 2);
404 break;
405
406 case '#': /* this is a wall */
407 lcd_drawpixel(c, b);
408 lcd_drawpixel(c+2, b);
409 lcd_drawpixel(c+1, b+1);
410 lcd_drawpixel(c+3, b+1);
411 lcd_drawpixel(c, b+2);
412 lcd_drawpixel(c+2, b+2);
413 lcd_drawpixel(c+1, b+3);
414 lcd_drawpixel(c+3, b+3);
415 break;
416
417 case '.': /* this is a home location */
418 lcd_drawrect(c+1, b+1, 2, 2);
419 break;
420
421 case '$': /* this is a box */
422 lcd_drawrect(c, b, magnify, magnify);
423 break;
424
425 case '@': /* this is you */
426 lcd_drawline(c+1, b, c+2, b);
427 lcd_drawline(c, b+1, c+3, b+1);
428 lcd_drawline(c+1, b+2, c+2, b+2);
429
430 lcd_drawpixel(c, b+3);
431 lcd_drawpixel(c+3, b+3);
432 break;
433
434 case '%': /* this is a box on a home spot */
435 lcd_drawrect(c, b, magnify, magnify);
436 lcd_drawrect(c+1, b+1, 2, 2);
437 break;
438 }
439 }
440 }
441
442
443 snprintf(s, sizeof(s), "%d", current_info.level.level);
444 lcd_putsxy(86, 22, s);
445 snprintf(s, sizeof(s), "%d", current_info.level.moves);
446 lcd_putsxy(86, 54, s);
447
448 lcd_drawrect(80,0,32,32);
449 lcd_drawrect(80,32,32,64);
450 lcd_putsxy(81, 10, str(LANG_SOKOBAN_LEVEL));
451 lcd_putsxy(81, 42, str(LANG_SOKOBAN_MOVE));
452
453 /* print out the screen */
454 lcd_update();
455}
456
457static void draw_level(void)
458{
459 load_level();
460 lcd_clear_display();
461 update_screen();
462}
463
464static bool sokoban_loop(void)
465{
466 char new_spot;
467 bool moved = true;
468 int i = 0, button = 0;
469 short r = 0, c = 0;
470
471 current_info.level.level = 1;
472
473 load_level();
474 update_screen();
475
476 while (1) {
477 moved = true;
478
479 r = current_info.player.row;
480 c = current_info.player.col;
481
482 button = button_get(true);
483
484 add_undo(button);
485
486 switch(button)
487 {
488 case BUTTON_OFF:
489 /* get out of here */
490 return false;
491
492 case BUTTON_ON:
493 case BUTTON_ON | BUTTON_REPEAT:
494 /* this is UNDO */
495 undo();
496 lcd_clear_display();
497 update_screen();
498 moved = false;
499 break;
500
501 case BUTTON_F3:
502 case BUTTON_F3 | BUTTON_REPEAT:
503 /* increase level */
504 init_undo();
505 current_info.level.boxes_to_go=0;
506 moved = true;
507 break;
508
509 case BUTTON_F1:
510 case BUTTON_F1 | BUTTON_REPEAT:
511 /* previous level */
512 init_undo();
513 if (current_info.level.level > 1)
514 current_info.level.level--;
515
516 draw_level();
517 moved = false;
518 break;
519
520 case BUTTON_F2:
521 case BUTTON_F2 | BUTTON_REPEAT:
522 /* same level */
523 init_undo();
524 draw_level();
525 moved = false;
526 break;
527
528 case BUTTON_LEFT:
529 switch(current_info.board[r][c-1])
530 {
531 case ' ': /* if it is a blank spot */
532 case '.': /* if it is a home spot */
533 new_spot = current_info.board[r][c-1];
534 current_info.board[r][c-1] = '@';
535 current_info.board[r][c] = current_info.player.spot;
536 current_info.player.spot = new_spot;
537 break;
538
539 case '$':
540 switch(current_info.board[r][c-2])
541 {
542 case ' ': /* going from blank to blank */
543 current_info.board[r][c-2] = current_info.board[r][c-1];
544 current_info.board[r][c-1] = current_info.board[r][c];
545 current_info.board[r][c] = current_info.player.spot;
546 current_info.player.spot = ' ';
547 break;
548
549 case '.': /* going from a blank to home */
550 current_info.board[r][c-2] = '%';
551 current_info.board[r][c-1] = current_info.board[r][c];
552 current_info.board[r][c] = current_info.player.spot;
553 current_info.player.spot = ' ';
554 current_info.level.boxes_to_go--;
555 break;
556
557 default:
558 moved = false;
559 break;
560 }
561 break;
562
563 case '%':
564 switch(current_info.board[r][c-2]) {
565 case ' ': /* we are going from a home to a blank */
566 current_info.board[r][c-2] = '$';
567 current_info.board[r][c-1] = current_info.board[r][c];
568 current_info.board[r][c] = current_info.player.spot;
569 current_info.player.spot = '.';
570 current_info.level.boxes_to_go++;
571 break;
572
573 case '.': /* if we are going from a home to home */
574 current_info.board[r][c-2] = '%';
575 current_info.board[r][c-1] = current_info.board[r][c];
576 current_info.board[r][c] = current_info.player.spot;
577 current_info.player.spot = '.';
578 break;
579
580 default:
581 moved = false;
582 break;
583 }
584 break;
585
586 default:
587 moved = false;
588 break;
589 }
590
591 if (moved)
592 current_info.player.col--;
593 break;
594
595 case BUTTON_RIGHT: /* if it is a blank spot */
596 switch(current_info.board[r][c+1]) {
597 case ' ':
598 case '.': /* if it is a home spot */
599 new_spot = current_info.board[r][c+1];
600 current_info.board[r][c+1] = '@';
601 current_info.board[r][c] = current_info.player.spot;
602 current_info.player.spot = new_spot;
603 break;
604
605 case '$':
606 switch(current_info.board[r][c+2]) {
607 case ' ': /* going from blank to blank */
608 current_info.board[r][c+2] = current_info.board[r][c+1];
609 current_info.board[r][c+1] = current_info.board[r][c];
610 current_info.board[r][c] = current_info.player.spot;
611 current_info.player.spot = ' ';
612 break;
613
614 case '.': /* going from a blank to home */
615 current_info.board[r][c+2] = '%';
616 current_info.board[r][c+1] = current_info.board[r][c];
617 current_info.board[r][c] = current_info.player.spot;
618 current_info.player.spot = ' ';
619 current_info.level.boxes_to_go--;
620 break;
621
622 default:
623 moved = false;
624 break;
625 }
626 break;
627
628 case '%':
629 switch(current_info.board[r][c+2]) {
630 case ' ': /* going from a home to a blank */
631 current_info.board[r][c+2] = '$';
632 current_info.board[r][c+1] = current_info.board[r][c];
633 current_info.board[r][c] = current_info.player.spot;
634 current_info.player.spot = '.';
635 current_info.level.boxes_to_go++;
636 break;
637
638 case '.':
639 current_info.board[r][c+2] = '%';
640 current_info.board[r][c+1] = current_info.board[r][c];
641 current_info.board[r][c] = current_info.player.spot;
642 current_info.player.spot = '.';
643 break;
644
645 default:
646 moved = false;
647 break;
648 }
649 break;
650
651 default:
652 moved = false;
653 break;
654 }
655
656 if (moved)
657 current_info.player.col++;
658 break;
659
660 case BUTTON_UP:
661 switch(current_info.board[r-1][c]) {
662 case ' ': /* if it is a blank spot */
663 case '.': /* if it is a home spot */
664 new_spot = current_info.board[r-1][c];
665 current_info.board[r-1][c] = '@';
666 current_info.board[r][c] = current_info.player.spot;
667 current_info.player.spot = new_spot;
668 break;
669
670 case '$':
671 switch(current_info.board[r-2][c]) {
672 case ' ': /* going from blank to blank */
673 current_info.board[r-2][c] = current_info.board[r-1][c];
674 current_info.board[r-1][c] = current_info.board[r][c];
675 current_info.board[r][c] = current_info.player.spot;
676 current_info.player.spot = ' ';
677 break;
678
679 case '.': /* going from a blank to home */
680 current_info.board[r-2][c] = '%';
681 current_info.board[r-1][c] = current_info.board[r][c];
682 current_info.board[r][c] = current_info.player.spot;
683 current_info.player.spot = ' ';
684 current_info.level.boxes_to_go--;
685 break;
686
687 default:
688 moved = false;
689 break;
690 }
691 break;
692
693 case '%':
694 switch(current_info.board[r-2][c]) {
695 case ' ': /* we are going from a home to a blank */
696 current_info.board[r-2][c] = '$';
697 current_info.board[r-1][c] = current_info.board[r][c];
698 current_info.board[r][c] = current_info.player.spot;
699 current_info.player.spot = '.';
700 current_info.level.boxes_to_go++;
701 break;
702
703 case '.': /* if we are going from a home to home */
704 current_info.board[r-2][c] = '%';
705 current_info.board[r-1][c] = current_info.board[r][c];
706 current_info.board[r][c] = current_info.player.spot;
707 current_info.player.spot = '.';
708 break;
709
710 default:
711 moved = false;
712 break;
713 }
714 break;
715
716 default:
717 moved = false;
718 break;
719 }
720
721 if (moved)
722 current_info.player.row--;
723 break;
724
725 case BUTTON_DOWN:
726 switch(current_info.board[r+1][c]) {
727 case ' ': /* if it is a blank spot */
728 case '.': /* if it is a home spot */
729 new_spot = current_info.board[r+1][c];
730 current_info.board[r+1][c] = '@';
731 current_info.board[r][c] = current_info.player.spot;
732 current_info.player.spot = new_spot;
733 break;
734
735 case '$':
736 switch(current_info.board[r+2][c]) {
737 case ' ': /* going from blank to blank */
738 current_info.board[r+2][c] = current_info.board[r+1][c];
739 current_info.board[r+1][c] = current_info.board[r][c];
740 current_info.board[r][c] = current_info.player.spot;
741 current_info.player.spot = ' ';
742 break;
743
744 case '.': /* going from a blank to home */
745 current_info.board[r+2][c] = '%';
746 current_info.board[r+1][c] = current_info.board[r][c];
747 current_info.board[r][c] = current_info.player.spot;
748 current_info.player.spot = ' ';
749 current_info.level.boxes_to_go--;
750 break;
751
752 default:
753 moved = false;
754 break;
755 }
756 break;
757
758 case '%':
759 switch(current_info.board[r+2][c]) {
760 case ' ': /* going from a home to a blank */
761 current_info.board[r+2][c] = '$';
762 current_info.board[r+1][c] = current_info.board[r][c];
763 current_info.board[r][c] = current_info.player.spot;
764 current_info.player.spot = '.';
765 current_info.level.boxes_to_go++;
766 break;
767
768 case '.': /* going from a home to home */
769 current_info.board[r+2][c] = '%';
770 current_info.board[r+1][c] = current_info.board[r][c];
771 current_info.board[r][c] = current_info.player.spot;
772 current_info.player.spot = '.';
773 break;
774
775 default:
776 moved = false;
777 break;
778 }
779 break;
780
781 default:
782 moved = false;
783 break;
784 }
785
786 if (moved)
787 current_info.player.row++;
788 break;
789
790 case SYS_USB_CONNECTED:
791 usb_screen();
792 return true;
793
794 default:
795 moved = false;
796 break;
797 }
798
799 if (moved) {
800 current_info.level.moves++;
801 lcd_clear_display();
802 update_screen();
803 }
804
805 /* We have completed this level */
806 if (current_info.level.boxes_to_go == 0) {
807 current_info.level.level++;
808
809 /* clear undo stats */
810 init_undo();
811
812 lcd_clear_display();
813
814 if (current_info.level.level > current_info.max_level) {
815 lcd_putsxy(10, 20, str(LANG_SOKOBAN_WIN));
816
817 for (i = 0; i < 30000 ; i++) {
818 lcd_invertrect(0, 0, 111, 63);
819 lcd_update();
820
821 button = button_get(false);
822 if (button && ((button & BUTTON_REL) != BUTTON_REL))
823 break;
824 }
825
826 return false;
827 }
828
829 load_level();
830 update_screen();
831 }
832
833 } /* end while */
834
835 return false;
836}
837
838
839bool sokoban(void)
840{
841 bool result;
842 int w, h;
843 int len;
844
845 lcd_setfont(FONT_SYSFIXED);
846
847 lcd_getstringsize(SOKOBAN_TITLE, &w, &h);
848
849 /* Get horizontel centering for text */
850 len = w;
851 if (len%2 != 0)
852 len =((len+1)/2)+(w/2);
853 else
854 len /= 2;
855
856 if (h%2 != 0)
857 h = (h/2)+1;
858 else
859 h /= 2;
860
861 lcd_clear_display();
862 lcd_putsxy(LCD_WIDTH/2-len,(LCD_HEIGHT/2)-h, SOKOBAN_TITLE);
863
864 lcd_update();
865 sleep(HZ*2);
866
867 lcd_clear_display();
868
869 lcd_putsxy(3, 6, str(LANG_SOKOBAN_QUIT));
870 lcd_putsxy(3, 16, str(LANG_SOKOBAN_ON));
871 lcd_putsxy(3, 26, str(LANG_SOKOBAN_F1));
872 lcd_putsxy(3, 36, str(LANG_SOKOBAN_F2));
873 lcd_putsxy(3, 46, str(LANG_SOKOBAN_F3));
874
875 lcd_update();
876 sleep(HZ*2);
877 lcd_clear_display();
878
879 init_boards();
880
881 if (get_level_count() != 0)
882 return false;
883
884 result = sokoban_loop();
885
886 lcd_setfont(FONT_UI);
887
888 return result;
889}
890
891#endif