Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

puzzles: add Slide and Sokoban.

This enables two of the "unfinished" puzzles. Slide requires a new "sticky
mouse mode" to enable dragging. The help system is disabled for these
puzzles, since they lack manual chapters.

Group is currently unplayable due to lack of request_keys() support, which
will need to be added upstream. Separate fails to draw anything.

Change-Id: I7bcff3679ac5b10b0f39c5eaa19a36b4b1fe8d53

+9698 -26
+1
apps/plugins/puzzles/SOURCES
··· 3 3 rbwrappers.c 4 4 rbmalloc.c 5 5 lz4tiny.c 6 + dummy/nullhelp.c 6 7 7 8 /* puzzles core sources */ 8 9 src/combi.c
+2 -7
apps/plugins/puzzles/SOURCES.games
··· 35 35 src/unequal.c 36 36 src/unruly.c 37 37 src/untangle.c 38 - 39 - /* Disabled for now. Fix puzzles.make and CATEGORIES to accomodate these. */ 40 - /* The help system would also need to be patched to compile these. */ 41 - /*src/unfinished/group.c*/ 42 - /*src/unfinished/separate.c*/ 43 - /*src/unfinished/slide.c*/ 44 - /*src/unfinished/sokoban.c*/ 38 + src/unfinished/slide.c 39 + src/unfinished/sokoban.c 45 40 46 41 /* no c200v2 */ 47 42 #if PLUGIN_BUFFER_SIZE > 0x14000
+1
apps/plugins/puzzles/SOURCES.rockbox
··· 2 2 rbwrappers.c 3 3 rbmalloc.c 4 4 lz4tiny.c 5 + dummy/nullhelp.c
+1
apps/plugins/puzzles/compress.c
··· 158 158 printf("};\n\n"); 159 159 printf("const unsigned short help_text_len = %d;\n", help_text_len); 160 160 printf("const unsigned short help_text_words = %d;\n", word_idx); 161 + printf("const bool help_valid = true;\n"); 161 162 162 163 return 0; 163 164 }
+8
apps/plugins/puzzles/dummy/nullhelp.c
··· 1 + #include "help.h" 2 + 3 + const char help_text[] __attribute__((weak)) = ""; 4 + const char quick_help_text[] __attribute__((weak)) = ""; 5 + const unsigned short help_text_len __attribute__((weak)) = 0, quick_help_text_len __attribute__((weak)) = 0, help_text_words __attribute__((weak)) = 0; 6 + struct style_text help_text_style[] __attribute__((weak)) = {}; 7 + 8 + const bool help_valid __attribute__((weak)) = false;
+4
apps/plugins/puzzles/help.h
··· 1 + #include <stdbool.h> 2 + 1 3 #ifdef ROCKBOX 2 4 #include "lib/display_text.h" 3 5 #endif ··· 12 14 #if defined(ROCKBOX) 13 15 extern struct style_text help_text_style[]; 14 16 #endif 17 + 18 + extern const bool help_valid;
+1
apps/plugins/puzzles/help/blackbox.c
··· 340 340 341 341 const unsigned short help_text_len = 5480; 342 342 const unsigned short help_text_words = 1016; 343 + const bool help_valid = true; 343 344 const char quick_help_text[] = "Find the hidden balls in the box by bouncing laser beams off them.";
+1
apps/plugins/puzzles/help/bridges.c
··· 358 358 359 359 const unsigned short help_text_len = 5613; 360 360 const unsigned short help_text_words = 1026; 361 + const bool help_valid = true; 361 362 const char quick_help_text[] = "Connect all the islands with a network of bridges.";
+1
apps/plugins/puzzles/help/cube.c
··· 166 166 167 167 const unsigned short help_text_len = 2071; 168 168 const unsigned short help_text_words = 386; 169 + const bool help_valid = true; 169 170 const char quick_help_text[] = "Pick up all the blue squares by rolling the cube over them.";
+1
apps/plugins/puzzles/help/dominosa.c
··· 176 176 177 177 const unsigned short help_text_len = 2299; 178 178 const unsigned short help_text_words = 401; 179 + const bool help_valid = true; 179 180 const char quick_help_text[] = "Tile the rectangle with a full set of dominoes.";
+1
apps/plugins/puzzles/help/fifteen.c
··· 152 152 153 153 const unsigned short help_text_len = 1927; 154 154 const unsigned short help_text_words = 353; 155 + const bool help_valid = true; 155 156 const char quick_help_text[] = "Slide the tiles around to arrange them into order.";
+1
apps/plugins/puzzles/help/filling.c
··· 142 142 143 143 const unsigned short help_text_len = 1821; 144 144 const unsigned short help_text_words = 328; 145 + const bool help_valid = true; 145 146 const char quick_help_text[] = "Mark every square with the area of its containing region.";
+1
apps/plugins/puzzles/help/flip.c
··· 131 131 132 132 const unsigned short help_text_len = 1539; 133 133 const unsigned short help_text_words = 299; 134 + const bool help_valid = true; 134 135 const char quick_help_text[] = "Flip groups of squares to light them all up at once.";
+1
apps/plugins/puzzles/help/flood.c
··· 182 182 183 183 const unsigned short help_text_len = 2395; 184 184 const unsigned short help_text_words = 452; 185 + const bool help_valid = true; 185 186 const char quick_help_text[] = "Turn the grid the same colour in as few flood fills as possible.";
+1
apps/plugins/puzzles/help/galaxies.c
··· 208 208 209 209 const unsigned short help_text_len = 2766; 210 210 const unsigned short help_text_words = 498; 211 + const bool help_valid = true; 211 212 const char quick_help_text[] = "Divide the grid into rotationally symmetric regions each centred on a dot.";
+1
apps/plugins/puzzles/help/guess.c
··· 238 238 239 239 const unsigned short help_text_len = 3506; 240 240 const unsigned short help_text_words = 650; 241 + const bool help_valid = true; 241 242 const char quick_help_text[] = "Guess the hidden combination of colours.";
+1
apps/plugins/puzzles/help/inertia.c
··· 179 179 180 180 const unsigned short help_text_len = 2286; 181 181 const unsigned short help_text_words = 431; 182 + const bool help_valid = true; 182 183 const char quick_help_text[] = "Collect all the gems without running into any of the mines.";
+1
apps/plugins/puzzles/help/keen.c
··· 260 260 261 261 const unsigned short help_text_len = 3969; 262 262 const unsigned short help_text_words = 762; 263 + const bool help_valid = true; 263 264 const char quick_help_text[] = "Complete the latin square in accordance with the arithmetic clues.";
+1
apps/plugins/puzzles/help/lightup.c
··· 191 191 192 192 const unsigned short help_text_len = 2549; 193 193 const unsigned short help_text_words = 468; 194 + const bool help_valid = true; 194 195 const char quick_help_text[] = "Place bulbs to light up all the squares.";
+1
apps/plugins/puzzles/help/loopy.c
··· 264 264 265 265 const unsigned short help_text_len = 3584; 266 266 const unsigned short help_text_words = 660; 267 + const bool help_valid = true; 267 268 const char quick_help_text[] = "Draw a single closed loop, given clues about number of adjacent edges.";
+1
apps/plugins/puzzles/help/magnets.c
··· 190 190 191 191 const unsigned short help_text_len = 2522; 192 192 const unsigned short help_text_words = 439; 193 + const bool help_valid = true; 193 194 const char quick_help_text[] = "Place magnets to satisfy the clues and avoid like poles touching.";
+1
apps/plugins/puzzles/help/map.c
··· 271 271 272 272 const unsigned short help_text_len = 3752; 273 273 const unsigned short help_text_words = 686; 274 + const bool help_valid = true; 274 275 const char quick_help_text[] = "Colour the map so that adjacent regions are never the same colour.";
+1
apps/plugins/puzzles/help/mines.c
··· 260 260 261 261 const unsigned short help_text_len = 3814; 262 262 const unsigned short help_text_words = 732; 263 + const bool help_valid = true; 263 264 const char quick_help_text[] = "Find all the mines without treading on any of them.";
+1
apps/plugins/puzzles/help/mosaic.c
··· 147 147 148 148 const unsigned short help_text_len = 1673; 149 149 const unsigned short help_text_words = 285; 150 + const bool help_valid = true; 150 151 const char quick_help_text[] = "Fill in the grid given clues about number of nearby black squares.";
+1
apps/plugins/puzzles/help/net.c
··· 297 297 298 298 const unsigned short help_text_len = 3919; 299 299 const unsigned short help_text_words = 689; 300 + const bool help_valid = true; 300 301 const char quick_help_text[] = "Rotate each tile to reassemble the network.";
+1
apps/plugins/puzzles/help/netslide.c
··· 58 58 59 59 const unsigned short help_text_len = 546; 60 60 const unsigned short help_text_words = 99; 61 + const bool help_valid = true; 61 62 const char quick_help_text[] = "Slide a row at a time to reassemble the network.";
+1
apps/plugins/puzzles/help/palisade.c
··· 140 140 141 141 const unsigned short help_text_len = 1672; 142 142 const unsigned short help_text_words = 285; 143 + const bool help_valid = true; 143 144 const char quick_help_text[] = "Divide the grid into equal-sized areas in accordance with the clues.";
+1
apps/plugins/puzzles/help/pattern.c
··· 168 168 169 169 const unsigned short help_text_len = 2167; 170 170 const unsigned short help_text_words = 389; 171 + const bool help_valid = true; 171 172 const char quick_help_text[] = "Fill in the pattern in the grid, given only the lengths of runs of black squares.";
+1
apps/plugins/puzzles/help/pearl.c
··· 249 249 250 250 const unsigned short help_text_len = 3598; 251 251 const unsigned short help_text_words = 659; 252 + const bool help_valid = true; 252 253 const char quick_help_text[] = "Draw a single closed loop, given clues about corner and straight squares.";
+1
apps/plugins/puzzles/help/pegs.c
··· 148 148 149 149 const unsigned short help_text_len = 1734; 150 150 const unsigned short help_text_words = 326; 151 + const bool help_valid = true; 151 152 const char quick_help_text[] = "Jump pegs over each other to remove all but one.";
+1
apps/plugins/puzzles/help/range.c
··· 170 170 171 171 const unsigned short help_text_len = 2223; 172 172 const unsigned short help_text_words = 395; 173 + const bool help_valid = true; 173 174 const char quick_help_text[] = "Place black squares to limit the visible distance from each numbered cell.";
+1
apps/plugins/puzzles/help/rect.c
··· 258 258 259 259 const unsigned short help_text_len = 3536; 260 260 const unsigned short help_text_words = 603; 261 + const bool help_valid = true; 261 262 const char quick_help_text[] = "Divide the grid into rectangles with areas equal to the numbers.";
+1
apps/plugins/puzzles/help/samegame.c
··· 188 188 189 189 const unsigned short help_text_len = 2492; 190 190 const unsigned short help_text_words = 445; 191 + const bool help_valid = true; 191 192 const char quick_help_text[] = "Clear the grid by removing touching groups of the same colour squares.";
+1
apps/plugins/puzzles/help/signpost.c
··· 222 222 223 223 const unsigned short help_text_len = 3255; 224 224 const unsigned short help_text_words = 595; 225 + const bool help_valid = true; 225 226 const char quick_help_text[] = "Connect the squares into a path following the arrows.";
+1
apps/plugins/puzzles/help/singles.c
··· 149 149 150 150 const unsigned short help_text_len = 1780; 151 151 const unsigned short help_text_words = 309; 152 + const bool help_valid = true; 152 153 const char quick_help_text[] = "Black out the right set of duplicate numbers.";
+1
apps/plugins/puzzles/help/sixteen.c
··· 197 197 198 198 const unsigned short help_text_len = 2553; 199 199 const unsigned short help_text_words = 454; 200 + const bool help_valid = true; 200 201 const char quick_help_text[] = "Slide a row at a time to arrange the tiles into order.";
+1
apps/plugins/puzzles/help/slant.c
··· 199 199 200 200 const unsigned short help_text_len = 2582; 201 201 const unsigned short help_text_words = 474; 202 + const bool help_valid = true; 202 203 const char quick_help_text[] = "Draw a maze of slanting lines that matches the clues.";
+1
apps/plugins/puzzles/help/solo.c
··· 383 383 384 384 const unsigned short help_text_len = 6259; 385 385 const unsigned short help_text_words = 1153; 386 + const bool help_valid = true; 386 387 const char quick_help_text[] = "Fill in the grid so that each row, column and square block contains one of every digit.";
+1
apps/plugins/puzzles/help/tents.c
··· 163 163 164 164 const unsigned short help_text_len = 2158; 165 165 const unsigned short help_text_words = 401; 166 + const bool help_valid = true; 166 167 const char quick_help_text[] = "Place a tent next to each tree.";
+1
apps/plugins/puzzles/help/towers.c
··· 263 263 264 264 const unsigned short help_text_len = 3906; 265 265 const unsigned short help_text_words = 732; 266 + const bool help_valid = true; 266 267 const char quick_help_text[] = "Complete the latin square of towers in accordance with the clues.";
+1
apps/plugins/puzzles/help/tracks.c
··· 150 150 151 151 const unsigned short help_text_len = 1881; 152 152 const unsigned short help_text_words = 337; 153 + const bool help_valid = true; 153 154 const char quick_help_text[] = "Fill in the railway track according to the clues.";
+1
apps/plugins/puzzles/help/twiddle.c
··· 205 205 206 206 const unsigned short help_text_len = 2945; 207 207 const unsigned short help_text_words = 549; 208 + const bool help_valid = true; 208 209 const char quick_help_text[] = "Rotate the tiles around themselves to arrange them into order.";
+1
apps/plugins/puzzles/help/undead.c
··· 248 248 249 249 const unsigned short help_text_len = 3574; 250 250 const unsigned short help_text_words = 660; 251 + const bool help_valid = true; 251 252 const char quick_help_text[] = "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.";
+1
apps/plugins/puzzles/help/unequal.c
··· 257 257 258 258 const unsigned short help_text_len = 3954; 259 259 const unsigned short help_text_words = 731; 260 + const bool help_valid = true; 260 261 const char quick_help_text[] = "Complete the latin square in accordance with the > signs.";
+1
apps/plugins/puzzles/help/unruly.c
··· 145 145 146 146 const unsigned short help_text_len = 1707; 147 147 const unsigned short help_text_words = 306; 148 + const bool help_valid = true; 148 149 const char quick_help_text[] = "Fill in the black and white grid to avoid runs of three.";
+1
apps/plugins/puzzles/help/untangle.c
··· 97 97 98 98 const unsigned short help_text_len = 974; 99 99 const unsigned short help_text_words = 174; 100 + const bool help_valid = true; 100 101 const char quick_help_text[] = "Reposition the points so that the lines do not cross.";
+9
apps/plugins/puzzles/puzzles.make
··· 25 25 PUZZLES_ROCKS = $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock))) 26 26 27 27 OTHER_SRC += $(PUZZLES_SRC) 28 + OTHER_INC += -I$(PUZZLES_SRCDIR)/src -I $(PUZZLES_SRCDIR) 29 + 28 30 ROCKS += $(PUZZLES_ROCKS) 29 31 30 32 PUZZLES_OPTIMIZE = -O2 ··· 43 45 -fdata-sections -w -Wl,--gc-sections 44 46 45 47 $(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/%.o $(PUZZLES_OBJDIR)/help/%.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB) 48 + $(call PRINTS,LD $(@F))$(CC) $(PLUGINFLAGS) -o $(PUZZLES_OBJDIR)/$*.elf \ 49 + $(filter %.o, $^) \ 50 + $(filter %.a, $+) \ 51 + -lgcc $(filter-out -Wl%.map, $(PLUGINLDFLAGS)) -Wl,-Map,$(PUZZLES_OBJDIR)/src/$*.map 52 + $(SILENT)$(call objcopy,$(PUZZLES_OBJDIR)/$*.elf,$@) 53 + 54 + $(PUZZLES_OBJDIR)/sgt-%.rock: $(PUZZLES_OBJDIR)/src/unfinished/%.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB) 46 55 $(call PRINTS,LD $(@F))$(CC) $(PLUGINFLAGS) -o $(PUZZLES_OBJDIR)/$*.elf \ 47 56 $(filter %.o, $^) \ 48 57 $(filter %.a, $+) \
+5 -10
apps/plugins/puzzles/resync.sh
··· 30 30 echo "[1/5] Removing current src/ directory" 31 31 rm -rf src 32 32 echo "[2/5] Copying new sources" 33 - mkdir src 34 - cp -r "$1"/{*.h,puzzles.but,LICENCE,README,CMakeLists.txt} src 33 + mkdir -p src/unfinished 34 + cp -r "$1"/{*.h,puzzles.but,LICENCE,README,CMakeLists.txt,unfinished} src 35 35 36 36 # Parse out definitions of core, core_obj, and common from 37 37 # CMakeLists. Extract the .c filenames, except malloc.c, and store ··· 46 46 SRC="$(cat SOURCES.games SOURCES.core | sed 's/src\///' | tr '\n' ' ' | head -c-1) loopy.c pearl.c solo.c" 47 47 echo "Detected sources:" $SRC 48 48 pushd "$1" > /dev/null 49 - cp $SRC "$ROOT"/src 49 + cp -r $SRC "$ROOT"/src 50 50 popd > /dev/null 51 51 52 - cat <<EOF >> SOURCES.games 52 + cat src/unfinished/CMakeLists.txt | awk '/puzzle\(/{p=1} p{print} /\)/{p=0}' | grep -Eo "\(.*$" | tr -dc "a-z\n" | awk '{print "src/unfinished/"$0".c"}' | grep -v "group" | grep -v "separate" >> SOURCES.games 53 53 54 - /* Disabled for now. Fix puzzles.make and CATEGORIES to accomodate these. */ 55 - /* The help system would also need to be patched to compile these. */ 56 - /*src/unfinished/group.c*/ 57 - /*src/unfinished/separate.c*/ 58 - /*src/unfinished/slide.c*/ 59 - /*src/unfinished/sokoban.c*/ 54 + cat <<EOF >> SOURCES.games 60 55 61 56 /* no c200v2 */ 62 57 #if PLUGIN_BUFFER_SIZE > 0x14000
+47 -9
apps/plugins/puzzles/rockbox.c
··· 322 322 static bool clipped = false, zoom_enabled = false, view_mode = true, mouse_mode = false; 323 323 324 324 static int mouse_x, mouse_y; 325 + static bool mouse_dragging = false; /* for sticky mode only */ 325 326 326 327 extern bool audiobuf_available; /* defined in rbmalloc.c */ 327 328 ··· 346 347 bool ignore_repeats; /* ignore repeated button events (currently in all games but Untangle) */ 347 348 bool rclick_on_hold; /* if in mouse mode, send right-click on long-press of select */ 348 349 bool numerical_chooser; /* repurpose select to activate a numerical chooser */ 350 + bool sticky_mouse; /* if mouse left button should be persistent and toggled on/off */ 349 351 } input_settings; 350 352 351 353 static bool accept_input = true; ··· 748 750 fatal("bad color %d", n); 749 751 return; 750 752 } 751 - rb->lcd_set_foreground(colors[n]); 753 + if(colors) 754 + rb->lcd_set_foreground(colors[n]); 752 755 } 753 756 754 757 /* clipping is implemented through viewports and offsetting ··· 1284 1287 rb->lcd_setfont(cur_font = FONT_UI); 1285 1288 rb->lcd_getstringsize(str, &w, &h); 1286 1289 1287 - rb->lcd_set_foreground(BG_COLOR); 1290 + 1291 + rb->lcd_set_foreground(colors ? colors[0] : BG_COLOR); 1288 1292 rb->lcd_fillrect(0, LCD_HEIGHT - h, clear_first ? LCD_WIDTH : w, h); 1289 1293 1290 1294 rb->lcd_set_drawmode(DRMODE_FG); ··· 1682 1686 LOGF("sending left click"); 1683 1687 send_click(LEFT_BUTTON, true); /* right-click is handled earlier */ 1684 1688 } 1685 - } 1686 - else 1687 - { 1689 + } else if(input_settings.sticky_mouse) { 1690 + if(pressed & BTN_FIRE) { 1691 + send_click(LEFT_BUTTON, false); 1692 + accept_input = false; 1693 + mouse_dragging = !mouse_dragging; 1694 + } else if(mouse_dragging) { 1695 + send_click(LEFT_DRAG, false); 1696 + } else { 1697 + send_click(LEFT_RELEASE, false); 1698 + } 1699 + } else { 1688 1700 if(pressed & BTN_FIRE) { 1689 1701 send_click(LEFT_BUTTON, false); 1690 1702 accept_input = false; ··· 2482 2494 2483 2495 static void quick_help(void) 2484 2496 { 2497 + #ifndef NO_HELP_TEXT 2485 2498 #if defined(FOR_REAL) && defined(DEBUG_MENU) 2486 2499 if(++help_times >= 5) 2487 2500 { ··· 2492 2505 2493 2506 rb->splash(0, quick_help_text); 2494 2507 rb->button_get(true); 2495 - return; 2508 + #endif 2496 2509 } 2497 2510 2498 2511 static void full_help(const char *name) 2499 2512 { 2513 + #ifndef NO_HELP_TEXT 2500 2514 unsigned old_bg = rb->lcd_get_background(); 2501 2515 2502 2516 bool orig_clipped = clipped; ··· 2551 2565 2552 2566 if(orig_clipped) 2553 2567 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height); 2568 + #endif 2554 2569 } 2555 2570 2556 2571 static void init_default_settings(void) ··· 2701 2716 if(!midend_which_game(me)->can_solve) 2702 2717 return ACTION_EXIT_MENUITEM; 2703 2718 break; 2719 + case 7: 2720 + case 8: 2721 + if(!help_valid) 2722 + return ACTION_EXIT_MENUITEM; 2723 + break; 2704 2724 case 9: 2705 2725 if(audiobuf_available) 2706 2726 break; ··· 2751 2771 rb->lcd_set_viewport(NULL); 2752 2772 rb->lcd_set_backdrop(NULL); 2753 2773 rb->lcd_set_foreground(LCD_BLACK); 2754 - rb->lcd_set_background(BG_COLOR); 2774 + rb->lcd_set_background(colors ? colors[0] : BG_COLOR); 2755 2775 } 2756 2776 2757 2777 /* Make a new game, but tell the user through a splash so they don't ··· 2876 2896 break; 2877 2897 } 2878 2898 } 2879 - rb->lcd_set_background(BG_COLOR); 2899 + rb->lcd_set_background(colors ? colors[0] : BG_COLOR); 2880 2900 rb->lcd_clear_display(); 2881 2901 midend_force_redraw(me); 2882 2902 rb->lcd_update(); ··· 2923 2943 float *floatcolors = midend_colors(me, &ncolors); 2924 2944 2925 2945 /* convert them to packed RGB */ 2946 + sfree(colors); 2926 2947 colors = smalloc(ncolors * sizeof(unsigned)); 2927 2948 unsigned *ptr = colors; 2928 2949 float *floatptr = floatcolors; ··· 3007 3028 static const char *no_rclick_on_hold[] = { 3008 3029 "Map", 3009 3030 "Signpost", 3031 + "Slide", 3010 3032 "Untangle", 3011 3033 NULL 3012 3034 }; ··· 3015 3037 3016 3038 static const char *mouse_games[] = { 3017 3039 "Loopy", 3040 + "Slide", 3018 3041 NULL 3019 3042 }; 3020 3043 3021 3044 mouse_mode = string_in_list(name, mouse_games); 3022 3045 3046 + static const char *sticky_mouse_games[] = { 3047 + "Map", 3048 + "Signpost", 3049 + "Slide", 3050 + "Untangle", 3051 + }; 3052 + 3053 + input_settings.sticky_mouse = string_in_list(name, sticky_mouse_games); 3054 + 3023 3055 static const char *number_chooser_games[] = { 3024 3056 "Filling", 3025 3057 "Keen", ··· 3312 3344 if(!load_success) 3313 3345 return ACTION_EXIT_MENUITEM; 3314 3346 break; 3347 + case 2: 3315 3348 case 3: 3316 - break; 3349 + if(!help_valid) 3350 + return ACTION_EXIT_MENUITEM; 3351 + break; 3317 3352 case 4: 3318 3353 if(audiobuf_available) 3319 3354 break; ··· 3476 3511 /* quit without saving */ 3477 3512 midend_free(me); 3478 3513 sfree(colors); 3514 + colors = NULL; 3479 3515 return; 3480 3516 case -3: 3481 3517 /* save and quit */ 3482 3518 save_game(); 3483 3519 midend_free(me); 3484 3520 sfree(colors); 3521 + colors = NULL; 3485 3522 return; 3486 3523 default: 3487 3524 break; ··· 3511 3548 rb->yield(); 3512 3549 } 3513 3550 sfree(colors); 3551 + colors = NULL; 3514 3552 } 3515 3553 } 3516 3554
+31
apps/plugins/puzzles/src/unfinished/CMakeLists.txt
··· 1 + puzzle(group 2 + DISPLAYNAME "Group" 3 + DESCRIPTION "Group theory puzzle" 4 + OBJECTIVE "Complete the unfinished Cayley table of a group.") 5 + solver(group ${CMAKE_SOURCE_DIR}/latin.c) 6 + 7 + puzzle(separate 8 + DISPLAYNAME "Separate" 9 + DESCRIPTION "Rectangle-dividing puzzle" 10 + OBJECTIVE "Partition the grid into regions containing one of each letter.") 11 + 12 + puzzle(slide 13 + DISPLAYNAME "Slide" 14 + DESCRIPTION "Sliding block puzzle" 15 + OBJECTIVE "Slide the blocks to let the key block out.") 16 + solver(slide) 17 + 18 + puzzle(sokoban 19 + DISPLAYNAME "Sokoban" 20 + DESCRIPTION "Barrel-pushing puzzle" 21 + OBJECTIVE "Push all the barrels into the target squares.") 22 + 23 + # These unfinished programs don't even have the structure of a puzzle 24 + # game yet; they're just command-line programs containing test 25 + # implementations of some of the needed functionality. 26 + 27 + cliprogram(numgame numgame.c) 28 + 29 + cliprogram(path path.c COMPILE_DEFINITIONS TEST_GEN) 30 + 31 + export_variables_to_parent_scope()
+14
apps/plugins/puzzles/src/unfinished/README
··· 1 + This subdirectory contains puzzle implementations which are 2 + half-written, fundamentally flawed, or in other ways unready to be 3 + shipped as part of the polished Puzzles collection. 4 + 5 + The CMake build system will _build_ all of the source in this 6 + directory (to ensure it hasn't become unbuildable), but they won't be 7 + included in all-in-one puzzle binaries or installed by 'make install' 8 + targets. If you want to temporarily change that, you can reconfigure 9 + your build by defining the CMake variable PUZZLES_ENABLE_UNFINISHED. 10 + For example, 11 + 12 + cmake . -DPUZZLES_ENABLE_UNFINISHED="group;slide" 13 + 14 + will build as if both Group and Slide were fully official puzzles.
+2497
apps/plugins/puzzles/src/unfinished/group.c
··· 1 + /* 2 + * group.c: a Latin-square puzzle, but played with groups' Cayley 3 + * tables. That is, you are given a Cayley table of a group with 4 + * most elements blank and a few clues, and you must fill it in 5 + * so as to preserve the group axioms. 6 + * 7 + * This is a perfectly playable and fully working puzzle, but I'm 8 + * leaving it for the moment in the 'unfinished' directory because 9 + * it's just too esoteric (not to mention _hard_) for me to be 10 + * comfortable presenting it to the general public as something they 11 + * might (implicitly) actually want to play. 12 + * 13 + * TODO: 14 + * 15 + * - more solver techniques? 16 + * * Inverses: once we know that gh = e, we can immediately 17 + * deduce hg = e as well; then for any gx=y we can deduce 18 + * hy=x, and for any xg=y we have yh=x. 19 + * * Hard-mode associativity: we currently deduce based on 20 + * definite numbers in the grid, but we could also winnow 21 + * based on _possible_ numbers. 22 + * * My overambitious original thoughts included wondering if we 23 + * could infer that there must be elements of certain orders 24 + * (e.g. a group of order divisible by 5 must contain an 25 + * element of order 5), but I think in fact this is probably 26 + * silly. 27 + */ 28 + 29 + #include <stdio.h> 30 + #include <stdlib.h> 31 + #include <string.h> 32 + #include <assert.h> 33 + #include <ctype.h> 34 + #ifdef NO_TGMATH_H 35 + # include <math.h> 36 + #else 37 + # include <tgmath.h> 38 + #endif 39 + 40 + #include "puzzles.h" 41 + #include "latin.h" 42 + 43 + /* 44 + * Difficulty levels. I do some macro ickery here to ensure that my 45 + * enum and the various forms of my name list always match up. 46 + */ 47 + #define DIFFLIST(A) \ 48 + A(TRIVIAL,Trivial,NULL,t) \ 49 + A(NORMAL,Normal,solver_normal,n) \ 50 + A(HARD,Hard,solver_hard,h) \ 51 + A(EXTREME,Extreme,NULL,x) \ 52 + A(UNREASONABLE,Unreasonable,NULL,u) 53 + #define ENUM(upper,title,func,lower) DIFF_ ## upper, 54 + #define TITLE(upper,title,func,lower) #title, 55 + #define ENCODE(upper,title,func,lower) #lower 56 + #define CONFIG(upper,title,func,lower) ":" #title 57 + enum { DIFFLIST(ENUM) DIFFCOUNT }; 58 + static char const *const group_diffnames[] = { DIFFLIST(TITLE) }; 59 + static char const group_diffchars[] = DIFFLIST(ENCODE); 60 + #define DIFFCONFIG DIFFLIST(CONFIG) 61 + 62 + enum { 63 + COL_BACKGROUND, 64 + COL_GRID, 65 + COL_USER, 66 + COL_HIGHLIGHT, 67 + COL_ERROR, 68 + COL_PENCIL, 69 + COL_DIAGONAL, 70 + NCOLOURS 71 + }; 72 + 73 + /* 74 + * In identity mode, we number the elements e,a,b,c,d,f,g,h,... 75 + * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way. 76 + */ 77 + #define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) ) 78 + #define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) ) 79 + 80 + #define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id) 81 + #define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z')) 82 + #define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1)) 83 + 84 + struct game_params { 85 + int w, diff; 86 + bool id; 87 + }; 88 + 89 + typedef struct group_common { 90 + int refcount; 91 + bool *immutable; 92 + } group_common; 93 + 94 + struct game_state { 95 + game_params par; 96 + digit *grid; 97 + int *pencil; /* bitmaps using bits 1<<1..1<<n */ 98 + group_common *common; 99 + bool completed, cheated; 100 + digit *sequence; /* sequence of group elements shown */ 101 + 102 + /* 103 + * This array indicates thick lines separating rows and columns 104 + * placed and unplaced manually by the user as a visual aid, e.g. 105 + * to delineate a subgroup and its cosets. 106 + * 107 + * When a line is placed, it's deemed to be between the two 108 + * particular group elements that are on either side of it at the 109 + * time; dragging those two away from each other automatically 110 + * gets rid of the line. Hence, for a given element i, dividers[i] 111 + * is either -1 (indicating no divider to the right of i), or some 112 + * other element (indicating a divider to the right of i iff that 113 + * element is the one right of it). These are eagerly cleared 114 + * during drags. 115 + */ 116 + int *dividers; /* thick lines between rows/cols */ 117 + }; 118 + 119 + static game_params *default_params(void) 120 + { 121 + game_params *ret = snew(game_params); 122 + 123 + ret->w = 6; 124 + ret->diff = DIFF_NORMAL; 125 + ret->id = true; 126 + 127 + return ret; 128 + } 129 + 130 + static const struct game_params group_presets[] = { 131 + { 6, DIFF_NORMAL, true }, 132 + { 6, DIFF_NORMAL, false }, 133 + { 8, DIFF_NORMAL, true }, 134 + { 8, DIFF_NORMAL, false }, 135 + { 8, DIFF_HARD, true }, 136 + { 8, DIFF_HARD, false }, 137 + { 12, DIFF_NORMAL, true }, 138 + }; 139 + 140 + static bool game_fetch_preset(int i, char **name, game_params **params) 141 + { 142 + game_params *ret; 143 + char buf[80]; 144 + 145 + if (i < 0 || i >= lenof(group_presets)) 146 + return false; 147 + 148 + ret = snew(game_params); 149 + *ret = group_presets[i]; /* structure copy */ 150 + 151 + sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff], 152 + ret->id ? "" : ", identity hidden"); 153 + 154 + *name = dupstr(buf); 155 + *params = ret; 156 + return true; 157 + } 158 + 159 + static void free_params(game_params *params) 160 + { 161 + sfree(params); 162 + } 163 + 164 + static game_params *dup_params(const game_params *params) 165 + { 166 + game_params *ret = snew(game_params); 167 + *ret = *params; /* structure copy */ 168 + return ret; 169 + } 170 + 171 + static void decode_params(game_params *params, char const *string) 172 + { 173 + char const *p = string; 174 + 175 + params->w = atoi(p); 176 + while (*p && isdigit((unsigned char)*p)) p++; 177 + params->diff = DIFF_NORMAL; 178 + params->id = true; 179 + 180 + while (*p) { 181 + if (*p == 'd') { 182 + int i; 183 + p++; 184 + params->diff = DIFFCOUNT+1; /* ...which is invalid */ 185 + if (*p) { 186 + for (i = 0; i < DIFFCOUNT; i++) { 187 + if (*p == group_diffchars[i]) 188 + params->diff = i; 189 + } 190 + p++; 191 + } 192 + } else if (*p == 'i') { 193 + params->id = false; 194 + p++; 195 + } else { 196 + /* unrecognised character */ 197 + p++; 198 + } 199 + } 200 + } 201 + 202 + static char *encode_params(const game_params *params, bool full) 203 + { 204 + char ret[80]; 205 + 206 + sprintf(ret, "%d", params->w); 207 + if (full) 208 + sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]); 209 + if (!params->id) 210 + sprintf(ret + strlen(ret), "i"); 211 + 212 + return dupstr(ret); 213 + } 214 + 215 + static config_item *game_configure(const game_params *params) 216 + { 217 + config_item *ret; 218 + char buf[80]; 219 + 220 + ret = snewn(4, config_item); 221 + 222 + ret[0].name = "Grid size"; 223 + ret[0].type = C_STRING; 224 + sprintf(buf, "%d", params->w); 225 + ret[0].u.string.sval = dupstr(buf); 226 + 227 + ret[1].name = "Difficulty"; 228 + ret[1].type = C_CHOICES; 229 + ret[1].u.choices.choicenames = DIFFCONFIG; 230 + ret[1].u.choices.selected = params->diff; 231 + 232 + ret[2].name = "Show identity"; 233 + ret[2].type = C_BOOLEAN; 234 + ret[2].u.boolean.bval = params->id; 235 + 236 + ret[3].name = NULL; 237 + ret[3].type = C_END; 238 + 239 + return ret; 240 + } 241 + 242 + static game_params *custom_params(const config_item *cfg) 243 + { 244 + game_params *ret = snew(game_params); 245 + 246 + ret->w = atoi(cfg[0].u.string.sval); 247 + ret->diff = cfg[1].u.choices.selected; 248 + ret->id = cfg[2].u.boolean.bval; 249 + 250 + return ret; 251 + } 252 + 253 + static const char *validate_params(const game_params *params, bool full) 254 + { 255 + if (params->w < 3 || params->w > 26) 256 + return "Grid size must be between 3 and 26"; 257 + if (params->diff >= DIFFCOUNT) 258 + return "Unknown difficulty rating"; 259 + if (!params->id && params->diff == DIFF_TRIVIAL) { 260 + /* 261 + * We can't have a Trivial-difficulty puzzle (i.e. latin 262 + * square deductions only) without a clear identity, because 263 + * identityless puzzles always have two rows and two columns 264 + * entirely blank, and no latin-square deduction permits the 265 + * distinguishing of two such rows. 266 + */ 267 + return "Trivial puzzles must have an identity"; 268 + } 269 + if (!params->id && params->w == 3) { 270 + /* 271 + * We can't have a 3x3 puzzle without an identity either, 272 + * because 3x3 puzzles can't ever be harder than Trivial 273 + * (there are no 3x3 latin squares which aren't also valid 274 + * group tables, so enabling group-based deductions doesn't 275 + * rule out any possible solutions) and - as above - Trivial 276 + * puzzles can't not have an identity. 277 + */ 278 + return "3x3 puzzles must have an identity"; 279 + } 280 + return NULL; 281 + } 282 + 283 + /* ---------------------------------------------------------------------- 284 + * Solver. 285 + */ 286 + 287 + static int find_identity(struct latin_solver *solver) 288 + { 289 + int w = solver->o; 290 + digit *grid = solver->grid; 291 + int i, j; 292 + 293 + for (i = 0; i < w; i++) 294 + for (j = 0; j < w; j++) { 295 + if (grid[i*w+j] == i+1) 296 + return j+1; 297 + if (grid[i*w+j] == j+1) 298 + return i+1; 299 + } 300 + 301 + return 0; 302 + } 303 + 304 + static int solver_normal(struct latin_solver *solver, void *vctx) 305 + { 306 + int w = solver->o; 307 + #ifdef STANDALONE_SOLVER 308 + char **names = solver->names; 309 + #endif 310 + digit *grid = solver->grid; 311 + int i, j, k; 312 + 313 + /* 314 + * Deduce using associativity: (ab)c = a(bc). 315 + * 316 + * So we pick any a,b,c we like; then if we know ab, bc, and 317 + * (ab)c we can fill in a(bc). 318 + */ 319 + for (i = 0; i < w; i++) 320 + for (j = 0; j < w; j++) 321 + for (k = 0; k < w; k++) { 322 + if (!grid[i*w+j] || !grid[j*w+k]) 323 + continue; 324 + if (grid[(grid[i*w+j]-1)*w+k] && 325 + !grid[i*w+(grid[j*w+k]-1)]) { 326 + int x = grid[j*w+k]-1, y = i; 327 + int n = grid[(grid[i*w+j]-1)*w+k]; 328 + #ifdef STANDALONE_SOLVER 329 + if (solver_show_working) { 330 + printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", 331 + solver_recurse_depth*4, "", 332 + names[i], names[j], names[k], 333 + names[grid[i*w+j]-1], names[k], 334 + names[i], names[grid[j*w+k]-1]); 335 + printf("%*s placing %s at (%d,%d)\n", 336 + solver_recurse_depth*4, "", 337 + names[n-1], x+1, y+1); 338 + } 339 + #endif 340 + if (solver->cube[(x*w+y)*w+n-1]) { 341 + latin_solver_place(solver, x, y, n); 342 + return 1; 343 + } else { 344 + #ifdef STANDALONE_SOLVER 345 + if (solver_show_working) 346 + printf("%*s contradiction!\n", 347 + solver_recurse_depth*4, ""); 348 + return -1; 349 + #endif 350 + } 351 + } 352 + if (!grid[(grid[i*w+j]-1)*w+k] && 353 + grid[i*w+(grid[j*w+k]-1)]) { 354 + int x = k, y = grid[i*w+j]-1; 355 + int n = grid[i*w+(grid[j*w+k]-1)]; 356 + #ifdef STANDALONE_SOLVER 357 + if (solver_show_working) { 358 + printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", 359 + solver_recurse_depth*4, "", 360 + names[i], names[j], names[k], 361 + names[grid[i*w+j]-1], names[k], 362 + names[i], names[grid[j*w+k]-1]); 363 + printf("%*s placing %s at (%d,%d)\n", 364 + solver_recurse_depth*4, "", 365 + names[n-1], x+1, y+1); 366 + } 367 + #endif 368 + if (solver->cube[(x*w+y)*w+n-1]) { 369 + latin_solver_place(solver, x, y, n); 370 + return 1; 371 + } else { 372 + #ifdef STANDALONE_SOLVER 373 + if (solver_show_working) 374 + printf("%*s contradiction!\n", 375 + solver_recurse_depth*4, ""); 376 + return -1; 377 + #endif 378 + } 379 + } 380 + } 381 + 382 + /* 383 + * Fill in the row and column for the group identity, if it's not 384 + * already known and if we've just found out what it is. 385 + */ 386 + i = find_identity(solver); 387 + if (i) { 388 + bool done_something = false; 389 + for (j = 1; j <= w; j++) { 390 + if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) { 391 + done_something = true; 392 + } 393 + } 394 + if (done_something) { 395 + #ifdef STANDALONE_SOLVER 396 + if (solver_show_working) { 397 + printf("%*s%s is the group identity\n", 398 + solver_recurse_depth*4, "", names[i-1]); 399 + } 400 + #endif 401 + for (j = 1; j <= w; j++) { 402 + if (!grid[(j-1)*w+(i-1)]) { 403 + if (!cube(i-1, j-1, j)) { 404 + #ifdef STANDALONE_SOLVER 405 + if (solver_show_working) { 406 + printf("%*s but %s cannot go at (%d,%d) - " 407 + "contradiction!\n", 408 + solver_recurse_depth*4, "", 409 + names[j-1], i, j); 410 + } 411 + #endif 412 + return -1; 413 + } 414 + #ifdef STANDALONE_SOLVER 415 + if (solver_show_working) { 416 + printf("%*s placing %s at (%d,%d)\n", 417 + solver_recurse_depth*4, "", 418 + names[j-1], i, j); 419 + } 420 + #endif 421 + latin_solver_place(solver, i-1, j-1, j); 422 + } 423 + if (!grid[(i-1)*w+(j-1)]) { 424 + if (!cube(j-1, i-1, j)) { 425 + #ifdef STANDALONE_SOLVER 426 + if (solver_show_working) { 427 + printf("%*s but %s cannot go at (%d,%d) - " 428 + "contradiction!\n", 429 + solver_recurse_depth*4, "", 430 + names[j-1], j, i); 431 + } 432 + #endif 433 + return -1; 434 + } 435 + #ifdef STANDALONE_SOLVER 436 + if (solver_show_working) { 437 + printf("%*s placing %s at (%d,%d)\n", 438 + solver_recurse_depth*4, "", 439 + names[j-1], j, i); 440 + } 441 + #endif 442 + latin_solver_place(solver, j-1, i-1, j); 443 + } 444 + } 445 + return 1; 446 + } 447 + } 448 + 449 + return 0; 450 + } 451 + 452 + static int solver_hard(struct latin_solver *solver, void *vctx) 453 + { 454 + bool done_something = false; 455 + int w = solver->o; 456 + #ifdef STANDALONE_SOLVER 457 + char **names = solver->names; 458 + #endif 459 + int i, j; 460 + 461 + /* 462 + * In identity-hidden mode, systematically rule out possibilities 463 + * for the group identity. 464 + * 465 + * In solver_normal, we used the fact that any filled square in 466 + * the grid whose contents _does_ match one of the elements it's 467 + * the product of - that is, ab=a or ab=b - tells you immediately 468 + * that the other element is the identity. 469 + * 470 + * Here, we use the flip side of that: any filled square in the 471 + * grid whose contents does _not_ match either its row or column - 472 + * that is, if ab is neither a nor b - tells you immediately that 473 + * _neither_ of those elements is the identity. And if that's 474 + * true, then we can also immediately rule out the possibility 475 + * that it acts as the identity on any element at all. 476 + */ 477 + for (i = 0; i < w; i++) { 478 + bool i_can_be_id = true; 479 + #ifdef STANDALONE_SOLVER 480 + char title[80]; 481 + #endif 482 + 483 + for (j = 0; j < w; j++) { 484 + if (grid(i,j) && grid(i,j) != j+1) { 485 + #ifdef STANDALONE_SOLVER 486 + if (solver_show_working) 487 + sprintf(title, "%s cannot be the identity: " 488 + "%s%s = %s =/= %s", names[i], names[i], names[j], 489 + names[grid(i,j)-1], names[j]); 490 + #endif 491 + i_can_be_id = false; 492 + break; 493 + } 494 + if (grid(j,i) && grid(j,i) != j+1) { 495 + #ifdef STANDALONE_SOLVER 496 + if (solver_show_working) 497 + sprintf(title, "%s cannot be the identity: " 498 + "%s%s = %s =/= %s", names[i], names[j], names[i], 499 + names[grid(j,i)-1], names[j]); 500 + #endif 501 + i_can_be_id = false; 502 + break; 503 + } 504 + } 505 + 506 + if (!i_can_be_id) { 507 + /* Now rule out ij=j or ji=j for all j. */ 508 + for (j = 0; j < w; j++) { 509 + if (cube(i, j, j+1)) { 510 + #ifdef STANDALONE_SOLVER 511 + if (solver_show_working) { 512 + if (title[0]) { 513 + printf("%*s%s\n", solver_recurse_depth*4, "", 514 + title); 515 + title[0] = '\0'; 516 + } 517 + printf("%*s ruling out %s at (%d,%d)\n", 518 + solver_recurse_depth*4, "", names[j], i, j); 519 + } 520 + #endif 521 + cube(i, j, j+1) = false; 522 + } 523 + if (cube(j, i, j+1)) { 524 + #ifdef STANDALONE_SOLVER 525 + if (solver_show_working) { 526 + if (title[0]) { 527 + printf("%*s%s\n", solver_recurse_depth*4, "", 528 + title); 529 + title[0] = '\0'; 530 + } 531 + printf("%*s ruling out %s at (%d,%d)\n", 532 + solver_recurse_depth*4, "", names[j], j, i); 533 + } 534 + #endif 535 + cube(j, i, j+1) = false; 536 + } 537 + } 538 + } 539 + } 540 + 541 + return done_something; 542 + } 543 + 544 + #define SOLVER(upper,title,func,lower) func, 545 + static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) }; 546 + 547 + static bool group_valid(struct latin_solver *solver, void *ctx) 548 + { 549 + int w = solver->o; 550 + #ifdef STANDALONE_SOLVER 551 + char **names = solver->names; 552 + #endif 553 + int i, j, k; 554 + 555 + for (i = 0; i < w; i++) 556 + for (j = 0; j < w; j++) 557 + for (k = 0; k < w; k++) { 558 + int ij = grid(i, j) - 1; 559 + int jk = grid(j, k) - 1; 560 + int ij_k = grid(ij, k) - 1; 561 + int i_jk = grid(i, jk) - 1; 562 + if (ij_k != i_jk) { 563 + #ifdef STANDALONE_SOLVER 564 + if (solver_show_working) { 565 + printf("%*sfailure of associativity: " 566 + "(%s%s)%s = %s%s = %s but " 567 + "%s(%s%s) = %s%s = %s\n", 568 + solver_recurse_depth*4, "", 569 + names[i], names[j], names[k], 570 + names[ij], names[k], names[ij_k], 571 + names[i], names[j], names[k], 572 + names[i], names[jk], names[i_jk]); 573 + } 574 + #endif 575 + return false; 576 + } 577 + } 578 + 579 + return true; 580 + } 581 + 582 + static int solver(const game_params *params, digit *grid, int maxdiff) 583 + { 584 + int w = params->w; 585 + int ret; 586 + struct latin_solver solver; 587 + 588 + #ifdef STANDALONE_SOLVER 589 + char *p, text[100], *names[50]; 590 + int i; 591 + 592 + for (i = 0, p = text; i < w; i++) { 593 + names[i] = p; 594 + *p++ = TOCHAR(i+1, params->id); 595 + *p++ = '\0'; 596 + } 597 + solver.names = names; 598 + #endif 599 + 600 + if (latin_solver_alloc(&solver, grid, w)) 601 + ret = latin_solver_main(&solver, maxdiff, 602 + DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME, 603 + DIFF_EXTREME, DIFF_UNREASONABLE, 604 + group_solvers, group_valid, NULL, NULL, NULL); 605 + else 606 + ret = diff_impossible; 607 + 608 + latin_solver_free(&solver); 609 + 610 + return ret; 611 + } 612 + 613 + /* ---------------------------------------------------------------------- 614 + * Grid generation. 615 + */ 616 + 617 + static char *encode_grid(char *desc, digit *grid, int area) 618 + { 619 + int run, i; 620 + char *p = desc; 621 + 622 + run = 0; 623 + for (i = 0; i <= area; i++) { 624 + int n = (i < area ? grid[i] : -1); 625 + 626 + if (!n) 627 + run++; 628 + else { 629 + if (run) { 630 + while (run > 0) { 631 + int c = 'a' - 1 + run; 632 + if (run > 26) 633 + c = 'z'; 634 + *p++ = c; 635 + run -= c - ('a' - 1); 636 + } 637 + } else { 638 + /* 639 + * If there's a number in the very top left or 640 + * bottom right, there's no point putting an 641 + * unnecessary _ before or after it. 642 + */ 643 + if (p > desc && n > 0) 644 + *p++ = '_'; 645 + } 646 + if (n > 0) 647 + p += sprintf(p, "%d", n); 648 + run = 0; 649 + } 650 + } 651 + return p; 652 + } 653 + 654 + /* ----- data generated by group.gap begins ----- */ 655 + 656 + struct group { 657 + unsigned long autosize; 658 + int order, ngens; 659 + const char *gens; 660 + }; 661 + struct groups { 662 + int ngroups; 663 + const struct group *groups; 664 + }; 665 + 666 + static const struct group groupdata[] = { 667 + /* order 2 */ 668 + {1L, 2, 1, "BA"}, 669 + /* order 3 */ 670 + {2L, 3, 1, "BCA"}, 671 + /* order 4 */ 672 + {2L, 4, 1, "BCDA"}, 673 + {6L, 4, 2, "BADC" "CDAB"}, 674 + /* order 5 */ 675 + {4L, 5, 1, "BCDEA"}, 676 + /* order 6 */ 677 + {6L, 6, 2, "CFEBAD" "BADCFE"}, 678 + {2L, 6, 1, "DCFEBA"}, 679 + /* order 7 */ 680 + {6L, 7, 1, "BCDEFGA"}, 681 + /* order 8 */ 682 + {4L, 8, 1, "BCEFDGHA"}, 683 + {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"}, 684 + {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"}, 685 + {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"}, 686 + {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"}, 687 + /* order 9 */ 688 + {6L, 9, 1, "BDECGHFIA"}, 689 + {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"}, 690 + /* order 10 */ 691 + {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"}, 692 + {4L, 10, 1, "DCFEHGJIBA"}, 693 + /* order 11 */ 694 + {10L, 11, 1, "BCDEFGHIJKA"}, 695 + /* order 12 */ 696 + {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"}, 697 + {4L, 12, 1, "EHIJKCBLDGFA"}, 698 + {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"}, 699 + {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"}, 700 + {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"}, 701 + /* order 13 */ 702 + {12L, 13, 1, "BCDEFGHIJKLMA"}, 703 + /* order 14 */ 704 + {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"}, 705 + {6L, 14, 1, "FEHGJILKNMBADC"}, 706 + /* order 15 */ 707 + {8L, 15, 1, "EGHCJKFMNIOBLDA"}, 708 + /* order 16 */ 709 + {8L, 16, 1, "MKNPFOADBGLCIEHJ"}, 710 + {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"}, 711 + {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"}, 712 + {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"}, 713 + {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"}, 714 + {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"}, 715 + {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"}, 716 + {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"}, 717 + {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"}, 718 + {192L, 16, 3, 719 + "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"}, 720 + {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"}, 721 + {192L, 16, 3, 722 + "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"}, 723 + {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"}, 724 + {20160L, 16, 4, 725 + "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN" 726 + "DGIAKLBNCOEFPHJM"}, 727 + /* order 17 */ 728 + {16L, 17, 1, "EFGHIJKLMNOPQABCD"}, 729 + /* order 18 */ 730 + {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"}, 731 + {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"}, 732 + {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"}, 733 + {432L, 18, 3, 734 + "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"}, 735 + {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"}, 736 + /* order 19 */ 737 + {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"}, 738 + /* order 20 */ 739 + {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"}, 740 + {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"}, 741 + {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"}, 742 + {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"}, 743 + {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"}, 744 + /* order 21 */ 745 + {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"}, 746 + {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"}, 747 + /* order 22 */ 748 + {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"}, 749 + {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"}, 750 + /* order 23 */ 751 + {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"}, 752 + /* order 24 */ 753 + {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"}, 754 + {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"}, 755 + {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"}, 756 + {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"}, 757 + {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"}, 758 + {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"}, 759 + {48L, 24, 3, 760 + "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" 761 + "HSNOPWLDTUVBRIAKXFGCQEMJ"}, 762 + {24L, 24, 3, 763 + "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH" 764 + "TRONXLWCHVUMSAIJPGFDEQBK"}, 765 + {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"}, 766 + {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"}, 767 + {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"}, 768 + {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"}, 769 + {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"}, 770 + {144L, 24, 3, 771 + "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" 772 + "BAFGHCDEMNOPIJKLTUVQRSXW"}, 773 + {336L, 24, 3, 774 + "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG" 775 + "HENOPJKLTUVBQRSAXFGWCDMI"}, 776 + /* order 25 */ 777 + {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"}, 778 + {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"}, 779 + /* order 26 */ 780 + {156L, 26, 2, 781 + "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"}, 782 + {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"}, 783 + }; 784 + 785 + static const struct groups groups[] = { 786 + {0, NULL}, /* trivial case: 0 */ 787 + {0, NULL}, /* trivial case: 1 */ 788 + {1, groupdata + 0}, /* 2 */ 789 + {1, groupdata + 1}, /* 3 */ 790 + {2, groupdata + 2}, /* 4 */ 791 + {1, groupdata + 4}, /* 5 */ 792 + {2, groupdata + 5}, /* 6 */ 793 + {1, groupdata + 7}, /* 7 */ 794 + {5, groupdata + 8}, /* 8 */ 795 + {2, groupdata + 13}, /* 9 */ 796 + {2, groupdata + 15}, /* 10 */ 797 + {1, groupdata + 17}, /* 11 */ 798 + {5, groupdata + 18}, /* 12 */ 799 + {1, groupdata + 23}, /* 13 */ 800 + {2, groupdata + 24}, /* 14 */ 801 + {1, groupdata + 26}, /* 15 */ 802 + {14, groupdata + 27}, /* 16 */ 803 + {1, groupdata + 41}, /* 17 */ 804 + {5, groupdata + 42}, /* 18 */ 805 + {1, groupdata + 47}, /* 19 */ 806 + {5, groupdata + 48}, /* 20 */ 807 + {2, groupdata + 53}, /* 21 */ 808 + {2, groupdata + 55}, /* 22 */ 809 + {1, groupdata + 57}, /* 23 */ 810 + {15, groupdata + 58}, /* 24 */ 811 + {2, groupdata + 73}, /* 25 */ 812 + {2, groupdata + 75}, /* 26 */ 813 + }; 814 + 815 + /* ----- data generated by group.gap ends ----- */ 816 + 817 + static char *new_game_desc(const game_params *params, random_state *rs, 818 + char **aux, bool interactive) 819 + { 820 + int w = params->w, a = w*w; 821 + digit *grid, *soln, *soln2; 822 + int *indices; 823 + int i, j, k, qh, qt; 824 + int diff = params->diff; 825 + const struct group *group; 826 + char *desc, *p; 827 + 828 + /* 829 + * Difficulty exceptions: some combinations of size and 830 + * difficulty cannot be satisfied, because all puzzles of at 831 + * most that difficulty are actually even easier. 832 + * 833 + * Remember to re-test this whenever a change is made to the 834 + * solver logic! 835 + * 836 + * I tested it using the following shell command: 837 + 838 + for d in t n h x u; do 839 + for id in '' i; do 840 + for i in {3..9}; do 841 + echo -n "./group --generate 1 ${i}d${d}${id}: " 842 + perl -e 'alarm 30; exec @ARGV' \ 843 + ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok 844 + done 845 + done 846 + done 847 + 848 + * Of course, it's better to do that after taking the exceptions 849 + * _out_, so as to detect exceptions that should be removed as 850 + * well as those which should be added. 851 + */ 852 + if (w < 5 && diff == DIFF_UNREASONABLE) 853 + diff--; 854 + if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME) 855 + diff--; 856 + if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD) 857 + diff--; 858 + if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL) 859 + diff--; 860 + 861 + grid = snewn(a, digit); 862 + soln = snewn(a, digit); 863 + soln2 = snewn(a, digit); 864 + indices = snewn(a, int); 865 + 866 + while (1) { 867 + /* 868 + * Construct a valid group table, by picking a group from 869 + * the above data table, decompressing it into a full 870 + * representation by BFS, and then randomly permuting its 871 + * non-identity elements. 872 + * 873 + * We build the canonical table in 'soln' (and use 'grid' as 874 + * our BFS queue), then transfer the table into 'grid' 875 + * having shuffled the rows. 876 + */ 877 + assert(w >= 2); 878 + assert(w < lenof(groups)); 879 + group = groups[w].groups + random_upto(rs, groups[w].ngroups); 880 + assert(group->order == w); 881 + memset(soln, 0, a); 882 + for (i = 0; i < w; i++) 883 + soln[i] = i+1; 884 + qh = qt = 0; 885 + grid[qt++] = 1; 886 + while (qh < qt) { 887 + digit *row, *newrow; 888 + 889 + i = grid[qh++]; 890 + row = soln + (i-1)*w; 891 + 892 + for (j = 0; j < group->ngens; j++) { 893 + int nri; 894 + const char *gen = group->gens + j*w; 895 + 896 + /* 897 + * Apply each group generator to row, constructing a 898 + * new row. 899 + */ 900 + nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */ 901 + newrow = soln + (nri-1)*w; 902 + if (!newrow[0]) { /* not done yet */ 903 + for (k = 0; k < w; k++) 904 + newrow[k] = gen[row[k]-1] - 'A' + 1; 905 + grid[qt++] = nri; 906 + } 907 + } 908 + } 909 + /* That's got the canonical table. Now shuffle it. */ 910 + for (i = 0; i < w; i++) 911 + soln2[i] = i; 912 + if (params->id) /* do we shuffle in the identity? */ 913 + shuffle(soln2+1, w-1, sizeof(*soln2), rs); 914 + else 915 + shuffle(soln2, w, sizeof(*soln2), rs); 916 + for (i = 0; i < w; i++) 917 + for (j = 0; j < w; j++) 918 + grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1; 919 + 920 + /* 921 + * Remove entries one by one while the puzzle is still 922 + * soluble at the appropriate difficulty level. 923 + */ 924 + memcpy(soln, grid, a); 925 + if (!params->id) { 926 + /* 927 + * Start by blanking the entire identity row and column, 928 + * and also another row and column so that the player 929 + * can't trivially determine which element is the 930 + * identity. 931 + */ 932 + 933 + j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */ 934 + for (i = 0; i < w; i++) { 935 + grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0; 936 + grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0; 937 + } 938 + 939 + memcpy(soln2, grid, a); 940 + if (solver(params, soln2, diff) > diff) 941 + continue; /* go round again if that didn't work */ 942 + } 943 + 944 + k = 0; 945 + for (i = (params->id ? 1 : 0); i < w; i++) 946 + for (j = (params->id ? 1 : 0); j < w; j++) 947 + if (grid[i*w+j]) 948 + indices[k++] = i*w+j; 949 + shuffle(indices, k, sizeof(*indices), rs); 950 + 951 + for (i = 0; i < k; i++) { 952 + memcpy(soln2, grid, a); 953 + soln2[indices[i]] = 0; 954 + if (solver(params, soln2, diff) <= diff) 955 + grid[indices[i]] = 0; 956 + } 957 + 958 + /* 959 + * Make sure the puzzle isn't too easy. 960 + */ 961 + if (diff > 0) { 962 + memcpy(soln2, grid, a); 963 + if (solver(params, soln2, diff-1) < diff) 964 + continue; /* go round and try again */ 965 + } 966 + 967 + /* 968 + * Done. 969 + */ 970 + break; 971 + } 972 + 973 + /* 974 + * Encode the puzzle description. 975 + */ 976 + desc = snewn(a*20, char); 977 + p = encode_grid(desc, grid, a); 978 + *p++ = '\0'; 979 + desc = sresize(desc, p - desc, char); 980 + 981 + /* 982 + * Encode the solution. 983 + */ 984 + *aux = snewn(a+2, char); 985 + (*aux)[0] = 'S'; 986 + for (i = 0; i < a; i++) 987 + (*aux)[i+1] = TOCHAR(soln[i], params->id); 988 + (*aux)[a+1] = '\0'; 989 + 990 + sfree(grid); 991 + sfree(soln); 992 + sfree(soln2); 993 + sfree(indices); 994 + 995 + return desc; 996 + } 997 + 998 + /* ---------------------------------------------------------------------- 999 + * Gameplay. 1000 + */ 1001 + 1002 + static const char *validate_grid_desc(const char **pdesc, int range, int area) 1003 + { 1004 + const char *desc = *pdesc; 1005 + int squares = 0; 1006 + while (*desc && *desc != ',') { 1007 + int n = *desc++; 1008 + if (n >= 'a' && n <= 'z') { 1009 + squares += n - 'a' + 1; 1010 + } else if (n == '_') { 1011 + /* do nothing */; 1012 + } else if (n > '0' && n <= '9') { 1013 + int val = atoi(desc-1); 1014 + if (val < 1 || val > range) 1015 + return "Out-of-range number in game description"; 1016 + squares++; 1017 + while (*desc >= '0' && *desc <= '9') 1018 + desc++; 1019 + } else 1020 + return "Invalid character in game description"; 1021 + } 1022 + 1023 + if (squares < area) 1024 + return "Not enough data to fill grid"; 1025 + 1026 + if (squares > area) 1027 + return "Too much data to fit in grid"; 1028 + *pdesc = desc; 1029 + return NULL; 1030 + } 1031 + 1032 + static const char *validate_desc(const game_params *params, const char *desc) 1033 + { 1034 + int w = params->w, a = w*w; 1035 + const char *p = desc; 1036 + 1037 + return validate_grid_desc(&p, w, a); 1038 + } 1039 + 1040 + static const char *spec_to_grid(const char *desc, digit *grid, int area) 1041 + { 1042 + int i = 0; 1043 + while (*desc && *desc != ',') { 1044 + int n = *desc++; 1045 + if (n >= 'a' && n <= 'z') { 1046 + int run = n - 'a' + 1; 1047 + assert(i + run <= area); 1048 + while (run-- > 0) 1049 + grid[i++] = 0; 1050 + } else if (n == '_') { 1051 + /* do nothing */; 1052 + } else if (n > '0' && n <= '9') { 1053 + assert(i < area); 1054 + grid[i++] = atoi(desc-1); 1055 + while (*desc >= '0' && *desc <= '9') 1056 + desc++; 1057 + } else { 1058 + assert(!"We can't get here"); 1059 + } 1060 + } 1061 + assert(i == area); 1062 + return desc; 1063 + } 1064 + 1065 + static game_state *new_game(midend *me, const game_params *params, 1066 + const char *desc) 1067 + { 1068 + int w = params->w, a = w*w; 1069 + game_state *state = snew(game_state); 1070 + int i; 1071 + 1072 + state->par = *params; /* structure copy */ 1073 + state->grid = snewn(a, digit); 1074 + state->common = snew(group_common); 1075 + state->common->refcount = 1; 1076 + state->common->immutable = snewn(a, bool); 1077 + state->pencil = snewn(a, int); 1078 + for (i = 0; i < a; i++) { 1079 + state->grid[i] = 0; 1080 + state->common->immutable[i] = false; 1081 + state->pencil[i] = 0; 1082 + } 1083 + state->sequence = snewn(w, digit); 1084 + state->dividers = snewn(w, int); 1085 + for (i = 0; i < w; i++) { 1086 + state->sequence[i] = i; 1087 + state->dividers[i] = -1; 1088 + } 1089 + 1090 + desc = spec_to_grid(desc, state->grid, a); 1091 + for (i = 0; i < a; i++) 1092 + if (state->grid[i] != 0) 1093 + state->common->immutable[i] = true; 1094 + 1095 + state->completed = false; 1096 + state->cheated = false; 1097 + 1098 + return state; 1099 + } 1100 + 1101 + static game_state *dup_game(const game_state *state) 1102 + { 1103 + int w = state->par.w, a = w*w; 1104 + game_state *ret = snew(game_state); 1105 + 1106 + ret->par = state->par; /* structure copy */ 1107 + 1108 + ret->grid = snewn(a, digit); 1109 + ret->common = state->common; 1110 + ret->common->refcount++; 1111 + ret->pencil = snewn(a, int); 1112 + ret->sequence = snewn(w, digit); 1113 + ret->dividers = snewn(w, int); 1114 + memcpy(ret->grid, state->grid, a*sizeof(digit)); 1115 + memcpy(ret->pencil, state->pencil, a*sizeof(int)); 1116 + memcpy(ret->sequence, state->sequence, w*sizeof(digit)); 1117 + memcpy(ret->dividers, state->dividers, w*sizeof(int)); 1118 + 1119 + ret->completed = state->completed; 1120 + ret->cheated = state->cheated; 1121 + 1122 + return ret; 1123 + } 1124 + 1125 + static void free_game(game_state *state) 1126 + { 1127 + sfree(state->grid); 1128 + if (--state->common->refcount == 0) { 1129 + sfree(state->common->immutable); 1130 + sfree(state->common); 1131 + } 1132 + sfree(state->pencil); 1133 + sfree(state->sequence); 1134 + sfree(state); 1135 + } 1136 + 1137 + static char *solve_game(const game_state *state, const game_state *currstate, 1138 + const char *aux, const char **error) 1139 + { 1140 + int w = state->par.w, a = w*w; 1141 + int i, ret; 1142 + digit *soln; 1143 + char *out; 1144 + 1145 + if (aux) 1146 + return dupstr(aux); 1147 + 1148 + soln = snewn(a, digit); 1149 + memcpy(soln, state->grid, a*sizeof(digit)); 1150 + 1151 + ret = solver(&state->par, soln, DIFFCOUNT-1); 1152 + 1153 + if (ret == diff_impossible) { 1154 + *error = "No solution exists for this puzzle"; 1155 + out = NULL; 1156 + } else if (ret == diff_ambiguous) { 1157 + *error = "Multiple solutions exist for this puzzle"; 1158 + out = NULL; 1159 + } else { 1160 + out = snewn(a+2, char); 1161 + out[0] = 'S'; 1162 + for (i = 0; i < a; i++) 1163 + out[i+1] = TOCHAR(soln[i], state->par.id); 1164 + out[a+1] = '\0'; 1165 + } 1166 + 1167 + sfree(soln); 1168 + return out; 1169 + } 1170 + 1171 + static bool game_can_format_as_text_now(const game_params *params) 1172 + { 1173 + return true; 1174 + } 1175 + 1176 + static char *game_text_format(const game_state *state) 1177 + { 1178 + int w = state->par.w; 1179 + int x, y; 1180 + char *ret, *p, ch; 1181 + 1182 + ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */ 1183 + 1184 + p = ret; 1185 + for (y = 0; y < w; y++) { 1186 + for (x = 0; x < w; x++) { 1187 + digit d = state->grid[y*w+x]; 1188 + 1189 + if (d == 0) { 1190 + ch = '.'; 1191 + } else { 1192 + ch = TOCHAR(d, state->par.id); 1193 + } 1194 + 1195 + *p++ = ch; 1196 + if (x == w-1) { 1197 + *p++ = '\n'; 1198 + } else { 1199 + *p++ = ' '; 1200 + } 1201 + } 1202 + } 1203 + 1204 + assert(p - ret == 2*w*w); 1205 + *p = '\0'; 1206 + return ret; 1207 + } 1208 + 1209 + struct game_ui { 1210 + /* 1211 + * These are the coordinates of the primary highlighted square on 1212 + * the grid, if hshow = 1. 1213 + */ 1214 + int hx, hy; 1215 + /* 1216 + * These are the coordinates hx,hy _before_ they go through 1217 + * state->sequence. 1218 + */ 1219 + int ohx, ohy; 1220 + /* 1221 + * These variables give the length and displacement of a diagonal 1222 + * sequence of highlighted squares starting at ohx,ohy (still if 1223 + * hshow = 1). To find the squares' real coordinates, for 0<=i<dn, 1224 + * compute ohx+i*odx and ohy+i*ody and then map through 1225 + * state->sequence. 1226 + */ 1227 + int odx, ody, odn; 1228 + /* 1229 + * This indicates whether the current highlight is a 1230 + * pencil-mark one or a real one. 1231 + */ 1232 + bool hpencil; 1233 + /* 1234 + * This indicates whether or not we're showing the highlight 1235 + * (used to be hx = hy = -1); important so that when we're 1236 + * using the cursor keys it doesn't keep coming back at a 1237 + * fixed position. When hshow = 1, pressing a valid number 1238 + * or letter key or Space will enter that number or letter in the grid. 1239 + */ 1240 + bool hshow; 1241 + /* 1242 + * This indicates whether we're using the highlight as a cursor; 1243 + * it means that it doesn't vanish on a keypress, and that it is 1244 + * allowed on immutable squares. 1245 + */ 1246 + bool hcursor; 1247 + /* 1248 + * This indicates whether we're dragging a table header to 1249 + * reposition an entire row or column. 1250 + */ 1251 + int drag; /* 0=none 1=row 2=col */ 1252 + int dragnum; /* element being dragged */ 1253 + int dragpos; /* its current position */ 1254 + int edgepos; 1255 + 1256 + /* 1257 + * User preference option: if the user right-clicks in a square 1258 + * and presses a letter key to add/remove a pencil mark, do we 1259 + * hide the mouse highlight again afterwards? 1260 + * 1261 + * Historically our answer was yes. The Android port prefers no. 1262 + * There are advantages both ways, depending how much you dislike 1263 + * the highlight cluttering your view. So it's a preference. 1264 + */ 1265 + bool pencil_keep_highlight; 1266 + }; 1267 + 1268 + static game_ui *new_ui(const game_state *state) 1269 + { 1270 + game_ui *ui = snew(game_ui); 1271 + 1272 + ui->hx = ui->hy = 0; 1273 + ui->hpencil = false; 1274 + ui->hshow = false; 1275 + ui->hcursor = false; 1276 + ui->drag = 0; 1277 + 1278 + ui->pencil_keep_highlight = false; 1279 + 1280 + return ui; 1281 + } 1282 + 1283 + static void free_ui(game_ui *ui) 1284 + { 1285 + sfree(ui); 1286 + } 1287 + 1288 + static config_item *get_prefs(game_ui *ui) 1289 + { 1290 + config_item *ret; 1291 + 1292 + ret = snewn(2, config_item); 1293 + 1294 + ret[0].name = "Keep mouse highlight after changing a pencil mark"; 1295 + ret[0].kw = "pencil-keep-highlight"; 1296 + ret[0].type = C_BOOLEAN; 1297 + ret[0].u.boolean.bval = ui->pencil_keep_highlight; 1298 + 1299 + ret[1].name = NULL; 1300 + ret[1].type = C_END; 1301 + 1302 + return ret; 1303 + } 1304 + 1305 + static void set_prefs(game_ui *ui, const config_item *cfg) 1306 + { 1307 + ui->pencil_keep_highlight = cfg[0].u.boolean.bval; 1308 + } 1309 + 1310 + static void game_changed_state(game_ui *ui, const game_state *oldstate, 1311 + const game_state *newstate) 1312 + { 1313 + int w = newstate->par.w; 1314 + /* 1315 + * We prevent pencil-mode highlighting of a filled square, unless 1316 + * we're using the cursor keys. So if the user has just filled in 1317 + * a square which we had a pencil-mode highlight in (by Undo, or 1318 + * by Redo, or by Solve), then we cancel the highlight. 1319 + */ 1320 + if (ui->hshow && ui->hpencil && !ui->hcursor && 1321 + newstate->grid[ui->hy * w + ui->hx] != 0) { 1322 + ui->hshow = false; 1323 + } 1324 + if (ui->hshow && ui->odn > 1) { 1325 + /* 1326 + * Reordering of rows or columns within the range of a 1327 + * multifill selection cancels the multifill and deselects 1328 + * everything. 1329 + */ 1330 + int i; 1331 + for (i = 0; i < ui->odn; i++) { 1332 + if (oldstate->sequence[ui->ohx + i*ui->odx] != 1333 + newstate->sequence[ui->ohx + i*ui->odx]) { 1334 + ui->hshow = false; 1335 + break; 1336 + } 1337 + if (oldstate->sequence[ui->ohy + i*ui->ody] != 1338 + newstate->sequence[ui->ohy + i*ui->ody]) { 1339 + ui->hshow = false; 1340 + break; 1341 + } 1342 + } 1343 + } else if (ui->hshow && 1344 + (newstate->sequence[ui->ohx] != ui->hx || 1345 + newstate->sequence[ui->ohy] != ui->hy)) { 1346 + /* 1347 + * Otherwise, reordering of the row or column containing the 1348 + * selection causes the selection to move with it. 1349 + */ 1350 + int i; 1351 + for (i = 0; i < w; i++) { 1352 + if (newstate->sequence[i] == ui->hx) 1353 + ui->ohx = i; 1354 + if (newstate->sequence[i] == ui->hy) 1355 + ui->ohy = i; 1356 + } 1357 + } 1358 + } 1359 + 1360 + static const char *current_key_label(const game_ui *ui, 1361 + const game_state *state, int button) 1362 + { 1363 + if (ui->hshow && button == CURSOR_SELECT) 1364 + return ui->hpencil ? "Ink" : "Pencil"; 1365 + if (ui->hshow && button == CURSOR_SELECT2) { 1366 + int w = state->par.w; 1367 + int i; 1368 + for (i = 0; i < ui->odn; i++) { 1369 + int x = state->sequence[ui->ohx + i*ui->odx]; 1370 + int y = state->sequence[ui->ohy + i*ui->ody]; 1371 + int index = y*w+x; 1372 + if (ui->hpencil && state->grid[index]) return ""; 1373 + if (state->common->immutable[index]) return ""; 1374 + } 1375 + return "Clear"; 1376 + } 1377 + return ""; 1378 + } 1379 + 1380 + #define PREFERRED_TILESIZE 48 1381 + #define TILESIZE (ds->tilesize) 1382 + #define BORDER (TILESIZE / 2) 1383 + #define LEGEND (TILESIZE) 1384 + #define GRIDEXTRA max((TILESIZE / 32),1) 1385 + #define COORD(x) ((x)*TILESIZE + BORDER + LEGEND) 1386 + #define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1) 1387 + 1388 + #define FLASH_TIME 0.4F 1389 + 1390 + #define DF_DIVIDER_TOP 0x1000 1391 + #define DF_DIVIDER_BOT 0x2000 1392 + #define DF_DIVIDER_LEFT 0x4000 1393 + #define DF_DIVIDER_RIGHT 0x8000 1394 + #define DF_HIGHLIGHT 0x0400 1395 + #define DF_HIGHLIGHT_PENCIL 0x0200 1396 + #define DF_IMMUTABLE 0x0100 1397 + #define DF_LEGEND 0x0080 1398 + #define DF_DIGIT_MASK 0x001F 1399 + 1400 + #define EF_DIGIT_SHIFT 5 1401 + #define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1) 1402 + #define EF_LEFT_SHIFT 0 1403 + #define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT) 1404 + #define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL) 1405 + #define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT) 1406 + #define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT)) 1407 + 1408 + struct game_drawstate { 1409 + game_params par; 1410 + int w, tilesize; 1411 + bool started; 1412 + long *tiles, *legend, *pencil, *errors; 1413 + long *errtmp; 1414 + digit *sequence; 1415 + }; 1416 + 1417 + static bool check_errors(const game_state *state, long *errors) 1418 + { 1419 + int w = state->par.w, a = w*w; 1420 + digit *grid = state->grid; 1421 + int i, j, k, x, y; 1422 + bool errs = false; 1423 + 1424 + /* 1425 + * To verify that we have a valid group table, it suffices to 1426 + * test latin-square-hood and associativity only. All the other 1427 + * group axioms follow from those two. 1428 + * 1429 + * Proof: 1430 + * 1431 + * Associativity is given; closure is obvious from latin- 1432 + * square-hood. We need to show that an identity exists and that 1433 + * every element has an inverse. 1434 + * 1435 + * Identity: take any element a. There will be some element e 1436 + * such that ea=a (in a latin square, every element occurs in 1437 + * every row and column, so a must occur somewhere in the a 1438 + * column, say on row e). For any other element b, there must 1439 + * exist x such that ax=b (same argument from latin-square-hood 1440 + * again), and then associativity gives us eb = e(ax) = (ea)x = 1441 + * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A 1442 + * similar argument tells us that there must be some f which is 1443 + * a right-identity, and then we show they are the same element 1444 + * by observing that ef must simultaneously equal e and equal f. 1445 + * 1446 + * Inverses: given any a, by the latin-square argument again, 1447 + * there must exist p and q such that pa=e and aq=e (i.e. left- 1448 + * and right-inverses). We can show these are equal by 1449 + * associativity: p = pe = p(aq) = (pa)q = eq = q. [] 1450 + */ 1451 + 1452 + if (errors) 1453 + for (i = 0; i < a; i++) 1454 + errors[i] = 0; 1455 + 1456 + for (y = 0; y < w; y++) { 1457 + unsigned long mask = 0, errmask = 0; 1458 + for (x = 0; x < w; x++) { 1459 + unsigned long bit = 1UL << grid[y*w+x]; 1460 + errmask |= (mask & bit); 1461 + mask |= bit; 1462 + } 1463 + 1464 + if (mask != (1 << (w+1)) - (1 << 1)) { 1465 + errs = true; 1466 + errmask &= ~1UL; 1467 + if (errors) { 1468 + for (x = 0; x < w; x++) 1469 + if (errmask & (1UL << grid[y*w+x])) 1470 + errors[y*w+x] |= EF_LATIN; 1471 + } 1472 + } 1473 + } 1474 + 1475 + for (x = 0; x < w; x++) { 1476 + unsigned long mask = 0, errmask = 0; 1477 + for (y = 0; y < w; y++) { 1478 + unsigned long bit = 1UL << grid[y*w+x]; 1479 + errmask |= (mask & bit); 1480 + mask |= bit; 1481 + } 1482 + 1483 + if (mask != (1 << (w+1)) - (1 << 1)) { 1484 + errs = true; 1485 + errmask &= ~1UL; 1486 + if (errors) { 1487 + for (y = 0; y < w; y++) 1488 + if (errmask & (1UL << grid[y*w+x])) 1489 + errors[y*w+x] |= EF_LATIN; 1490 + } 1491 + } 1492 + } 1493 + 1494 + for (i = 1; i < w; i++) 1495 + for (j = 1; j < w; j++) 1496 + for (k = 1; k < w; k++) 1497 + if (grid[i*w+j] && grid[j*w+k] && 1498 + grid[(grid[i*w+j]-1)*w+k] && 1499 + grid[i*w+(grid[j*w+k]-1)] && 1500 + grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) { 1501 + if (errors) { 1502 + int a = i+1, b = j+1, c = k+1; 1503 + int ab = grid[i*w+j], bc = grid[j*w+k]; 1504 + int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1); 1505 + /* 1506 + * If the appropriate error slot is already 1507 + * used for one of the squares, we don't 1508 + * fill either of them. 1509 + */ 1510 + if (!(errors[left] & EF_LEFT_MASK) && 1511 + !(errors[right] & EF_RIGHT_MASK)) { 1512 + long err; 1513 + err = a; 1514 + err = (err << EF_DIGIT_SHIFT) | b; 1515 + err = (err << EF_DIGIT_SHIFT) | c; 1516 + errors[left] |= err << EF_LEFT_SHIFT; 1517 + errors[right] |= err << EF_RIGHT_SHIFT; 1518 + } 1519 + } 1520 + errs = true; 1521 + } 1522 + 1523 + return errs; 1524 + } 1525 + 1526 + static int find_in_sequence(digit *seq, int len, digit n) 1527 + { 1528 + int i; 1529 + 1530 + for (i = 0; i < len; i++) 1531 + if (seq[i] == n) 1532 + return i; 1533 + 1534 + assert(!"Should never get here"); 1535 + return -1; 1536 + } 1537 + 1538 + static char *interpret_move(const game_state *state, game_ui *ui, 1539 + const game_drawstate *ds, 1540 + int x, int y, int button) 1541 + { 1542 + int w = state->par.w; 1543 + int tx, ty; 1544 + char buf[80]; 1545 + 1546 + button = STRIP_BUTTON_MODIFIERS(button); 1547 + 1548 + tx = FROMCOORD(x); 1549 + ty = FROMCOORD(y); 1550 + 1551 + if (ui->drag) { 1552 + if (IS_MOUSE_DRAG(button)) { 1553 + int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx); 1554 + ui->drag |= 4; /* some movement has happened */ 1555 + if (tcoord >= 0 && tcoord < w) { 1556 + ui->dragpos = tcoord; 1557 + return MOVE_UI_UPDATE; 1558 + } 1559 + } else if (IS_MOUSE_RELEASE(button)) { 1560 + if (ui->drag & 4) { 1561 + ui->drag = 0; /* end drag */ 1562 + if (state->sequence[ui->dragpos] == ui->dragnum) 1563 + return MOVE_UI_UPDATE; /* drag was a no-op overall */ 1564 + sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos); 1565 + return dupstr(buf); 1566 + } else { 1567 + ui->drag = 0; /* end 'drag' */ 1568 + if (ui->edgepos > 0 && ui->edgepos < w) { 1569 + sprintf(buf, "V%d,%d", 1570 + state->sequence[ui->edgepos-1], 1571 + state->sequence[ui->edgepos]); 1572 + return dupstr(buf); 1573 + } else 1574 + return MOVE_UI_UPDATE; /* no-op */ 1575 + } 1576 + } 1577 + } else if (IS_MOUSE_DOWN(button)) { 1578 + if (tx >= 0 && tx < w && ty >= 0 && ty < w) { 1579 + int otx = tx, oty = ty; 1580 + tx = state->sequence[tx]; 1581 + ty = state->sequence[ty]; 1582 + if (button == LEFT_BUTTON) { 1583 + if (tx == ui->hx && ty == ui->hy && 1584 + ui->hshow && !ui->hpencil) { 1585 + ui->hshow = false; 1586 + } else { 1587 + ui->hx = tx; 1588 + ui->hy = ty; 1589 + ui->ohx = otx; 1590 + ui->ohy = oty; 1591 + ui->odx = ui->ody = 0; 1592 + ui->odn = 1; 1593 + ui->hshow = !state->common->immutable[ty*w+tx]; 1594 + ui->hpencil = false; 1595 + } 1596 + ui->hcursor = false; 1597 + return MOVE_UI_UPDATE; 1598 + } 1599 + if (button == RIGHT_BUTTON) { 1600 + /* 1601 + * Pencil-mode highlighting for non filled squares. 1602 + */ 1603 + if (state->grid[ty*w+tx] == 0) { 1604 + if (tx == ui->hx && ty == ui->hy && 1605 + ui->hshow && ui->hpencil) { 1606 + ui->hshow = false; 1607 + } else { 1608 + ui->hpencil = true; 1609 + ui->hx = tx; 1610 + ui->hy = ty; 1611 + ui->ohx = otx; 1612 + ui->ohy = oty; 1613 + ui->odx = ui->ody = 0; 1614 + ui->odn = 1; 1615 + ui->hshow = true; 1616 + } 1617 + } else { 1618 + ui->hshow = false; 1619 + } 1620 + ui->hcursor = false; 1621 + return MOVE_UI_UPDATE; 1622 + } 1623 + } else if (tx >= 0 && tx < w && ty == -1) { 1624 + ui->drag = 2; 1625 + ui->dragnum = state->sequence[tx]; 1626 + ui->dragpos = tx; 1627 + ui->edgepos = FROMCOORD(x + TILESIZE/2); 1628 + return MOVE_UI_UPDATE; 1629 + } else if (ty >= 0 && ty < w && tx == -1) { 1630 + ui->drag = 1; 1631 + ui->dragnum = state->sequence[ty]; 1632 + ui->dragpos = ty; 1633 + ui->edgepos = FROMCOORD(y + TILESIZE/2); 1634 + return MOVE_UI_UPDATE; 1635 + } 1636 + } else if (IS_MOUSE_DRAG(button)) { 1637 + if (!ui->hpencil && 1638 + tx >= 0 && tx < w && ty >= 0 && ty < w && 1639 + abs(tx - ui->ohx) == abs(ty - ui->ohy)) { 1640 + ui->odn = abs(tx - ui->ohx) + 1; 1641 + ui->odx = (tx < ui->ohx ? -1 : +1); 1642 + ui->ody = (ty < ui->ohy ? -1 : +1); 1643 + } else { 1644 + ui->odx = ui->ody = 0; 1645 + ui->odn = 1; 1646 + } 1647 + return MOVE_UI_UPDATE; 1648 + } 1649 + 1650 + if (IS_CURSOR_MOVE(button)) { 1651 + int cx = find_in_sequence(state->sequence, w, ui->hx); 1652 + int cy = find_in_sequence(state->sequence, w, ui->hy); 1653 + move_cursor(button, &cx, &cy, w, w, false, NULL); 1654 + ui->hx = state->sequence[cx]; 1655 + ui->hy = state->sequence[cy]; 1656 + ui->hshow = true; 1657 + ui->hcursor = true; 1658 + ui->ohx = cx; 1659 + ui->ohy = cy; 1660 + ui->odx = ui->ody = 0; 1661 + ui->odn = 1; 1662 + return MOVE_UI_UPDATE; 1663 + } 1664 + if (ui->hshow && 1665 + (button == CURSOR_SELECT)) { 1666 + ui->hpencil = !ui->hpencil; 1667 + ui->hcursor = true; 1668 + return MOVE_UI_UPDATE; 1669 + } 1670 + 1671 + if (ui->hshow && 1672 + ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) || 1673 + button == CURSOR_SELECT2 || button == '\b')) { 1674 + int n = FROMCHAR(button, state->par.id); 1675 + int i, buflen; 1676 + char *movebuf; 1677 + 1678 + if (button == CURSOR_SELECT2 || button == '\b') 1679 + n = 0; 1680 + 1681 + for (i = 0; i < ui->odn; i++) { 1682 + int x = state->sequence[ui->ohx + i*ui->odx]; 1683 + int y = state->sequence[ui->ohy + i*ui->ody]; 1684 + int index = y*w+x; 1685 + 1686 + /* 1687 + * Can't make pencil marks in a filled square. This can only 1688 + * become highlighted if we're using cursor keys. 1689 + */ 1690 + if (ui->hpencil && state->grid[index]) 1691 + return NULL; 1692 + 1693 + /* 1694 + * Can't do anything to an immutable square. Exception: 1695 + * trying to set it to what it already was is OK (so that 1696 + * multifilling can set a whole diagonal to a without 1697 + * having to detour round the one immutable square in the 1698 + * middle that already said a). 1699 + */ 1700 + if (!ui->hpencil && state->grid[index] == n) 1701 + /* OK even if it is immutable */; 1702 + else if (state->common->immutable[index]) 1703 + return NULL; 1704 + } 1705 + 1706 + movebuf = snewn(80 * ui->odn, char); 1707 + buflen = sprintf(movebuf, "%c%d,%d,%d", 1708 + (char)(ui->hpencil && n > 0 ? 'P' : 'R'), 1709 + ui->hx, ui->hy, n); 1710 + for (i = 1; i < ui->odn; i++) { 1711 + assert(buflen < i*80); 1712 + buflen += sprintf(movebuf + buflen, "+%d,%d", 1713 + state->sequence[ui->ohx + i*ui->odx], 1714 + state->sequence[ui->ohy + i*ui->ody]); 1715 + } 1716 + movebuf = sresize(movebuf, buflen+1, char); 1717 + 1718 + /* 1719 + * Hide the highlight after a keypress, if it was mouse- 1720 + * generated. Also, don't hide it if this move has changed 1721 + * pencil marks and the user preference says not to hide the 1722 + * highlight in that situation. 1723 + */ 1724 + if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight)) 1725 + ui->hshow = false; 1726 + 1727 + return movebuf; 1728 + } 1729 + 1730 + if (button == 'M' || button == 'm') 1731 + return dupstr("M"); 1732 + 1733 + return NULL; 1734 + } 1735 + 1736 + static game_state *execute_move(const game_state *from, const char *move) 1737 + { 1738 + int w = from->par.w, a = w*w; 1739 + game_state *ret; 1740 + int x, y, i, j, n, pos; 1741 + 1742 + if (move[0] == 'S') { 1743 + ret = dup_game(from); 1744 + ret->completed = ret->cheated = true; 1745 + 1746 + for (i = 0; i < a; i++) { 1747 + if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) { 1748 + free_game(ret); 1749 + return NULL; 1750 + } 1751 + ret->grid[i] = FROMCHAR(move[i+1], from->par.id); 1752 + ret->pencil[i] = 0; 1753 + } 1754 + 1755 + if (move[a+1] != '\0') { 1756 + free_game(ret); 1757 + return NULL; 1758 + } 1759 + 1760 + return ret; 1761 + } else if ((move[0] == 'P' || move[0] == 'R') && 1762 + sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 && 1763 + n >= 0 && n <= w) { 1764 + const char *mp = move + 1 + pos; 1765 + bool pencil = (move[0] == 'P'); 1766 + ret = dup_game(from); 1767 + 1768 + while (1) { 1769 + if (x < 0 || x >= w || y < 0 || y >= w) { 1770 + free_game(ret); 1771 + return NULL; 1772 + } 1773 + if (from->common->immutable[y*w+x] && 1774 + !(!pencil && from->grid[y*w+x] == n)) 1775 + return NULL; 1776 + 1777 + if (move[0] == 'P' && n > 0) { 1778 + ret->pencil[y*w+x] ^= 1 << n; 1779 + } else { 1780 + ret->grid[y*w+x] = n; 1781 + ret->pencil[y*w+x] = 0; 1782 + } 1783 + 1784 + if (!*mp) 1785 + break; 1786 + 1787 + if (*mp != '+') 1788 + return NULL; 1789 + if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2) 1790 + return NULL; 1791 + mp += pos; 1792 + } 1793 + 1794 + if (!ret->completed && !check_errors(ret, NULL)) 1795 + ret->completed = true; 1796 + 1797 + return ret; 1798 + } else if (move[0] == 'M') { 1799 + /* 1800 + * Fill in absolutely all pencil marks everywhere. (I 1801 + * wouldn't use this for actual play, but it's a handy 1802 + * starting point when following through a set of 1803 + * diagnostics output by the standalone solver.) 1804 + */ 1805 + ret = dup_game(from); 1806 + for (i = 0; i < a; i++) { 1807 + if (!ret->grid[i]) 1808 + ret->pencil[i] = (1 << (w+1)) - (1 << 1); 1809 + } 1810 + return ret; 1811 + } else if (move[0] == 'D' && 1812 + sscanf(move+1, "%d,%d", &x, &y) == 2) { 1813 + /* 1814 + * Reorder the rows and columns so that digit x is in position 1815 + * y. 1816 + */ 1817 + ret = dup_game(from); 1818 + for (i = j = 0; i < w; i++) { 1819 + if (i == y) { 1820 + ret->sequence[i] = x; 1821 + } else { 1822 + if (from->sequence[j] == x) 1823 + j++; 1824 + ret->sequence[i] = from->sequence[j++]; 1825 + } 1826 + } 1827 + /* 1828 + * Eliminate any obsoleted dividers. 1829 + */ 1830 + for (x = 0; x < w; x++) { 1831 + int i = ret->sequence[x]; 1832 + int j = (x+1 < w ? ret->sequence[x+1] : -1); 1833 + if (ret->dividers[i] != j) 1834 + ret->dividers[i] = -1; 1835 + } 1836 + return ret; 1837 + } else if (move[0] == 'V' && 1838 + sscanf(move+1, "%d,%d", &i, &j) == 2) { 1839 + ret = dup_game(from); 1840 + if (ret->dividers[i] == j) 1841 + ret->dividers[i] = -1; 1842 + else 1843 + ret->dividers[i] = j; 1844 + return ret; 1845 + } else 1846 + return NULL; /* couldn't parse move string */ 1847 + } 1848 + 1849 + /* ---------------------------------------------------------------------- 1850 + * Drawing routines. 1851 + */ 1852 + 1853 + #define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND) 1854 + 1855 + static void game_compute_size(const game_params *params, int tilesize, 1856 + const game_ui *ui, int *x, int *y) 1857 + { 1858 + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1859 + struct { int tilesize; } ads, *ds = &ads; 1860 + ads.tilesize = tilesize; 1861 + 1862 + *x = *y = SIZE(params->w); 1863 + } 1864 + 1865 + static void game_set_size(drawing *dr, game_drawstate *ds, 1866 + const game_params *params, int tilesize) 1867 + { 1868 + ds->tilesize = tilesize; 1869 + } 1870 + 1871 + static float *game_colours(frontend *fe, int *ncolours) 1872 + { 1873 + float *ret = snewn(3 * NCOLOURS, float); 1874 + 1875 + frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 1876 + 1877 + ret[COL_GRID * 3 + 0] = 0.0F; 1878 + ret[COL_GRID * 3 + 1] = 0.0F; 1879 + ret[COL_GRID * 3 + 2] = 0.0F; 1880 + 1881 + ret[COL_USER * 3 + 0] = 0.0F; 1882 + ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1]; 1883 + ret[COL_USER * 3 + 2] = 0.0F; 1884 + 1885 + ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0]; 1886 + ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1]; 1887 + ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2]; 1888 + 1889 + ret[COL_ERROR * 3 + 0] = 1.0F; 1890 + ret[COL_ERROR * 3 + 1] = 0.0F; 1891 + ret[COL_ERROR * 3 + 2] = 0.0F; 1892 + 1893 + ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; 1894 + ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; 1895 + ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; 1896 + 1897 + ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0]; 1898 + ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1]; 1899 + ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2]; 1900 + 1901 + *ncolours = NCOLOURS; 1902 + return ret; 1903 + } 1904 + 1905 + static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 1906 + { 1907 + int w = state->par.w, a = w*w; 1908 + struct game_drawstate *ds = snew(struct game_drawstate); 1909 + int i; 1910 + 1911 + ds->w = w; 1912 + ds->par = state->par; /* structure copy */ 1913 + ds->tilesize = 0; 1914 + ds->started = false; 1915 + ds->tiles = snewn(a, long); 1916 + ds->legend = snewn(w, long); 1917 + ds->pencil = snewn(a, long); 1918 + ds->errors = snewn(a, long); 1919 + ds->sequence = snewn(a, digit); 1920 + for (i = 0; i < a; i++) 1921 + ds->tiles[i] = ds->pencil[i] = -1; 1922 + for (i = 0; i < w; i++) 1923 + ds->legend[i] = -1; 1924 + ds->errtmp = snewn(a, long); 1925 + 1926 + return ds; 1927 + } 1928 + 1929 + static void game_free_drawstate(drawing *dr, game_drawstate *ds) 1930 + { 1931 + sfree(ds->tiles); 1932 + sfree(ds->pencil); 1933 + sfree(ds->errors); 1934 + sfree(ds->errtmp); 1935 + sfree(ds->sequence); 1936 + sfree(ds); 1937 + } 1938 + 1939 + static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile, 1940 + long pencil, long error) 1941 + { 1942 + int w = ds->w /* , a = w*w */; 1943 + int tx, ty, tw, th; 1944 + int cx, cy, cw, ch; 1945 + char str[64]; 1946 + 1947 + tx = BORDER + LEGEND + x * TILESIZE + 1; 1948 + ty = BORDER + LEGEND + y * TILESIZE + 1; 1949 + 1950 + cx = tx; 1951 + cy = ty; 1952 + cw = tw = TILESIZE-1; 1953 + ch = th = TILESIZE-1; 1954 + 1955 + if (tile & DF_LEGEND) { 1956 + cx += TILESIZE/10; 1957 + cy += TILESIZE/10; 1958 + cw -= TILESIZE/5; 1959 + ch -= TILESIZE/5; 1960 + tile |= DF_IMMUTABLE; 1961 + } 1962 + 1963 + clip(dr, cx, cy, cw, ch); 1964 + 1965 + /* background needs erasing */ 1966 + draw_rect(dr, cx, cy, cw, ch, 1967 + (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : 1968 + (x == y) ? COL_DIAGONAL : COL_BACKGROUND); 1969 + 1970 + /* dividers */ 1971 + if (tile & DF_DIVIDER_TOP) 1972 + draw_rect(dr, cx, cy, cw, 1, COL_GRID); 1973 + if (tile & DF_DIVIDER_BOT) 1974 + draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID); 1975 + if (tile & DF_DIVIDER_LEFT) 1976 + draw_rect(dr, cx, cy, 1, ch, COL_GRID); 1977 + if (tile & DF_DIVIDER_RIGHT) 1978 + draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID); 1979 + 1980 + /* pencil-mode highlight */ 1981 + if (tile & DF_HIGHLIGHT_PENCIL) { 1982 + int coords[6]; 1983 + coords[0] = cx; 1984 + coords[1] = cy; 1985 + coords[2] = cx+cw/2; 1986 + coords[3] = cy; 1987 + coords[4] = cx; 1988 + coords[5] = cy+ch/2; 1989 + draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); 1990 + } 1991 + 1992 + /* new number needs drawing? */ 1993 + if (tile & DF_DIGIT_MASK) { 1994 + str[1] = '\0'; 1995 + str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id); 1996 + draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1997 + FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, 1998 + (error & EF_LATIN) ? COL_ERROR : 1999 + (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str); 2000 + 2001 + if (error & EF_LEFT_MASK) { 2002 + int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; 2003 + int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; 2004 + int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK; 2005 + char buf[10]; 2006 + sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id), 2007 + TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); 2008 + draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6, 2009 + FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, 2010 + COL_ERROR, buf); 2011 + } 2012 + if (error & EF_RIGHT_MASK) { 2013 + int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; 2014 + int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; 2015 + int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK; 2016 + char buf[10]; 2017 + sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id), 2018 + TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); 2019 + draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6, 2020 + FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, 2021 + COL_ERROR, buf); 2022 + } 2023 + } else { 2024 + int i, j, npencil; 2025 + int pl, pr, pt, pb; 2026 + float bestsize; 2027 + int pw, ph, minph, pbest, fontsize; 2028 + 2029 + /* Count the pencil marks required. */ 2030 + for (i = 1, npencil = 0; i <= w; i++) 2031 + if (pencil & (1 << i)) 2032 + npencil++; 2033 + if (npencil) { 2034 + 2035 + minph = 2; 2036 + 2037 + /* 2038 + * Determine the bounding rectangle within which we're going 2039 + * to put the pencil marks. 2040 + */ 2041 + /* Start with the whole square */ 2042 + pl = tx + GRIDEXTRA; 2043 + pr = pl + TILESIZE - GRIDEXTRA; 2044 + pt = ty + GRIDEXTRA; 2045 + pb = pt + TILESIZE - GRIDEXTRA; 2046 + 2047 + /* 2048 + * We arrange our pencil marks in a grid layout, with 2049 + * the number of rows and columns adjusted to allow the 2050 + * maximum font size. 2051 + * 2052 + * So now we work out what the grid size ought to be. 2053 + */ 2054 + bestsize = 0.0; 2055 + pbest = 0; 2056 + /* Minimum */ 2057 + for (pw = 3; pw < max(npencil,4); pw++) { 2058 + float fw, fh, fs; 2059 + 2060 + ph = (npencil + pw - 1) / pw; 2061 + ph = max(ph, minph); 2062 + fw = (pr - pl) / (float)pw; 2063 + fh = (pb - pt) / (float)ph; 2064 + fs = min(fw, fh); 2065 + if (fs > bestsize) { 2066 + bestsize = fs; 2067 + pbest = pw; 2068 + } 2069 + } 2070 + assert(pbest > 0); 2071 + pw = pbest; 2072 + ph = (npencil + pw - 1) / pw; 2073 + ph = max(ph, minph); 2074 + 2075 + /* 2076 + * Now we've got our grid dimensions, work out the pixel 2077 + * size of a grid element, and round it to the nearest 2078 + * pixel. (We don't want rounding errors to make the 2079 + * grid look uneven at low pixel sizes.) 2080 + */ 2081 + fontsize = min((pr - pl) / pw, (pb - pt) / ph); 2082 + 2083 + /* 2084 + * Centre the resulting figure in the square. 2085 + */ 2086 + pl = tx + (TILESIZE - fontsize * pw) / 2; 2087 + pt = ty + (TILESIZE - fontsize * ph) / 2; 2088 + 2089 + /* 2090 + * Now actually draw the pencil marks. 2091 + */ 2092 + for (i = 1, j = 0; i <= w; i++) 2093 + if (pencil & (1 << i)) { 2094 + int dx = j % pw, dy = j / pw; 2095 + 2096 + str[1] = '\0'; 2097 + str[0] = TOCHAR(i, ds->par.id); 2098 + draw_text(dr, pl + fontsize * (2*dx+1) / 2, 2099 + pt + fontsize * (2*dy+1) / 2, 2100 + FONT_VARIABLE, fontsize, 2101 + ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str); 2102 + j++; 2103 + } 2104 + } 2105 + } 2106 + 2107 + unclip(dr); 2108 + 2109 + draw_update(dr, cx, cy, cw, ch); 2110 + } 2111 + 2112 + static void game_redraw(drawing *dr, game_drawstate *ds, 2113 + const game_state *oldstate, const game_state *state, 2114 + int dir, const game_ui *ui, 2115 + float animtime, float flashtime) 2116 + { 2117 + int w = state->par.w /*, a = w*w */; 2118 + int x, y, i, j; 2119 + 2120 + if (!ds->started) { 2121 + /* 2122 + * Big containing rectangle. 2123 + */ 2124 + draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA, 2125 + w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2, 2126 + COL_GRID); 2127 + 2128 + draw_update(dr, 0, 0, SIZE(w), SIZE(w)); 2129 + 2130 + ds->started = true; 2131 + } 2132 + 2133 + check_errors(state, ds->errtmp); 2134 + 2135 + /* 2136 + * Construct a modified version of state->sequence which takes 2137 + * into account an unfinished drag operation. 2138 + */ 2139 + if (ui->drag) { 2140 + x = ui->dragnum; 2141 + y = ui->dragpos; 2142 + } else { 2143 + x = y = -1; 2144 + } 2145 + for (i = j = 0; i < w; i++) { 2146 + if (i == y) { 2147 + ds->sequence[i] = x; 2148 + } else { 2149 + if (state->sequence[j] == x) 2150 + j++; 2151 + ds->sequence[i] = state->sequence[j++]; 2152 + } 2153 + } 2154 + 2155 + /* 2156 + * Draw the table legend. 2157 + */ 2158 + for (x = 0; x < w; x++) { 2159 + int sx = ds->sequence[x]; 2160 + long tile = (sx+1) | DF_LEGEND; 2161 + if (ds->legend[x] != tile) { 2162 + ds->legend[x] = tile; 2163 + draw_tile(dr, ds, -1, x, tile, 0, 0); 2164 + draw_tile(dr, ds, x, -1, tile, 0, 0); 2165 + } 2166 + } 2167 + 2168 + for (y = 0; y < w; y++) { 2169 + int sy = ds->sequence[y]; 2170 + for (x = 0; x < w; x++) { 2171 + long tile = 0L, pencil = 0L, error; 2172 + int sx = ds->sequence[x]; 2173 + 2174 + if (state->grid[sy*w+sx]) 2175 + tile = state->grid[sy*w+sx]; 2176 + else 2177 + pencil = (long)state->pencil[sy*w+sx]; 2178 + 2179 + if (state->common->immutable[sy*w+sx]) 2180 + tile |= DF_IMMUTABLE; 2181 + 2182 + if ((ui->drag == 5 && ui->dragnum == sy) || 2183 + (ui->drag == 6 && ui->dragnum == sx)) { 2184 + tile |= DF_HIGHLIGHT; 2185 + } else if (ui->hshow) { 2186 + int i = abs(x - ui->ohx); 2187 + bool highlight = false; 2188 + if (ui->odn > 1) { 2189 + /* 2190 + * When a diagonal multifill selection is shown, 2191 + * we show it in its original grid position 2192 + * regardless of in-progress row/col drags. Moving 2193 + * every square about would be horrible. 2194 + */ 2195 + if (i >= 0 && i < ui->odn && 2196 + x == ui->ohx + i*ui->odx && 2197 + y == ui->ohy + i*ui->ody) 2198 + highlight = true; 2199 + } else { 2200 + /* 2201 + * For a single square, we move its highlight 2202 + * around with the drag. 2203 + */ 2204 + highlight = (ui->hx == sx && ui->hy == sy); 2205 + } 2206 + if (highlight) 2207 + tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT); 2208 + } 2209 + 2210 + if (flashtime > 0 && 2211 + (flashtime <= FLASH_TIME/3 || 2212 + flashtime >= FLASH_TIME*2/3)) 2213 + tile |= DF_HIGHLIGHT; /* completion flash */ 2214 + 2215 + if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy) 2216 + tile |= DF_DIVIDER_TOP; 2217 + if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1]) 2218 + tile |= DF_DIVIDER_BOT; 2219 + if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx) 2220 + tile |= DF_DIVIDER_LEFT; 2221 + if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1]) 2222 + tile |= DF_DIVIDER_RIGHT; 2223 + 2224 + error = ds->errtmp[sy*w+sx]; 2225 + 2226 + if (ds->tiles[y*w+x] != tile || 2227 + ds->pencil[y*w+x] != pencil || 2228 + ds->errors[y*w+x] != error) { 2229 + ds->tiles[y*w+x] = tile; 2230 + ds->pencil[y*w+x] = pencil; 2231 + ds->errors[y*w+x] = error; 2232 + draw_tile(dr, ds, x, y, tile, pencil, error); 2233 + } 2234 + } 2235 + } 2236 + } 2237 + 2238 + static float game_anim_length(const game_state *oldstate, 2239 + const game_state *newstate, int dir, game_ui *ui) 2240 + { 2241 + return 0.0F; 2242 + } 2243 + 2244 + static float game_flash_length(const game_state *oldstate, 2245 + const game_state *newstate, int dir, game_ui *ui) 2246 + { 2247 + if (!oldstate->completed && newstate->completed && 2248 + !oldstate->cheated && !newstate->cheated) 2249 + return FLASH_TIME; 2250 + return 0.0F; 2251 + } 2252 + 2253 + static void game_get_cursor_location(const game_ui *ui, 2254 + const game_drawstate *ds, 2255 + const game_state *state, 2256 + const game_params *params, 2257 + int *x, int *y, int *w, int *h) 2258 + { 2259 + } 2260 + 2261 + static int game_status(const game_state *state) 2262 + { 2263 + return state->completed ? +1 : 0; 2264 + } 2265 + 2266 + static bool game_timing_state(const game_state *state, game_ui *ui) 2267 + { 2268 + if (state->completed) 2269 + return false; 2270 + return true; 2271 + } 2272 + 2273 + static void game_print_size(const game_params *params, const game_ui *ui, 2274 + float *x, float *y) 2275 + { 2276 + int pw, ph; 2277 + 2278 + /* 2279 + * We use 9mm squares by default, like Solo. 2280 + */ 2281 + game_compute_size(params, 900, ui, &pw, &ph); 2282 + *x = pw / 100.0F; 2283 + *y = ph / 100.0F; 2284 + } 2285 + 2286 + static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 2287 + int tilesize) 2288 + { 2289 + int w = state->par.w; 2290 + int ink = print_mono_colour(dr, 0); 2291 + int x, y; 2292 + 2293 + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2294 + game_drawstate ads, *ds = &ads; 2295 + game_set_size(dr, ds, NULL, tilesize); 2296 + 2297 + /* 2298 + * Border. 2299 + */ 2300 + print_line_width(dr, 3 * TILESIZE / 40); 2301 + draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND, 2302 + w*TILESIZE, w*TILESIZE, ink); 2303 + 2304 + /* 2305 + * Legend on table. 2306 + */ 2307 + for (x = 0; x < w; x++) { 2308 + char str[2]; 2309 + str[1] = '\0'; 2310 + str[0] = TOCHAR(x+1, state->par.id); 2311 + draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, 2312 + BORDER + TILESIZE/2, 2313 + FONT_VARIABLE, TILESIZE/2, 2314 + ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); 2315 + draw_text(dr, BORDER + TILESIZE/2, 2316 + BORDER+LEGEND + x*TILESIZE + TILESIZE/2, 2317 + FONT_VARIABLE, TILESIZE/2, 2318 + ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); 2319 + } 2320 + 2321 + /* 2322 + * Main grid. 2323 + */ 2324 + for (x = 1; x < w; x++) { 2325 + print_line_width(dr, TILESIZE / 40); 2326 + draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND, 2327 + BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink); 2328 + } 2329 + for (y = 1; y < w; y++) { 2330 + print_line_width(dr, TILESIZE / 40); 2331 + draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE, 2332 + BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink); 2333 + } 2334 + 2335 + /* 2336 + * Numbers. 2337 + */ 2338 + for (y = 0; y < w; y++) 2339 + for (x = 0; x < w; x++) 2340 + if (state->grid[y*w+x]) { 2341 + char str[2]; 2342 + str[1] = '\0'; 2343 + str[0] = TOCHAR(state->grid[y*w+x], state->par.id); 2344 + draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, 2345 + BORDER+LEGEND + y*TILESIZE + TILESIZE/2, 2346 + FONT_VARIABLE, TILESIZE/2, 2347 + ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); 2348 + } 2349 + } 2350 + 2351 + #ifdef COMBINED 2352 + #define thegame group 2353 + #endif 2354 + 2355 + const struct game thegame = { 2356 + "Group", NULL, NULL, 2357 + default_params, 2358 + game_fetch_preset, NULL, 2359 + decode_params, 2360 + encode_params, 2361 + free_params, 2362 + dup_params, 2363 + true, game_configure, custom_params, 2364 + validate_params, 2365 + new_game_desc, 2366 + validate_desc, 2367 + new_game, 2368 + dup_game, 2369 + free_game, 2370 + true, solve_game, 2371 + true, game_can_format_as_text_now, game_text_format, 2372 + get_prefs, set_prefs, 2373 + new_ui, 2374 + free_ui, 2375 + NULL, /* encode_ui */ 2376 + NULL, /* decode_ui */ 2377 + NULL, /* game_request_keys */ 2378 + game_changed_state, 2379 + current_key_label, 2380 + interpret_move, 2381 + execute_move, 2382 + PREFERRED_TILESIZE, game_compute_size, game_set_size, 2383 + game_colours, 2384 + game_new_drawstate, 2385 + game_free_drawstate, 2386 + game_redraw, 2387 + game_anim_length, 2388 + game_flash_length, 2389 + game_get_cursor_location, 2390 + game_status, 2391 + true, false, game_print_size, game_print, 2392 + false, /* wants_statusbar */ 2393 + false, game_timing_state, 2394 + REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ 2395 + }; 2396 + 2397 + #ifdef STANDALONE_SOLVER 2398 + 2399 + #include <stdarg.h> 2400 + 2401 + int main(int argc, char **argv) 2402 + { 2403 + game_params *p; 2404 + game_state *s; 2405 + char *id = NULL, *desc; 2406 + const char *err; 2407 + digit *grid; 2408 + bool grade = false; 2409 + int ret, diff; 2410 + bool really_show_working = false; 2411 + 2412 + while (--argc > 0) { 2413 + char *p = *++argv; 2414 + if (!strcmp(p, "-v")) { 2415 + really_show_working = true; 2416 + } else if (!strcmp(p, "-g")) { 2417 + grade = true; 2418 + } else if (*p == '-') { 2419 + fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); 2420 + return 1; 2421 + } else { 2422 + id = p; 2423 + } 2424 + } 2425 + 2426 + if (!id) { 2427 + fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]); 2428 + return 1; 2429 + } 2430 + 2431 + desc = strchr(id, ':'); 2432 + if (!desc) { 2433 + fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); 2434 + return 1; 2435 + } 2436 + *desc++ = '\0'; 2437 + 2438 + p = default_params(); 2439 + decode_params(p, id); 2440 + err = validate_desc(p, desc); 2441 + if (err) { 2442 + fprintf(stderr, "%s: %s\n", argv[0], err); 2443 + return 1; 2444 + } 2445 + s = new_game(NULL, p, desc); 2446 + 2447 + grid = snewn(p->w * p->w, digit); 2448 + 2449 + /* 2450 + * When solving a Normal puzzle, we don't want to bother the 2451 + * user with Hard-level deductions. For this reason, we grade 2452 + * the puzzle internally before doing anything else. 2453 + */ 2454 + ret = -1; /* placate optimiser */ 2455 + solver_show_working = 0; 2456 + for (diff = 0; diff < DIFFCOUNT; diff++) { 2457 + memcpy(grid, s->grid, p->w * p->w); 2458 + ret = solver(&s->par, grid, diff); 2459 + if (ret <= diff) 2460 + break; 2461 + } 2462 + 2463 + if (diff == DIFFCOUNT) { 2464 + if (really_show_working) { 2465 + solver_show_working = true; 2466 + memcpy(grid, s->grid, p->w * p->w); 2467 + ret = solver(&s->par, grid, DIFFCOUNT - 1); 2468 + } 2469 + if (grade) 2470 + printf("Difficulty rating: ambiguous\n"); 2471 + else 2472 + printf("Unable to find a unique solution\n"); 2473 + } else { 2474 + if (grade) { 2475 + if (ret == diff_impossible) 2476 + printf("Difficulty rating: impossible (no solution exists)\n"); 2477 + else 2478 + printf("Difficulty rating: %s\n", group_diffnames[ret]); 2479 + } else { 2480 + solver_show_working = really_show_working; 2481 + memcpy(grid, s->grid, p->w * p->w); 2482 + ret = solver(&s->par, grid, diff); 2483 + if (ret != diff) 2484 + printf("Puzzle is inconsistent\n"); 2485 + else { 2486 + memcpy(s->grid, grid, p->w * p->w); 2487 + fputs(game_text_format(s), stdout); 2488 + } 2489 + } 2490 + } 2491 + 2492 + return 0; 2493 + } 2494 + 2495 + #endif 2496 + 2497 + /* vim: set shiftwidth=4 tabstop=8: */
+97
apps/plugins/puzzles/src/unfinished/group.gap
··· 1 + # run this file with 2 + # gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr 3 + 4 + Print("/* ----- data generated by group.gap begins ----- */\n\n"); 5 + Print("struct group {\n unsigned long autosize;\n"); 6 + Print(" int order, ngens;\n const char *gens;\n};\n"); 7 + Print("struct groups {\n int ngroups;\n"); 8 + Print(" const struct group *groups;\n};\n\n"); 9 + Print("static const struct group groupdata[] = {\n"); 10 + offsets := [0]; 11 + offset := 0; 12 + for n in [2..26] do 13 + Print(" /* order ", n, " */\n"); 14 + for G in AllSmallGroups(n) do 15 + 16 + # Construct a representation of the group G as a subgroup 17 + # of a permutation group, and find its generators in that 18 + # group. 19 + 20 + # GAP has the 'IsomorphismPermGroup' function, but I don't want 21 + # to use it because it doesn't guarantee that the permutation 22 + # representation of the group forms a Cayley table. For example, 23 + # C_4 could be represented as a subgroup of S_4 in many ways, 24 + # and not all of them work: the group generated by (12) and (34) 25 + # is clearly isomorphic to C_4 but its four elements do not form 26 + # a Cayley table. The group generated by (12)(34) and (13)(24) 27 + # is OK, though. 28 + # 29 + # Hence I construct the permutation representation _as_ the 30 + # Cayley table, and then pick generators of that. This 31 + # guarantees that when we rebuild the full group by BFS in 32 + # group.c, we will end up with the right thing. 33 + 34 + ge := Elements(G); 35 + gi := []; 36 + for g in ge do 37 + gr := []; 38 + for h in ge do 39 + k := g*h; 40 + for i in [1..n] do 41 + if k = ge[i] then 42 + Add(gr, i); 43 + fi; 44 + od; 45 + od; 46 + Add(gi, PermList(gr)); 47 + od; 48 + 49 + # GAP has the 'GeneratorsOfGroup' function, but we don't want to 50 + # use it because it's bad at picking generators - it thinks the 51 + # generators of C_4 are [ (1,2)(3,4), (1,3,2,4) ] and that those 52 + # of C_6 are [ (1,2,3)(4,5,6), (1,4)(2,5)(3,6) ] ! 53 + 54 + gl := ShallowCopy(Elements(gi)); 55 + Sort(gl, function(v,w) return Order(v) > Order(w); end); 56 + 57 + gens := []; 58 + for x in gl do 59 + if gens = [] or not (x in gp) then 60 + Add(gens, x); 61 + gp := GroupWithGenerators(gens); 62 + fi; 63 + od; 64 + 65 + # Construct the C representation of the group generators. 66 + s := []; 67 + for x in gens do 68 + if Size(s) > 0 then 69 + Add(s, '"'); 70 + Add(s, ' '); 71 + Add(s, '"'); 72 + fi; 73 + sep := "\\0"; 74 + for i in ListPerm(x) do 75 + chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 76 + Add(s, chars[i]); 77 + od; 78 + od; 79 + s := JoinStringsWithSeparator([" {", String(Size(AutomorphismGroup(G))), 80 + "L, ", String(Size(G)), 81 + ", ", String(Size(gens)), 82 + ", \"", s, "\"},\n"],""); 83 + Print(s); 84 + offset := offset + 1; 85 + od; 86 + Add(offsets, offset); 87 + od; 88 + Print("};\n\nstatic const struct groups groups[] = {\n"); 89 + Print(" {0, NULL}, /* trivial case: 0 */\n"); 90 + Print(" {0, NULL}, /* trivial case: 1 */\n"); 91 + n := 2; 92 + for i in [1..Size(offsets)-1] do 93 + Print(" {", offsets[i+1] - offsets[i], ", groupdata+", 94 + offsets[i], "}, /* ", i+1, " */\n"); 95 + od; 96 + Print("};\n\n/* ----- data generated by group.gap ends ----- */\n"); 97 + quit;
+1294
apps/plugins/puzzles/src/unfinished/numgame.c
··· 1 + /* 2 + * This program implements a breadth-first search which 3 + * exhaustively solves the Countdown numbers game, and related 4 + * games with slightly different rule sets such as `Flippo'. 5 + * 6 + * Currently it is simply a standalone command-line utility to 7 + * which you provide a set of numbers and it tells you everything 8 + * it can make together with how many different ways it can be 9 + * made. I would like ultimately to turn it into the generator for 10 + * a Puzzles puzzle, but I haven't even started on writing a 11 + * Puzzles user interface yet. 12 + */ 13 + 14 + /* 15 + * TODO: 16 + * 17 + * - start thinking about difficulty ratings 18 + * + anything involving associative operations will be flagged 19 + * as many-paths because of the associative options (e.g. 20 + * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This 21 + * is probably a _good_ thing, since those are unusually 22 + * easy. 23 + * + tree-structured calculations ((a*b)/(c+d)) have multiple 24 + * paths because the independent branches of the tree can be 25 + * evaluated in either order, whereas straight-line 26 + * calculations with no branches will be considered easier. 27 + * Can we do anything about this? It's certainly not clear to 28 + * me that tree-structure calculations are _easier_, although 29 + * I'm also not convinced they're harder. 30 + * + I think for a realistic difficulty assessment we must also 31 + * consider the `obviousness' of the arithmetic operations in 32 + * some heuristic sense, and also (in Countdown) how many 33 + * numbers ended up being used. 34 + * - actually try some generations 35 + * - at this point we're probably ready to start on the Puzzles 36 + * integration. 37 + */ 38 + 39 + #include <stdio.h> 40 + #include <string.h> 41 + #include <limits.h> 42 + #include <assert.h> 43 + #ifdef NO_TGMATH_H 44 + # include <math.h> 45 + #else 46 + # include <tgmath.h> 47 + #endif 48 + 49 + #include "puzzles.h" 50 + #include "tree234.h" 51 + 52 + /* 53 + * To search for numbers we can make, we employ a breadth-first 54 + * search across the space of sets of input numbers. That is, for 55 + * example, we start with the set (3,6,25,50,75,100); we apply 56 + * moves which involve combining two numbers (e.g. adding the 50 57 + * and the 75 takes us to the set (3,6,25,100,125); and then we see 58 + * if we ever end up with a set containing (say) 952. 59 + * 60 + * If the rules are changed so that all the numbers must be used, 61 + * this is easy to adjust to: we simply see if we end up with a set 62 + * containing _only_ (say) 952. 63 + * 64 + * Obviously, we can vary the rules about permitted arithmetic 65 + * operations simply by altering the set of valid moves in the bfs. 66 + * However, there's one common rule in this sort of puzzle which 67 + * takes a little more thought, and that's _concatenation_. For 68 + * example, if you are given (say) four 4s and required to make 10, 69 + * you are permitted to combine two of the 4s into a 44 to begin 70 + * with, making (44-4)/4 = 10. However, you are generally not 71 + * allowed to concatenate two numbers that _weren't_ both in the 72 + * original input set (you couldn't multiply two 4s to get 16 and 73 + * then concatenate a 4 on to it to make 164), so concatenation is 74 + * not an operation which is valid in all situations. 75 + * 76 + * We could enforce this restriction by storing a flag alongside 77 + * each number indicating whether or not it's an original number; 78 + * the rules being that concatenation of two numbers is only valid 79 + * if they both have the original flag, and that its output _also_ 80 + * has the original flag (so that you can concatenate three 4s into 81 + * a 444), but that applying any other arithmetic operation clears 82 + * the original flag on the output. However, we can get marginally 83 + * simpler than that by observing that since concatenation has to 84 + * happen to a number before any other operation, we can simply 85 + * place all the concatenations at the start of the search. In 86 + * other words, we have a global flag on an entire number _set_ 87 + * which indicates whether we are still permitted to perform 88 + * concatenations; if so, we can concatenate any of the numbers in 89 + * that set. Performing any other operation clears the flag. 90 + */ 91 + 92 + #define SETFLAG_CONCAT 1 /* we can do concatenation */ 93 + 94 + struct sets; 95 + 96 + struct ancestor { 97 + struct set *prev; /* index of ancestor set in set list */ 98 + unsigned char pa, pb, po, pr; /* operation that got here from prev */ 99 + }; 100 + 101 + struct set { 102 + int *numbers; /* rationals stored as n,d pairs */ 103 + short nnumbers; /* # of rationals, so half # of ints */ 104 + short flags; /* SETFLAG_CONCAT only, at present */ 105 + int npaths; /* number of ways to reach this set */ 106 + struct ancestor a; /* primary ancestor */ 107 + struct ancestor *as; /* further ancestors, if we care */ 108 + int nas, assize; 109 + }; 110 + 111 + struct output { 112 + int number; 113 + struct set *set; 114 + int index; /* which number in the set is it? */ 115 + int npaths; /* number of ways to reach this */ 116 + }; 117 + 118 + #define SETLISTLEN 1024 119 + #define NUMBERLISTLEN 32768 120 + #define OUTPUTLISTLEN 1024 121 + struct operation; 122 + struct sets { 123 + struct set **setlists; 124 + int nsets, nsetlists, setlistsize; 125 + tree234 *settree; 126 + int **numberlists; 127 + int nnumbers, nnumberlists, numberlistsize; 128 + struct output **outputlists; 129 + int noutputs, noutputlists, outputlistsize; 130 + tree234 *outputtree; 131 + const struct operation *const *ops; 132 + }; 133 + 134 + #define OPFLAG_NEEDS_CONCAT 1 135 + #define OPFLAG_KEEPS_CONCAT 2 136 + #define OPFLAG_UNARY 4 137 + #define OPFLAG_UNARYPREFIX 8 138 + #define OPFLAG_FN 16 139 + 140 + struct operation { 141 + /* 142 + * Most operations should be shown in the output working, but 143 + * concatenation should not; we just take the result of the 144 + * concatenation and assume that it's obvious how it was 145 + * derived. 146 + */ 147 + int display; 148 + 149 + /* 150 + * Text display of the operator, in expressions and for 151 + * debugging respectively. 152 + */ 153 + const char *text, *dbgtext; 154 + 155 + /* 156 + * Flags dictating when the operator can be applied. 157 + */ 158 + int flags; 159 + 160 + /* 161 + * Priority of the operator (for avoiding unnecessary 162 + * parentheses when formatting it into a string). 163 + */ 164 + int priority; 165 + 166 + /* 167 + * Associativity of the operator. Bit 0 means we need parens 168 + * when the left operand of one of these operators is another 169 + * instance of it, e.g. (2^3)^4. Bit 1 means we need parens 170 + * when the right operand is another instance of the same 171 + * operator, e.g. 2-(3-4). Thus: 172 + * 173 + * - this field is 0 for a fully associative operator, since 174 + * we never need parens. 175 + * - it's 1 for a right-associative operator. 176 + * - it's 2 for a left-associative operator. 177 + * - it's 3 for a _non_-associative operator (which always 178 + * uses parens just to be sure). 179 + */ 180 + int assoc; 181 + 182 + /* 183 + * Whether the operator is commutative. Saves time in the 184 + * search if we don't have to try it both ways round. 185 + */ 186 + int commutes; 187 + 188 + /* 189 + * Function which implements the operator. Returns true on 190 + * success, false on failure. Takes two rationals and writes 191 + * out a third. 192 + */ 193 + int (*perform)(int *a, int *b, int *output); 194 + }; 195 + 196 + struct rules { 197 + const struct operation *const *ops; 198 + int use_all; 199 + }; 200 + 201 + #define MUL(r, a, b) do { \ 202 + (r) = (a) * (b); \ 203 + if ((b) && (a) && (r) / (b) != (a)) return false; \ 204 + } while (0) 205 + 206 + #define ADD(r, a, b) do { \ 207 + (r) = (a) + (b); \ 208 + if ((a) > 0 && (b) > 0 && (r) < 0) return false; \ 209 + if ((a) < 0 && (b) < 0 && (r) > 0) return false; \ 210 + } while (0) 211 + 212 + #define OUT(output, n, d) do { \ 213 + int g = gcd((n),(d)); \ 214 + if (g < 0) g = -g; \ 215 + if ((d) < 0) g = -g; \ 216 + if (g == -1 && (n) < -INT_MAX) return false; \ 217 + if (g == -1 && (d) < -INT_MAX) return false; \ 218 + (output)[0] = (n)/g; \ 219 + (output)[1] = (d)/g; \ 220 + assert((output)[1] > 0); \ 221 + } while (0) 222 + 223 + static int gcd(int x, int y) 224 + { 225 + while (x != 0 && y != 0) { 226 + int t = x; 227 + x = y; 228 + y = t % y; 229 + } 230 + 231 + return abs(x + y); /* i.e. whichever one isn't zero */ 232 + } 233 + 234 + static int perform_add(int *a, int *b, int *output) 235 + { 236 + int at, bt, tn, bn; 237 + /* 238 + * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1) 239 + */ 240 + MUL(at, a[0], b[1]); 241 + MUL(bt, b[0], a[1]); 242 + ADD(tn, at, bt); 243 + MUL(bn, a[1], b[1]); 244 + OUT(output, tn, bn); 245 + return true; 246 + } 247 + 248 + static int perform_sub(int *a, int *b, int *output) 249 + { 250 + int at, bt, tn, bn; 251 + /* 252 + * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1) 253 + */ 254 + MUL(at, a[0], b[1]); 255 + MUL(bt, b[0], a[1]); 256 + ADD(tn, at, -bt); 257 + MUL(bn, a[1], b[1]); 258 + OUT(output, tn, bn); 259 + return true; 260 + } 261 + 262 + static int perform_mul(int *a, int *b, int *output) 263 + { 264 + int tn, bn; 265 + /* 266 + * a0/a1 * b0/b1 = (a0*b0) / (a1*b1) 267 + */ 268 + MUL(tn, a[0], b[0]); 269 + MUL(bn, a[1], b[1]); 270 + OUT(output, tn, bn); 271 + return true; 272 + } 273 + 274 + static int perform_div(int *a, int *b, int *output) 275 + { 276 + int tn, bn; 277 + 278 + /* 279 + * Division by zero is outlawed. 280 + */ 281 + if (b[0] == 0) 282 + return false; 283 + 284 + /* 285 + * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) 286 + */ 287 + MUL(tn, a[0], b[1]); 288 + MUL(bn, a[1], b[0]); 289 + OUT(output, tn, bn); 290 + return true; 291 + } 292 + 293 + static int perform_exact_div(int *a, int *b, int *output) 294 + { 295 + int tn, bn; 296 + 297 + /* 298 + * Division by zero is outlawed. 299 + */ 300 + if (b[0] == 0) 301 + return false; 302 + 303 + /* 304 + * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) 305 + */ 306 + MUL(tn, a[0], b[1]); 307 + MUL(bn, a[1], b[0]); 308 + OUT(output, tn, bn); 309 + 310 + /* 311 + * Exact division means we require the result to be an integer. 312 + */ 313 + return (output[1] == 1); 314 + } 315 + 316 + static int max_p10(int n, int *p10_r) 317 + { 318 + /* 319 + * Find the smallest power of ten strictly greater than n. 320 + * 321 + * Special case: we must return at least 10, even if n is 322 + * zero. (This is because this function is used for finding 323 + * the power of ten by which to multiply a number being 324 + * concatenated to the front of n, and concatenating 1 to 0 325 + * should yield 10 and not 1.) 326 + */ 327 + int p10 = 10; 328 + while (p10 <= (INT_MAX/10) && p10 <= n) 329 + p10 *= 10; 330 + if (p10 > INT_MAX/10) 331 + return false; /* integer overflow */ 332 + *p10_r = p10; 333 + return true; 334 + } 335 + 336 + static int perform_concat(int *a, int *b, int *output) 337 + { 338 + int t1, t2, p10; 339 + 340 + /* 341 + * We can't concatenate anything which isn't a non-negative 342 + * integer. 343 + */ 344 + if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0) 345 + return false; 346 + 347 + /* 348 + * For concatenation, we can safely assume leading zeroes 349 + * aren't an issue. It isn't clear whether they `should' be 350 + * allowed, but it turns out not to matter: concatenating a 351 + * leading zero on to a number in order to harmlessly get rid 352 + * of the zero is never necessary because unwanted zeroes can 353 + * be disposed of by adding them to something instead. So we 354 + * disallow them always. 355 + * 356 + * The only other possibility is that you might want to 357 + * concatenate a leading zero on to something and then 358 + * concatenate another non-zero digit on to _that_ (to make, 359 + * for example, 106); but that's also unnecessary, because you 360 + * can make 106 just as easily by concatenating the 0 on to the 361 + * _end_ of the 1 first. 362 + */ 363 + if (a[0] == 0) 364 + return false; 365 + 366 + if (!max_p10(b[0], &p10)) return false; 367 + 368 + MUL(t1, p10, a[0]); 369 + ADD(t2, t1, b[0]); 370 + OUT(output, t2, 1); 371 + return true; 372 + } 373 + 374 + #define IPOW(ret, x, y) do { \ 375 + int ipow_limit = (y); \ 376 + if ((x) == 1 || (x) == 0) ipow_limit = 1; \ 377 + else if ((x) == -1) ipow_limit &= 1; \ 378 + (ret) = 1; \ 379 + while (ipow_limit-- > 0) { \ 380 + int tmp; \ 381 + MUL(tmp, ret, x); \ 382 + ret = tmp; \ 383 + } \ 384 + } while (0) 385 + 386 + static int perform_exp(int *a, int *b, int *output) 387 + { 388 + int an, ad, xn, xd; 389 + 390 + /* 391 + * Exponentiation is permitted if the result is rational. This 392 + * means that: 393 + * 394 + * - first we see whether we can take the (denominator-of-b)th 395 + * root of a and get a rational; if not, we give up. 396 + * 397 + * - then we do take that root of a 398 + * 399 + * - then we multiply by itself (numerator-of-b) times. 400 + */ 401 + if (b[1] > 1) { 402 + an = (int)(0.5 + pow(a[0], 1.0/b[1])); 403 + ad = (int)(0.5 + pow(a[1], 1.0/b[1])); 404 + IPOW(xn, an, b[1]); 405 + IPOW(xd, ad, b[1]); 406 + if (xn != a[0] || xd != a[1]) 407 + return false; 408 + } else { 409 + an = a[0]; 410 + ad = a[1]; 411 + } 412 + if (b[0] >= 0) { 413 + IPOW(xn, an, b[0]); 414 + IPOW(xd, ad, b[0]); 415 + } else { 416 + IPOW(xd, an, -b[0]); 417 + IPOW(xn, ad, -b[0]); 418 + } 419 + if (xd == 0) 420 + return false; 421 + 422 + OUT(output, xn, xd); 423 + return true; 424 + } 425 + 426 + static int perform_factorial(int *a, int *b, int *output) 427 + { 428 + int ret, t, i; 429 + 430 + /* 431 + * Factorials of non-negative integers are permitted. 432 + */ 433 + if (a[1] != 1 || a[0] < 0) 434 + return false; 435 + 436 + /* 437 + * However, a special case: we don't take a factorial of 438 + * anything which would thereby remain the same. 439 + */ 440 + if (a[0] == 1 || a[0] == 2) 441 + return false; 442 + 443 + ret = 1; 444 + for (i = 1; i <= a[0]; i++) { 445 + MUL(t, ret, i); 446 + ret = t; 447 + } 448 + 449 + OUT(output, ret, 1); 450 + return true; 451 + } 452 + 453 + static int perform_decimal(int *a, int *b, int *output) 454 + { 455 + int p10; 456 + 457 + /* 458 + * Add a decimal digit to the front of a number; 459 + * fail if it's not an integer. 460 + * So, 1 --> 0.1, 15 --> 0.15, 461 + * or, rather, 1 --> 1/10, 15 --> 15/100, 462 + * x --> x / (smallest power of 10 > than x) 463 + * 464 + */ 465 + if (a[1] != 1) return false; 466 + 467 + if (!max_p10(a[0], &p10)) return false; 468 + 469 + OUT(output, a[0], p10); 470 + return true; 471 + } 472 + 473 + static int perform_recur(int *a, int *b, int *output) 474 + { 475 + int p10, tn, bn; 476 + 477 + /* 478 + * This converts a number like .4 to .44444..., or .45 to .45454... 479 + * The input number must be -1 < a < 1. 480 + * 481 + * Calculate the smallest power of 10 that divides the denominator exactly, 482 + * returning if no such power of 10 exists. Then multiply the numerator 483 + * up accordingly, and the new denominator becomes that power of 10 - 1. 484 + */ 485 + if (abs(a[0]) >= abs(a[1])) return false; /* -1 < a < 1 */ 486 + 487 + p10 = 10; 488 + while (p10 <= (INT_MAX/10)) { 489 + if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found; 490 + p10 *= 10; 491 + } 492 + return false; 493 + found: 494 + tn = a[0] * (p10 / a[1]); 495 + bn = p10 - 1; 496 + 497 + OUT(output, tn, bn); 498 + return true; 499 + } 500 + 501 + static int perform_root(int *a, int *b, int *output) 502 + { 503 + /* 504 + * A root B is: 1 iff a == 0 505 + * B ^ (1/A) otherwise 506 + */ 507 + int ainv[2], res; 508 + 509 + if (a[0] == 0) { 510 + OUT(output, 1, 1); 511 + return true; 512 + } 513 + 514 + OUT(ainv, a[1], a[0]); 515 + res = perform_exp(b, ainv, output); 516 + return res; 517 + } 518 + 519 + static int perform_perc(int *a, int *b, int *output) 520 + { 521 + if (a[0] == 0) return false; /* 0% = 0, uninteresting. */ 522 + if (a[1] > (INT_MAX/100)) return false; 523 + 524 + OUT(output, a[0], a[1]*100); 525 + return true; 526 + } 527 + 528 + static int perform_gamma(int *a, int *b, int *output) 529 + { 530 + int asub1[2]; 531 + 532 + /* 533 + * gamma(a) = (a-1)! 534 + * 535 + * special case not caught by perform_fact: gamma(1) is 1 so 536 + * don't bother. 537 + */ 538 + if (a[0] == 1 && a[1] == 1) return false; 539 + 540 + OUT(asub1, a[0]-a[1], a[1]); 541 + return perform_factorial(asub1, b, output); 542 + } 543 + 544 + static int perform_sqrt(int *a, int *b, int *output) 545 + { 546 + int half[2] = { 1, 2 }; 547 + 548 + /* 549 + * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops. 550 + */ 551 + if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return false; 552 + 553 + return perform_exp(a, half, output); 554 + } 555 + 556 + static const struct operation op_add = { 557 + true, "+", "+", 0, 10, 0, true, perform_add 558 + }; 559 + static const struct operation op_sub = { 560 + true, "-", "-", 0, 10, 2, false, perform_sub 561 + }; 562 + static const struct operation op_mul = { 563 + true, "*", "*", 0, 20, 0, true, perform_mul 564 + }; 565 + static const struct operation op_div = { 566 + true, "/", "/", 0, 20, 2, false, perform_div 567 + }; 568 + static const struct operation op_xdiv = { 569 + true, "/", "/", 0, 20, 2, false, perform_exact_div 570 + }; 571 + static const struct operation op_concat = { 572 + false, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 573 + 1000, 0, false, perform_concat 574 + }; 575 + static const struct operation op_exp = { 576 + true, "^", "^", 0, 30, 1, false, perform_exp 577 + }; 578 + static const struct operation op_factorial = { 579 + true, "!", "!", OPFLAG_UNARY, 40, 0, false, perform_factorial 580 + }; 581 + static const struct operation op_decimal = { 582 + true, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, false, perform_decimal 583 + }; 584 + static const struct operation op_recur = { 585 + true, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, false, perform_recur 586 + }; 587 + static const struct operation op_root = { 588 + true, "v~", "root", 0, 30, 1, false, perform_root 589 + }; 590 + static const struct operation op_perc = { 591 + true, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, false, perform_perc 592 + }; 593 + static const struct operation op_gamma = { 594 + true, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, false, perform_gamma 595 + }; 596 + static const struct operation op_sqrt = { 597 + true, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, false, perform_sqrt 598 + }; 599 + 600 + /* 601 + * In Countdown, divisions resulting in fractions are disallowed. 602 + * http://www.askoxford.com/wordgames/countdown/rules/ 603 + */ 604 + static const struct operation *const ops_countdown[] = { 605 + &op_add, &op_mul, &op_sub, &op_xdiv, NULL 606 + }; 607 + static const struct rules rules_countdown = { 608 + ops_countdown, false 609 + }; 610 + 611 + /* 612 + * A slightly different rule set which handles the reasonably well 613 + * known puzzle of making 24 using two 3s and two 8s. For this we 614 + * need rational rather than integer division. 615 + */ 616 + static const struct operation *const ops_3388[] = { 617 + &op_add, &op_mul, &op_sub, &op_div, NULL 618 + }; 619 + static const struct rules rules_3388 = { 620 + ops_3388, true 621 + }; 622 + 623 + /* 624 + * A still more permissive rule set usable for the four-4s problem 625 + * and similar things. Permits concatenation. 626 + */ 627 + static const struct operation *const ops_four4s[] = { 628 + &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL 629 + }; 630 + static const struct rules rules_four4s = { 631 + ops_four4s, true 632 + }; 633 + 634 + /* 635 + * The most permissive ruleset I can think of. Permits 636 + * exponentiation, and also silly unary operators like factorials. 637 + */ 638 + static const struct operation *const ops_anythinggoes[] = { 639 + &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial, 640 + &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL 641 + }; 642 + static const struct rules rules_anythinggoes = { 643 + ops_anythinggoes, true 644 + }; 645 + 646 + #define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \ 647 + (long long)(b)[0] * (a)[1] ) 648 + 649 + static int addtoset(struct set *set, int newnumber[2]) 650 + { 651 + int i, j; 652 + 653 + /* Find where we want to insert the new number */ 654 + for (i = 0; i < set->nnumbers && 655 + ratcmp(set->numbers+2*i, <, newnumber); i++); 656 + 657 + /* Move everything else up */ 658 + for (j = set->nnumbers; j > i; j--) { 659 + set->numbers[2*j] = set->numbers[2*j-2]; 660 + set->numbers[2*j+1] = set->numbers[2*j-1]; 661 + } 662 + 663 + /* Insert the new number */ 664 + set->numbers[2*i] = newnumber[0]; 665 + set->numbers[2*i+1] = newnumber[1]; 666 + 667 + set->nnumbers++; 668 + 669 + return i; 670 + } 671 + 672 + #define ensure(array, size, newlen, type) do { \ 673 + if ((newlen) > (size)) { \ 674 + (size) = (newlen) + 512; \ 675 + (array) = sresize((array), (size), type); \ 676 + } \ 677 + } while (0) 678 + 679 + static int setcmp(void *av, void *bv) 680 + { 681 + struct set *a = (struct set *)av; 682 + struct set *b = (struct set *)bv; 683 + int i; 684 + 685 + if (a->nnumbers < b->nnumbers) 686 + return -1; 687 + else if (a->nnumbers > b->nnumbers) 688 + return +1; 689 + 690 + if (a->flags < b->flags) 691 + return -1; 692 + else if (a->flags > b->flags) 693 + return +1; 694 + 695 + for (i = 0; i < a->nnumbers; i++) { 696 + if (ratcmp(a->numbers+2*i, <, b->numbers+2*i)) 697 + return -1; 698 + else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i)) 699 + return +1; 700 + } 701 + 702 + return 0; 703 + } 704 + 705 + static int outputcmp(void *av, void *bv) 706 + { 707 + struct output *a = (struct output *)av; 708 + struct output *b = (struct output *)bv; 709 + 710 + if (a->number < b->number) 711 + return -1; 712 + else if (a->number > b->number) 713 + return +1; 714 + 715 + return 0; 716 + } 717 + 718 + static int outputfindcmp(void *av, void *bv) 719 + { 720 + int *a = (int *)av; 721 + struct output *b = (struct output *)bv; 722 + 723 + if (*a < b->number) 724 + return -1; 725 + else if (*a > b->number) 726 + return +1; 727 + 728 + return 0; 729 + } 730 + 731 + static void addset(struct sets *s, struct set *set, int multiple, 732 + struct set *prev, int pa, int po, int pb, int pr) 733 + { 734 + struct set *s2; 735 + int npaths = (prev ? prev->npaths : 1); 736 + 737 + assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN); 738 + s2 = add234(s->settree, set); 739 + if (s2 == set) { 740 + /* 741 + * New set added to the tree. 742 + */ 743 + set->a.prev = prev; 744 + set->a.pa = pa; 745 + set->a.po = po; 746 + set->a.pb = pb; 747 + set->a.pr = pr; 748 + set->npaths = npaths; 749 + s->nsets++; 750 + s->nnumbers += 2 * set->nnumbers; 751 + set->as = NULL; 752 + set->nas = set->assize = 0; 753 + } else { 754 + /* 755 + * Rediscovered an existing set. Update its npaths. 756 + */ 757 + s2->npaths += npaths; 758 + /* 759 + * And optionally enter it as an additional ancestor. 760 + */ 761 + if (multiple) { 762 + if (s2->nas >= s2->assize) { 763 + s2->assize = s2->nas * 3 / 2 + 4; 764 + s2->as = sresize(s2->as, s2->assize, struct ancestor); 765 + } 766 + s2->as[s2->nas].prev = prev; 767 + s2->as[s2->nas].pa = pa; 768 + s2->as[s2->nas].po = po; 769 + s2->as[s2->nas].pb = pb; 770 + s2->as[s2->nas].pr = pr; 771 + s2->nas++; 772 + } 773 + } 774 + } 775 + 776 + static struct set *newset(struct sets *s, int nnumbers, int flags) 777 + { 778 + struct set *sn; 779 + 780 + ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *); 781 + while (s->nsetlists <= s->nsets / SETLISTLEN) 782 + s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set); 783 + sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN; 784 + 785 + if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN) 786 + s->nnumbers = s->nnumberlists * NUMBERLISTLEN; 787 + ensure(s->numberlists, s->numberlistsize, 788 + s->nnumbers/NUMBERLISTLEN+1, int *); 789 + while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN) 790 + s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int); 791 + sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] + 792 + s->nnumbers % NUMBERLISTLEN; 793 + 794 + /* 795 + * Start the set off empty. 796 + */ 797 + sn->nnumbers = 0; 798 + 799 + sn->flags = flags; 800 + 801 + return sn; 802 + } 803 + 804 + static int addoutput(struct sets *s, struct set *ss, int index, int *n) 805 + { 806 + struct output *o, *o2; 807 + 808 + /* 809 + * Target numbers are always integers. 810 + */ 811 + if (ss->numbers[2*index+1] != 1) 812 + return false; 813 + 814 + ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1, 815 + struct output *); 816 + while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN) 817 + s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN, 818 + struct output); 819 + o = s->outputlists[s->noutputs / OUTPUTLISTLEN] + 820 + s->noutputs % OUTPUTLISTLEN; 821 + 822 + o->number = ss->numbers[2*index]; 823 + o->set = ss; 824 + o->index = index; 825 + o->npaths = ss->npaths; 826 + o2 = add234(s->outputtree, o); 827 + if (o2 != o) { 828 + o2->npaths += o->npaths; 829 + } else { 830 + s->noutputs++; 831 + } 832 + *n = o->number; 833 + return true; 834 + } 835 + 836 + static struct sets *do_search(int ninputs, int *inputs, 837 + const struct rules *rules, int *target, 838 + int debug, int multiple) 839 + { 840 + struct sets *s; 841 + struct set *sn; 842 + int qpos, i; 843 + const struct operation *const *ops = rules->ops; 844 + 845 + s = snew(struct sets); 846 + s->setlists = NULL; 847 + s->nsets = s->nsetlists = s->setlistsize = 0; 848 + s->numberlists = NULL; 849 + s->nnumbers = s->nnumberlists = s->numberlistsize = 0; 850 + s->outputlists = NULL; 851 + s->noutputs = s->noutputlists = s->outputlistsize = 0; 852 + s->settree = newtree234(setcmp); 853 + s->outputtree = newtree234(outputcmp); 854 + s->ops = ops; 855 + 856 + /* 857 + * Start with the input set. 858 + */ 859 + sn = newset(s, ninputs, SETFLAG_CONCAT); 860 + for (i = 0; i < ninputs; i++) { 861 + int newnumber[2]; 862 + newnumber[0] = inputs[i]; 863 + newnumber[1] = 1; 864 + addtoset(sn, newnumber); 865 + } 866 + addset(s, sn, multiple, NULL, 0, 0, 0, 0); 867 + 868 + /* 869 + * Now perform the breadth-first search: keep looping over sets 870 + * until we run out of steam. 871 + */ 872 + qpos = 0; 873 + while (qpos < s->nsets) { 874 + struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN; 875 + struct set *sn; 876 + int i, j, k, m; 877 + 878 + if (debug) { 879 + int i; 880 + printf("processing set:"); 881 + for (i = 0; i < ss->nnumbers; i++) { 882 + printf(" %d", ss->numbers[2*i]); 883 + if (ss->numbers[2*i+1] != 1) 884 + printf("/%d", ss->numbers[2*i+1]); 885 + } 886 + printf("\n"); 887 + } 888 + 889 + /* 890 + * Record all the valid output numbers in this state. We 891 + * can always do this if there's only one number in the 892 + * state; otherwise, we can only do it if we aren't 893 + * required to use all the numbers in coming to our answer. 894 + */ 895 + if (ss->nnumbers == 1 || !rules->use_all) { 896 + for (i = 0; i < ss->nnumbers; i++) { 897 + int n; 898 + 899 + if (addoutput(s, ss, i, &n) && target && n == *target) 900 + return s; 901 + } 902 + } 903 + 904 + /* 905 + * Try every possible operation from this state. 906 + */ 907 + for (k = 0; ops[k] && ops[k]->perform; k++) { 908 + if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) && 909 + !(ss->flags & SETFLAG_CONCAT)) 910 + continue; /* can't use this operation here */ 911 + for (i = 0; i < ss->nnumbers; i++) { 912 + int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers); 913 + for (j = 0; j < jlimit; j++) { 914 + int n[2], newnn = ss->nnumbers; 915 + int pa, po, pb, pr; 916 + 917 + if (!(ops[k]->flags & OPFLAG_UNARY)) { 918 + if (i == j) 919 + continue; /* can't combine a number with itself */ 920 + if (i > j && ops[k]->commutes) 921 + continue; /* no need to do this both ways round */ 922 + newnn--; 923 + } 924 + if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n)) 925 + continue; /* operation failed */ 926 + 927 + sn = newset(s, newnn, ss->flags); 928 + 929 + if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT)) 930 + sn->flags &= ~SETFLAG_CONCAT; 931 + 932 + for (m = 0; m < ss->nnumbers; m++) { 933 + if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) && 934 + m == j)) 935 + continue; 936 + sn->numbers[2*sn->nnumbers] = ss->numbers[2*m]; 937 + sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1]; 938 + sn->nnumbers++; 939 + } 940 + pa = i; 941 + if (ops[k]->flags & OPFLAG_UNARY) 942 + pb = sn->nnumbers+10; 943 + else 944 + pb = j; 945 + po = k; 946 + pr = addtoset(sn, n); 947 + addset(s, sn, multiple, ss, pa, po, pb, pr); 948 + if (debug) { 949 + int i; 950 + if (ops[k]->flags & OPFLAG_UNARYPREFIX) 951 + printf(" %s %d ->", ops[po]->dbgtext, pa); 952 + else if (ops[k]->flags & OPFLAG_UNARY) 953 + printf(" %d %s ->", pa, ops[po]->dbgtext); 954 + else 955 + printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb); 956 + for (i = 0; i < sn->nnumbers; i++) { 957 + printf(" %d", sn->numbers[2*i]); 958 + if (sn->numbers[2*i+1] != 1) 959 + printf("/%d", sn->numbers[2*i+1]); 960 + } 961 + printf("\n"); 962 + } 963 + } 964 + } 965 + } 966 + 967 + qpos++; 968 + } 969 + 970 + return s; 971 + } 972 + 973 + static void free_sets(struct sets *s) 974 + { 975 + int i; 976 + 977 + freetree234(s->settree); 978 + freetree234(s->outputtree); 979 + for (i = 0; i < s->nsetlists; i++) 980 + sfree(s->setlists[i]); 981 + sfree(s->setlists); 982 + for (i = 0; i < s->nnumberlists; i++) 983 + sfree(s->numberlists[i]); 984 + sfree(s->numberlists); 985 + for (i = 0; i < s->noutputlists; i++) 986 + sfree(s->outputlists[i]); 987 + sfree(s->outputlists); 988 + sfree(s); 989 + } 990 + 991 + /* 992 + * Print a text formula for producing a given output. 993 + */ 994 + static void print_recurse(struct sets *s, struct set *ss, int pathindex, 995 + int index, int priority, int assoc, int child); 996 + static void print_recurse_inner(struct sets *s, struct set *ss, 997 + struct ancestor *a, int pathindex, int index, 998 + int priority, int assoc, int child) 999 + { 1000 + if (a->prev && index != a->pr) { 1001 + int pi; 1002 + 1003 + /* 1004 + * This number was passed straight down from this set's 1005 + * predecessor. Find its index in the previous set and 1006 + * recurse to there. 1007 + */ 1008 + pi = index; 1009 + assert(pi != a->pr); 1010 + if (pi > a->pr) 1011 + pi--; 1012 + if (pi >= min(a->pa, a->pb)) { 1013 + pi++; 1014 + if (pi >= max(a->pa, a->pb)) 1015 + pi++; 1016 + } 1017 + print_recurse(s, a->prev, pathindex, pi, priority, assoc, child); 1018 + } else if (a->prev && index == a->pr && 1019 + s->ops[a->po]->display) { 1020 + /* 1021 + * This number was created by a displayed operator in the 1022 + * transition from this set to its predecessor. Hence we 1023 + * write an open paren, then recurse into the first 1024 + * operand, then write the operator, then the second 1025 + * operand, and finally close the paren. 1026 + */ 1027 + const char *op; 1028 + int parens, thispri, thisassoc; 1029 + 1030 + /* 1031 + * Determine whether we need parentheses. 1032 + */ 1033 + thispri = s->ops[a->po]->priority; 1034 + thisassoc = s->ops[a->po]->assoc; 1035 + parens = (thispri < priority || 1036 + (thispri == priority && (assoc & child))); 1037 + 1038 + if (parens) 1039 + putchar('('); 1040 + 1041 + if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX) 1042 + for (op = s->ops[a->po]->text; *op; op++) 1043 + putchar(*op); 1044 + 1045 + if (s->ops[a->po]->flags & OPFLAG_FN) 1046 + putchar('('); 1047 + 1048 + print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1); 1049 + 1050 + if (s->ops[a->po]->flags & OPFLAG_FN) 1051 + putchar(')'); 1052 + 1053 + if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)) 1054 + for (op = s->ops[a->po]->text; *op; op++) 1055 + putchar(*op); 1056 + 1057 + if (!(s->ops[a->po]->flags & OPFLAG_UNARY)) 1058 + print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2); 1059 + 1060 + if (parens) 1061 + putchar(')'); 1062 + } else { 1063 + /* 1064 + * This number is either an original, or something formed 1065 + * by a non-displayed operator (concatenation). Either way, 1066 + * we display it as is. 1067 + */ 1068 + printf("%d", ss->numbers[2*index]); 1069 + if (ss->numbers[2*index+1] != 1) 1070 + printf("/%d", ss->numbers[2*index+1]); 1071 + } 1072 + } 1073 + static void print_recurse(struct sets *s, struct set *ss, int pathindex, 1074 + int index, int priority, int assoc, int child) 1075 + { 1076 + if (!ss->a.prev || pathindex < ss->a.prev->npaths) { 1077 + print_recurse_inner(s, ss, &ss->a, pathindex, 1078 + index, priority, assoc, child); 1079 + } else { 1080 + int i; 1081 + pathindex -= ss->a.prev->npaths; 1082 + for (i = 0; i < ss->nas; i++) { 1083 + if (pathindex < ss->as[i].prev->npaths) { 1084 + print_recurse_inner(s, ss, &ss->as[i], pathindex, 1085 + index, priority, assoc, child); 1086 + break; 1087 + } 1088 + pathindex -= ss->as[i].prev->npaths; 1089 + } 1090 + } 1091 + } 1092 + static void print(int pathindex, struct sets *s, struct output *o) 1093 + { 1094 + print_recurse(s, o->set, pathindex, o->index, 0, 0, 0); 1095 + } 1096 + 1097 + /* 1098 + * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm 1099 + */ 1100 + int main(int argc, char **argv) 1101 + { 1102 + int doing_opts = true; 1103 + const struct rules *rules = NULL; 1104 + char *pname = argv[0]; 1105 + int got_target = false, target = 0; 1106 + int numbers[10], nnumbers = 0; 1107 + int verbose = false; 1108 + int pathcounts = false; 1109 + int multiple = false; 1110 + int debug_bfs = false; 1111 + int got_range = false, rangemin = 0, rangemax = 0; 1112 + 1113 + struct output *o; 1114 + struct sets *s; 1115 + int i, start, limit; 1116 + 1117 + while (--argc) { 1118 + char *p = *++argv; 1119 + int c; 1120 + 1121 + if (doing_opts && *p == '-') { 1122 + p++; 1123 + 1124 + if (!strcmp(p, "-")) { 1125 + doing_opts = false; 1126 + continue; 1127 + } else if (*p == '-') { 1128 + p++; 1129 + if (!strcmp(p, "debug-bfs")) { 1130 + debug_bfs = true; 1131 + } else { 1132 + fprintf(stderr, "%s: option '--%s' not recognised\n", 1133 + pname, p); 1134 + } 1135 + } else while (p && *p) switch (c = *p++) { 1136 + case 'C': 1137 + rules = &rules_countdown; 1138 + break; 1139 + case 'B': 1140 + rules = &rules_3388; 1141 + break; 1142 + case 'D': 1143 + rules = &rules_four4s; 1144 + break; 1145 + case 'A': 1146 + rules = &rules_anythinggoes; 1147 + break; 1148 + case 'v': 1149 + verbose = true; 1150 + break; 1151 + case 'p': 1152 + pathcounts = true; 1153 + break; 1154 + case 'm': 1155 + multiple = true; 1156 + break; 1157 + case 't': 1158 + case 'r': 1159 + { 1160 + char *v; 1161 + if (*p) { 1162 + v = p; 1163 + p = NULL; 1164 + } else if (--argc) { 1165 + v = *++argv; 1166 + } else { 1167 + fprintf(stderr, "%s: option '-%c' expects an" 1168 + " argument\n", pname, c); 1169 + return 1; 1170 + } 1171 + switch (c) { 1172 + case 't': 1173 + got_target = true; 1174 + target = atoi(v); 1175 + break; 1176 + case 'r': 1177 + { 1178 + char *sep = strchr(v, '-'); 1179 + got_range = true; 1180 + if (sep) { 1181 + rangemin = atoi(v); 1182 + rangemax = atoi(sep+1); 1183 + } else { 1184 + rangemin = 0; 1185 + rangemax = atoi(v); 1186 + } 1187 + } 1188 + break; 1189 + } 1190 + } 1191 + break; 1192 + default: 1193 + fprintf(stderr, "%s: option '-%c' not" 1194 + " recognised\n", pname, c); 1195 + return 1; 1196 + } 1197 + } else { 1198 + if (nnumbers >= lenof(numbers)) { 1199 + fprintf(stderr, "%s: internal limit of %d numbers exceeded\n", 1200 + pname, (int)lenof(numbers)); 1201 + return 1; 1202 + } else { 1203 + numbers[nnumbers++] = atoi(p); 1204 + } 1205 + } 1206 + } 1207 + 1208 + if (!rules) { 1209 + fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname); 1210 + return 1; 1211 + } 1212 + 1213 + if (!nnumbers) { 1214 + fprintf(stderr, "%s: no input numbers specified\n", pname); 1215 + return 1; 1216 + } 1217 + 1218 + if (got_range) { 1219 + if (got_target) { 1220 + fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname); 1221 + return 1; 1222 + } 1223 + if (rangemin >= rangemax) { 1224 + fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax); 1225 + return 1; 1226 + } 1227 + } 1228 + 1229 + s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL), 1230 + debug_bfs, multiple); 1231 + 1232 + if (got_target) { 1233 + o = findrelpos234(s->outputtree, &target, outputfindcmp, 1234 + REL234_LE, &start); 1235 + if (!o) 1236 + start = -1; 1237 + o = findrelpos234(s->outputtree, &target, outputfindcmp, 1238 + REL234_GE, &limit); 1239 + if (!o) 1240 + limit = -1; 1241 + assert(start != -1 || limit != -1); 1242 + if (start == -1) 1243 + start = limit; 1244 + else if (limit == -1) 1245 + limit = start; 1246 + limit++; 1247 + } else if (got_range) { 1248 + if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp, 1249 + REL234_GE, &start) || 1250 + !findrelpos234(s->outputtree, &rangemax, outputfindcmp, 1251 + REL234_LE, &limit)) { 1252 + printf("No solutions available in specified range %d-%d\n", rangemin, rangemax); 1253 + return 1; 1254 + } 1255 + limit++; 1256 + } else { 1257 + start = 0; 1258 + limit = count234(s->outputtree); 1259 + } 1260 + 1261 + for (i = start; i < limit; i++) { 1262 + char buf[256]; 1263 + 1264 + o = index234(s->outputtree, i); 1265 + 1266 + sprintf(buf, "%d", o->number); 1267 + 1268 + if (pathcounts) 1269 + sprintf(buf + strlen(buf), " [%d]", o->npaths); 1270 + 1271 + if (got_target || verbose) { 1272 + int j, npaths; 1273 + 1274 + if (multiple) 1275 + npaths = o->npaths; 1276 + else 1277 + npaths = 1; 1278 + 1279 + for (j = 0; j < npaths; j++) { 1280 + printf("%s = ", buf); 1281 + print(j, s, o); 1282 + putchar('\n'); 1283 + } 1284 + } else { 1285 + printf("%s\n", buf); 1286 + } 1287 + } 1288 + 1289 + free_sets(s); 1290 + 1291 + return 0; 1292 + } 1293 + 1294 + /* vim: set shiftwidth=4 tabstop=8: */
+866
apps/plugins/puzzles/src/unfinished/path.c
··· 1 + /* 2 + * Experimental grid generator for Nikoli's `Number Link' puzzle. 3 + */ 4 + 5 + #include <stdio.h> 6 + #include <stdlib.h> 7 + #include <string.h> 8 + #include <assert.h> 9 + #include "puzzles.h" 10 + 11 + /* 12 + * 2005-07-08: This is currently a Path grid generator which will 13 + * construct valid grids at a plausible speed. However, the grids 14 + * are not of suitable quality to be used directly as puzzles. 15 + * 16 + * The basic strategy is to start with an empty grid, and 17 + * repeatedly either (a) add a new path to it, or (b) extend one 18 + * end of a path by one square in some direction and push other 19 + * paths into new shapes in the process. The effect of this is that 20 + * we are able to construct a set of paths which between them fill 21 + * the entire grid. 22 + * 23 + * Quality issues: if we set the main loop to do (a) where possible 24 + * and (b) only where necessary, we end up with a grid containing a 25 + * few too many small paths, which therefore doesn't make for an 26 + * interesting puzzle. If we reverse the priority so that we do (b) 27 + * where possible and (a) only where necessary, we end up with some 28 + * staggeringly interwoven grids with very very few separate paths, 29 + * but the result of this is that there's invariably a solution 30 + * other than the intended one which leaves many grid squares 31 + * unfilled. There's also a separate problem which is that many 32 + * grids have really boring and obvious paths in them, such as the 33 + * entire bottom row of the grid being taken up by a single path. 34 + * 35 + * It's not impossible that a few tweaks might eliminate or reduce 36 + * the incidence of boring paths, and might also find a happy 37 + * medium between too many and too few. There remains the question 38 + * of unique solutions, however. I fear there is no alternative but 39 + * to write - somehow! - a solver. 40 + * 41 + * While I'm here, some notes on UI strategy for the parts of the 42 + * puzzle implementation that _aren't_ the generator: 43 + * 44 + * - data model is to track connections between adjacent squares, 45 + * so that you aren't limited to extending a path out from each 46 + * number but can also mark sections of path which you know 47 + * _will_ come in handy later. 48 + * 49 + * - user interface is to click in one square and drag to an 50 + * adjacent one, thus creating a link between them. We can 51 + * probably tolerate rapid mouse motion causing a drag directly 52 + * to a square which is a rook move away, but any other rapid 53 + * motion is ambiguous and probably the best option is to wait 54 + * until the mouse returns to a square we know how to reach. 55 + * 56 + * - a drag causing the current path to backtrack has the effect 57 + * of removing bits of it. 58 + * 59 + * - the UI should enforce at all times the constraint that at 60 + * most two links can come into any square. 61 + * 62 + * - my Cunning Plan for actually implementing this: the game_ui 63 + * contains a grid-sized array, which is copied from the current 64 + * game_state on starting a drag. While a drag is active, the 65 + * contents of the game_ui is adjusted with every mouse motion, 66 + * and is displayed _in place_ of the game_state itself. On 67 + * termination of a drag, the game_ui array is copied back into 68 + * the new game_state (or rather, a string move is encoded which 69 + * has precisely the set of link changes to cause that effect). 70 + */ 71 + 72 + /* 73 + * 2020-05-11: some thoughts on a solver. 74 + * 75 + * Consider this example puzzle, from Wikipedia: 76 + * 77 + * ---4--- 78 + * -3--25- 79 + * ---31-- 80 + * ---5--- 81 + * ------- 82 + * --1---- 83 + * 2---4-- 84 + * 85 + * The kind of deduction that a human wants to make here is: which way 86 + * does the path between the 4s go? In particular, does it go round 87 + * the left of the W-shaped cluster of endpoints, or round the right 88 + * of it? It's clear at a glance that it must go to the right, because 89 + * _any_ path between the 4s that goes to the left of that cluster, no 90 + * matter what detailed direction it takes, will disconnect the 91 + * remaining grid squares into two components, with the two 2s not in 92 + * the same component. So we immediately know that the path between 93 + * the 4s _must_ go round the right-hand side of the grid. 94 + * 95 + * How do you model that global and topological reasoning in a 96 + * computer? 97 + * 98 + * The most plausible idea I've seen so far is to use fundamental 99 + * groups. The fundamental group of loops based at a given point in a 100 + * space is a free group, under loop concatenation and up to homotopy, 101 + * generated by the loops that go in each direction around each hole 102 + * in the space. In this case, the 'holes' are clues, or connected 103 + * groups of clues. 104 + * 105 + * So you might be able to enumerate all the homotopy classes of paths 106 + * between (say) the two 4s as follows. Start with any old path 107 + * between them (say, find the first one that breadth-first search 108 + * will give you). Choose one of the 4s to regard as the base point 109 + * (arbitrarily). Then breadth-first search among the space of _paths_ 110 + * by the following procedure. Given a candidate path, append to it 111 + * each of the possible loops that starts from the base point, 112 + * circumnavigates one clue cluster, and returns to the base point. 113 + * The result will typically be a path that retraces its steps and 114 + * self-intersects. Now adjust it homotopically so that it doesn't. If 115 + * that can't be done, then we haven't generated a fresh candidate 116 + * path; if it can, then we've got a new path that is not homotopic to 117 + * any path we already had, so add it to our list and queue it up to 118 + * become the starting point of this search later. 119 + * 120 + * The idea is that this should exhaustively enumerate, up to 121 + * homotopy, the different ways in which the two 4s can connect to 122 + * each other within the constraint that you have to actually fit the 123 + * path non-self-intersectingly into this grid. Then you can keep a 124 + * list of those homotopy classes in mind, and start ruling them out 125 + * by techniques like the connectivity approach described above. 126 + * Hopefully you end up narrowing down to few enough homotopy classes 127 + * that you can deduce something concrete about actual squares of the 128 + * grid - for example, here, that if the path between 4s has to go 129 + * round the right, then we know some specific squares it must go 130 + * through, so we can fill those in. And then, having filled in a 131 + * piece of the middle of a path, you can now regard connecting the 132 + * ultimate endpoints to that mid-section as two separate subproblems, 133 + * so you've reduced to a simpler instance of the same puzzle. 134 + * 135 + * But I don't know whether all of this actually works. I more or less 136 + * believe the process for enumerating elements of the free group; but 137 + * I'm not as confident that when you find a group element that won't 138 + * fit in the grid, you'll never have to consider its descendants in 139 + * the BFS either. And I'm assuming that 'unwind the self-intersection 140 + * homotopically' is a thing that can actually be turned into a 141 + * sensible algorithm. 142 + * 143 + * -------- 144 + * 145 + * Another thing that might be needed is to characterise _which_ 146 + * homotopy class a given path is in. 147 + * 148 + * For this I think it's sufficient to choose a collection of paths 149 + * along the _edges_ of the square grid, each of which connects two of 150 + * the holes in the grid (including the grid exterior, which counts as 151 + * a huge hole), such that they form a spanning tree between the 152 + * holes. Then assign each of those paths an orientation, so that 153 + * crossing it in one direction counts as 'positive' and the other 154 + * 'negative'. Now analyse a candidate path from one square to another 155 + * by following it and noting down which of those paths it crosses in 156 + * which direction, then simplifying the result like a free group word 157 + * (i.e. adjacent + and - crossings of the same path cancel out). 158 + * 159 + * -------- 160 + * 161 + * If we choose those paths to be of minimal length, then we can get 162 + * an upper bound on the number of homotopy classes by observing that 163 + * you can't traverse any of those barriers more times than will fit 164 + * non-self-intersectingly in the grid. That might be an alternative 165 + * method of bounding the search through the fundamental group to only 166 + * finitely many possibilities. 167 + */ 168 + 169 + /* 170 + * Standard notation for directions. 171 + */ 172 + #define L 0 173 + #define U 1 174 + #define R 2 175 + #define D 3 176 + #define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0) 177 + #define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0) 178 + 179 + /* 180 + * Perform a breadth-first search over a grid of squares with the 181 + * colour of square (X,Y) given by grid[Y*w+X]. The search begins 182 + * at (x,y), and finds all squares which are the same colour as 183 + * (x,y) and reachable from it by orthogonal moves. On return: 184 + * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if 185 + * unreachable or a different colour 186 + * - the returned value is the number of reachable squares, 187 + * including (x,y) itself 188 + * - list[0] up to list[returned value - 1] list those squares, in 189 + * increasing order of distance from (x,y) (and in arbitrary 190 + * order within that). 191 + */ 192 + static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list) 193 + { 194 + int i, j, c, listsize, listdone; 195 + 196 + /* 197 + * Start by clearing the output arrays. 198 + */ 199 + for (i = 0; i < w*h; i++) 200 + dist[i] = list[i] = -1; 201 + 202 + /* 203 + * Set up the initial list. 204 + */ 205 + listsize = 1; 206 + listdone = 0; 207 + list[0] = y*w+x; 208 + dist[y*w+x] = 0; 209 + c = grid[y*w+x]; 210 + 211 + /* 212 + * Repeatedly process a square and add any extra squares to the 213 + * end of list. 214 + */ 215 + while (listdone < listsize) { 216 + i = list[listdone++]; 217 + y = i / w; 218 + x = i % w; 219 + for (j = 0; j < 4; j++) { 220 + int xx, yy, ii; 221 + 222 + xx = x + DX(j); 223 + yy = y + DY(j); 224 + ii = yy*w+xx; 225 + 226 + if (xx >= 0 && xx < w && yy >= 0 && yy < h && 227 + grid[ii] == c && dist[ii] == -1) { 228 + dist[ii] = dist[i] + 1; 229 + assert(listsize < w*h); 230 + list[listsize++] = ii; 231 + } 232 + } 233 + } 234 + 235 + return listsize; 236 + } 237 + 238 + struct genctx { 239 + int w, h; 240 + int *grid, *sparegrid, *sparegrid2, *sparegrid3; 241 + int *dist, *list; 242 + 243 + int npaths, pathsize; 244 + int *pathends, *sparepathends; /* 2*npaths entries */ 245 + int *pathspare; /* npaths entries */ 246 + int *extends; /* 8*npaths entries */ 247 + }; 248 + 249 + static struct genctx *new_genctx(int w, int h) 250 + { 251 + struct genctx *ctx = snew(struct genctx); 252 + ctx->w = w; 253 + ctx->h = h; 254 + ctx->grid = snewn(w * h, int); 255 + ctx->sparegrid = snewn(w * h, int); 256 + ctx->sparegrid2 = snewn(w * h, int); 257 + ctx->sparegrid3 = snewn(w * h, int); 258 + ctx->dist = snewn(w * h, int); 259 + ctx->list = snewn(w * h, int); 260 + ctx->npaths = ctx->pathsize = 0; 261 + ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL; 262 + return ctx; 263 + } 264 + 265 + static void free_genctx(struct genctx *ctx) 266 + { 267 + sfree(ctx->grid); 268 + sfree(ctx->sparegrid); 269 + sfree(ctx->sparegrid2); 270 + sfree(ctx->sparegrid3); 271 + sfree(ctx->dist); 272 + sfree(ctx->list); 273 + sfree(ctx->pathends); 274 + sfree(ctx->sparepathends); 275 + sfree(ctx->pathspare); 276 + sfree(ctx->extends); 277 + } 278 + 279 + static int newpath(struct genctx *ctx) 280 + { 281 + int n; 282 + 283 + n = ctx->npaths++; 284 + if (ctx->npaths > ctx->pathsize) { 285 + ctx->pathsize += 16; 286 + ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int); 287 + ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int); 288 + ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int); 289 + ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int); 290 + } 291 + return n; 292 + } 293 + 294 + static int is_endpoint(struct genctx *ctx, int x, int y) 295 + { 296 + int w = ctx->w, h = ctx->h, c; 297 + 298 + assert(x >= 0 && x < w && y >= 0 && y < h); 299 + 300 + c = ctx->grid[y*w+x]; 301 + if (c < 0) 302 + return false; /* empty square is not an endpoint! */ 303 + assert(c >= 0 && c < ctx->npaths); 304 + if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x) 305 + return true; 306 + return false; 307 + } 308 + 309 + /* 310 + * Tries to extend a path by one square in the given direction, 311 + * pushing other paths around if necessary. Returns true on success 312 + * or false on failure. 313 + */ 314 + static int extend_path(struct genctx *ctx, int path, int end, int direction) 315 + { 316 + int w = ctx->w, h = ctx->h; 317 + int x, y, xe, ye, cut; 318 + int i, j, jp, n, first, last; 319 + 320 + assert(path >= 0 && path < ctx->npaths); 321 + assert(end == 0 || end == 1); 322 + 323 + /* 324 + * Find the endpoint of the path and the point we plan to 325 + * extend it into. 326 + */ 327 + y = ctx->pathends[path * 2 + end] / w; 328 + x = ctx->pathends[path * 2 + end] % w; 329 + assert(x >= 0 && x < w && y >= 0 && y < h); 330 + 331 + xe = x + DX(direction); 332 + ye = y + DY(direction); 333 + if (xe < 0 || xe >= w || ye < 0 || ye >= h) 334 + return false; /* could not extend in this direction */ 335 + 336 + /* 337 + * We don't extend paths _directly_ into endpoints of other 338 + * paths, although we don't mind too much if a knock-on effect 339 + * of an extension is to push part of another path into a third 340 + * path's endpoint. 341 + */ 342 + if (is_endpoint(ctx, xe, ye)) 343 + return false; 344 + 345 + /* 346 + * We can't extend a path back the way it came. 347 + */ 348 + if (ctx->grid[ye*w+xe] == path) 349 + return false; 350 + 351 + /* 352 + * Paths may not double back on themselves. Check if the new 353 + * point is adjacent to any point of this path other than (x,y). 354 + */ 355 + for (j = 0; j < 4; j++) { 356 + int xf, yf; 357 + 358 + xf = xe + DX(j); 359 + yf = ye + DY(j); 360 + 361 + if (xf >= 0 && xf < w && yf >= 0 && yf < h && 362 + (xf != x || yf != y) && ctx->grid[yf*w+xf] == path) 363 + return false; 364 + } 365 + 366 + /* 367 + * Now we're convinced it's valid to _attempt_ the extension. 368 + * It may still fail if we run out of space to push other paths 369 + * into. 370 + * 371 + * So now we can set up our temporary data structures. We will 372 + * need: 373 + * 374 + * - a spare copy of the grid on which to gradually move paths 375 + * around (sparegrid) 376 + * 377 + * - a second spare copy with which to remember how paths 378 + * looked just before being cut (sparegrid2). FIXME: is 379 + * sparegrid2 necessary? right now it's never different from 380 + * grid itself 381 + * 382 + * - a third spare copy with which to do the internal 383 + * calculations involved in reconstituting a cut path 384 + * (sparegrid3) 385 + * 386 + * - something to track which paths currently need 387 + * reconstituting after being cut, and which have already 388 + * been cut (pathspare) 389 + * 390 + * - a spare copy of pathends to store the altered states in 391 + * (sparepathends) 392 + */ 393 + memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int)); 394 + memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int)); 395 + memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int)); 396 + for (i = 0; i < ctx->npaths; i++) 397 + ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */ 398 + 399 + /* 400 + * Working in sparegrid, actually extend the path. If it cuts 401 + * another, begin a loop in which we restore any cut path by 402 + * moving it out of the way. 403 + */ 404 + cut = ctx->sparegrid[ye*w+xe]; 405 + ctx->sparegrid[ye*w+xe] = path; 406 + ctx->sparepathends[path*2+end] = ye*w+xe; 407 + ctx->pathspare[path] = 2; /* this one is sacrosanct */ 408 + if (cut >= 0) { 409 + assert(cut >= 0 && cut < ctx->npaths); 410 + ctx->pathspare[cut] = 1; /* broken */ 411 + 412 + while (1) { 413 + for (i = 0; i < ctx->npaths; i++) 414 + if (ctx->pathspare[i] == 1) 415 + break; 416 + if (i == ctx->npaths) 417 + break; /* we're done */ 418 + 419 + /* 420 + * Path i needs restoring. So walk along its original 421 + * track (as given in sparegrid2) and see where it's 422 + * been cut. Where it has, surround the cut points in 423 + * the same colour, without overwriting already-fixed 424 + * paths. 425 + */ 426 + memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int)); 427 + n = bfs(w, h, ctx->sparegrid2, 428 + ctx->pathends[i*2] % w, ctx->pathends[i*2] / w, 429 + ctx->dist, ctx->list); 430 + first = last = -1; 431 + if (ctx->sparegrid3[ctx->pathends[i*2]] != i || 432 + ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return false;/* FIXME */ 433 + for (j = 0; j < n; j++) { 434 + jp = ctx->list[j]; 435 + assert(ctx->dist[jp] == j); 436 + assert(ctx->sparegrid2[jp] == i); 437 + 438 + /* 439 + * Wipe out the original path in sparegrid. 440 + */ 441 + if (ctx->sparegrid[jp] == i) 442 + ctx->sparegrid[jp] = -1; 443 + 444 + /* 445 + * Be prepared to shorten the path at either end if 446 + * the endpoints have been stomped on. 447 + */ 448 + if (ctx->sparegrid3[jp] == i) { 449 + if (first < 0) 450 + first = jp; 451 + last = jp; 452 + } 453 + 454 + if (ctx->sparegrid3[jp] != i) { 455 + int jx = jp % w, jy = jp / w; 456 + int dx, dy; 457 + for (dy = -1; dy <= +1; dy++) 458 + for (dx = -1; dx <= +1; dx++) { 459 + int newp, newv; 460 + if (!dy && !dx) 461 + continue; /* central square */ 462 + if (jx+dx < 0 || jx+dx >= w || 463 + jy+dy < 0 || jy+dy >= h) 464 + continue; /* out of range */ 465 + newp = (jy+dy)*w+(jx+dx); 466 + newv = ctx->sparegrid3[newp]; 467 + if (newv >= 0 && (newv == i || 468 + ctx->pathspare[newv] == 2)) 469 + continue; /* can't use this square */ 470 + ctx->sparegrid3[newp] = i; 471 + } 472 + } 473 + } 474 + 475 + if (first < 0 || last < 0) 476 + return false; /* path is completely wiped out! */ 477 + 478 + /* 479 + * Now we've covered sparegrid3 in possible squares for 480 + * the new layout of path i. Find the actual layout 481 + * we're going to use by bfs: we want the shortest path 482 + * from one endpoint to the other. 483 + */ 484 + n = bfs(w, h, ctx->sparegrid3, first % w, first / w, 485 + ctx->dist, ctx->list); 486 + if (ctx->dist[last] < 2) { 487 + /* 488 + * Either there is no way to get between the path's 489 + * endpoints, or the remaining endpoints simply 490 + * aren't far enough apart to make the path viable 491 + * any more. This means the entire push operation 492 + * has failed. 493 + */ 494 + return false; 495 + } 496 + 497 + /* 498 + * Write the new path into sparegrid. Also save the new 499 + * endpoint locations, in case they've changed. 500 + */ 501 + jp = last; 502 + j = ctx->dist[jp]; 503 + while (1) { 504 + int d; 505 + 506 + if (ctx->sparegrid[jp] >= 0) { 507 + if (ctx->pathspare[ctx->sparegrid[jp]] == 2) 508 + return false; /* somehow we've hit a fixed path */ 509 + ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */ 510 + } 511 + ctx->sparegrid[jp] = i; 512 + 513 + if (j == 0) 514 + break; 515 + 516 + /* 517 + * Now look at the neighbours of jp to find one 518 + * which has dist[] one less. 519 + */ 520 + for (d = 0; d < 4; d++) { 521 + int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d); 522 + if (jx >= 0 && jx < w && jy >= 0 && jy < w && 523 + ctx->dist[jy*w+jx] == j-1) { 524 + jp = jy*w+jx; 525 + j--; 526 + break; 527 + } 528 + } 529 + assert(d < 4); 530 + } 531 + 532 + ctx->sparepathends[i*2] = first; 533 + ctx->sparepathends[i*2+1] = last; 534 + /* printf("new ends of path %d: %d,%d\n", i, first, last); */ 535 + ctx->pathspare[i] = 2; /* fixed */ 536 + } 537 + } 538 + 539 + /* 540 + * If we got here, the extension was successful! 541 + */ 542 + memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int)); 543 + memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int)); 544 + return true; 545 + } 546 + 547 + /* 548 + * Tries to add a new path to the grid. 549 + */ 550 + static int add_path(struct genctx *ctx, random_state *rs) 551 + { 552 + int w = ctx->w, h = ctx->h; 553 + int i, ii, n; 554 + 555 + /* 556 + * Our strategy is: 557 + * - randomly choose an empty square in the grid 558 + * - do a BFS from that point to find a long path starting 559 + * from it 560 + * - if we run out of viable empty squares, return failure. 561 + */ 562 + 563 + /* 564 + * Use `sparegrid' to collect a list of empty squares. 565 + */ 566 + n = 0; 567 + for (i = 0; i < w*h; i++) 568 + if (ctx->grid[i] == -1) 569 + ctx->sparegrid[n++] = i; 570 + 571 + /* 572 + * Shuffle the grid. 573 + */ 574 + for (i = n; i-- > 1 ;) { 575 + int k = random_upto(rs, i+1); 576 + if (k != i) { 577 + int t = ctx->sparegrid[i]; 578 + ctx->sparegrid[i] = ctx->sparegrid[k]; 579 + ctx->sparegrid[k] = t; 580 + } 581 + } 582 + 583 + /* 584 + * Loop over it trying to add paths. This looks like a 585 + * horrifying N^4 algorithm (that is, (w*h)^2), but I predict 586 + * that in fact the worst case will very rarely arise because 587 + * when there's lots of grid space an attempt will succeed very 588 + * quickly. 589 + */ 590 + for (ii = 0; ii < n; ii++) { 591 + int i = ctx->sparegrid[ii]; 592 + int y = i / w, x = i % w, nsq; 593 + int r, c, j; 594 + 595 + /* 596 + * BFS from here to find long paths. 597 + */ 598 + nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list); 599 + 600 + /* 601 + * If there aren't any long enough, give up immediately. 602 + */ 603 + assert(nsq > 0); /* must be the start square at least! */ 604 + if (ctx->dist[ctx->list[nsq-1]] < 3) 605 + continue; 606 + 607 + /* 608 + * Find the first viable endpoint in ctx->list (i.e. the 609 + * first point with distance at least three). I could 610 + * binary-search for this, but that would be O(log N) 611 + * whereas in fact I can get a constant time bound by just 612 + * searching up from the start - after all, there can be at 613 + * most 13 points at _less_ than distance 3 from the 614 + * starting one! 615 + */ 616 + for (j = 0; j < nsq; j++) 617 + if (ctx->dist[ctx->list[j]] >= 3) 618 + break; 619 + assert(j < nsq); /* we tested above that there was one */ 620 + 621 + /* 622 + * Now we know that any element of `list' between j and nsq 623 + * would be valid in principle. However, we want a few long 624 + * paths rather than many small ones, so select only those 625 + * elements which are either the maximum length or one 626 + * below it. 627 + */ 628 + while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]]) 629 + j++; 630 + r = j + random_upto(rs, nsq - j); 631 + j = ctx->list[r]; 632 + 633 + /* 634 + * And that's our endpoint. Mark the new path on the grid. 635 + */ 636 + c = newpath(ctx); 637 + ctx->pathends[c*2] = i; 638 + ctx->pathends[c*2+1] = j; 639 + ctx->grid[j] = c; 640 + while (j != i) { 641 + int d, np, index, pts[4]; 642 + np = 0; 643 + for (d = 0; d < 4; d++) { 644 + int xn = (j % w) + DX(d), yn = (j / w) + DY(d); 645 + if (xn >= 0 && xn < w && yn >= 0 && yn < w && 646 + ctx->dist[yn*w+xn] == ctx->dist[j] - 1) 647 + pts[np++] = yn*w+xn; 648 + } 649 + if (np > 1) 650 + index = random_upto(rs, np); 651 + else 652 + index = 0; 653 + j = pts[index]; 654 + ctx->grid[j] = c; 655 + } 656 + 657 + return true; 658 + } 659 + 660 + return false; 661 + } 662 + 663 + /* 664 + * The main grid generation loop. 665 + */ 666 + static void gridgen_mainloop(struct genctx *ctx, random_state *rs) 667 + { 668 + int w = ctx->w, h = ctx->h; 669 + int i, n; 670 + 671 + /* 672 + * The generation algorithm doesn't always converge. Loop round 673 + * until it does. 674 + */ 675 + while (1) { 676 + for (i = 0; i < w*h; i++) 677 + ctx->grid[i] = -1; 678 + ctx->npaths = 0; 679 + 680 + while (1) { 681 + /* 682 + * See if the grid is full. 683 + */ 684 + for (i = 0; i < w*h; i++) 685 + if (ctx->grid[i] < 0) 686 + break; 687 + if (i == w*h) 688 + return; 689 + 690 + #ifdef GENERATION_DIAGNOSTICS 691 + { 692 + int x, y; 693 + for (y = 0; y < h; y++) { 694 + printf("|"); 695 + for (x = 0; x < w; x++) { 696 + if (ctx->grid[y*w+x] >= 0) 697 + printf("%2d", ctx->grid[y*w+x]); 698 + else 699 + printf(" ."); 700 + } 701 + printf(" |\n"); 702 + } 703 + } 704 + #endif 705 + /* 706 + * Try adding a path. 707 + */ 708 + if (add_path(ctx, rs)) { 709 + #ifdef GENERATION_DIAGNOSTICS 710 + printf("added path\n"); 711 + #endif 712 + continue; 713 + } 714 + 715 + /* 716 + * Try extending a path. First list all the possible 717 + * extensions. 718 + */ 719 + for (i = 0; i < ctx->npaths * 8; i++) 720 + ctx->extends[i] = i; 721 + n = i; 722 + 723 + /* 724 + * Then shuffle the list. 725 + */ 726 + for (i = n; i-- > 1 ;) { 727 + int k = random_upto(rs, i+1); 728 + if (k != i) { 729 + int t = ctx->extends[i]; 730 + ctx->extends[i] = ctx->extends[k]; 731 + ctx->extends[k] = t; 732 + } 733 + } 734 + 735 + /* 736 + * Now try each one in turn until one works. 737 + */ 738 + for (i = 0; i < n; i++) { 739 + int p, d, e; 740 + p = ctx->extends[i]; 741 + d = p % 4; 742 + p /= 4; 743 + e = p % 2; 744 + p /= 2; 745 + 746 + #ifdef GENERATION_DIAGNOSTICS 747 + printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e, 748 + ctx->pathends[p*2+e] % w, 749 + ctx->pathends[p*2+e] / w, d); 750 + #endif 751 + if (extend_path(ctx, p, e, d)) { 752 + #ifdef GENERATION_DIAGNOSTICS 753 + printf("extended path %d end %d (%d,%d) in dir %d\n", p, e, 754 + ctx->pathends[p*2+e] % w, 755 + ctx->pathends[p*2+e] / w, d); 756 + #endif 757 + break; 758 + } 759 + } 760 + 761 + if (i < n) 762 + continue; 763 + 764 + break; 765 + } 766 + } 767 + } 768 + 769 + /* 770 + * Wrapper function which deals with the boring bits such as 771 + * removing the solution from the generated grid, shuffling the 772 + * numeric labels and creating/disposing of the context structure. 773 + */ 774 + static int *gridgen(int w, int h, random_state *rs) 775 + { 776 + struct genctx *ctx; 777 + int *ret; 778 + int i; 779 + 780 + ctx = new_genctx(w, h); 781 + 782 + gridgen_mainloop(ctx, rs); 783 + 784 + /* 785 + * There is likely to be an ordering bias in the numbers 786 + * (longer paths on lower numbers due to there having been more 787 + * grid space when laying them down). So we must shuffle the 788 + * numbers. We use ctx->pathspare for this. 789 + * 790 + * This is also as good a time as any to shift to numbering 791 + * from 1, for display to the user. 792 + */ 793 + for (i = 0; i < ctx->npaths; i++) 794 + ctx->pathspare[i] = i+1; 795 + for (i = ctx->npaths; i-- > 1 ;) { 796 + int k = random_upto(rs, i+1); 797 + if (k != i) { 798 + int t = ctx->pathspare[i]; 799 + ctx->pathspare[i] = ctx->pathspare[k]; 800 + ctx->pathspare[k] = t; 801 + } 802 + } 803 + 804 + /* FIXME: remove this at some point! */ 805 + { 806 + int y, x; 807 + for (y = 0; y < h; y++) { 808 + printf("|"); 809 + for (x = 0; x < w; x++) { 810 + assert(ctx->grid[y*w+x] >= 0); 811 + printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]); 812 + } 813 + printf(" |\n"); 814 + } 815 + printf("\n"); 816 + } 817 + 818 + /* 819 + * Clear the grid, and write in just the endpoints. 820 + */ 821 + for (i = 0; i < w*h; i++) 822 + ctx->grid[i] = 0; 823 + for (i = 0; i < ctx->npaths; i++) { 824 + ctx->grid[ctx->pathends[i*2]] = 825 + ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i]; 826 + } 827 + 828 + ret = ctx->grid; 829 + ctx->grid = NULL; 830 + 831 + free_genctx(ctx); 832 + 833 + return ret; 834 + } 835 + 836 + #ifdef TEST_GEN 837 + 838 + #define TEST_GENERAL 839 + 840 + int main(void) 841 + { 842 + int w = 10, h = 8; 843 + random_state *rs = random_new("12345", 5); 844 + int x, y, i, *grid; 845 + 846 + for (i = 0; i < 10; i++) { 847 + grid = gridgen(w, h, rs); 848 + 849 + for (y = 0; y < h; y++) { 850 + printf("|"); 851 + for (x = 0; x < w; x++) { 852 + if (grid[y*w+x] > 0) 853 + printf("%2d", grid[y*w+x]); 854 + else 855 + printf(" ."); 856 + } 857 + printf(" |\n"); 858 + } 859 + printf("\n"); 860 + 861 + sfree(grid); 862 + } 863 + 864 + return 0; 865 + } 866 + #endif
+861
apps/plugins/puzzles/src/unfinished/separate.c
··· 1 + /* 2 + * separate.c: Implementation of `Block Puzzle', a Japanese-only 3 + * Nikoli puzzle seen at 4 + * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/ 5 + * 6 + * It's difficult to be absolutely sure of the rules since online 7 + * Japanese translators are so bad, but looking at the sample 8 + * puzzle it seems fairly clear that the rules of this one are 9 + * very simple. You have an mxn grid in which every square 10 + * contains a letter, there are k distinct letters with k dividing 11 + * mn, and every letter occurs the same number of times; your aim 12 + * is to find a partition of the grid into disjoint k-ominoes such 13 + * that each k-omino contains exactly one of each letter. 14 + * 15 + * (It may be that Nikoli always have m,n,k equal to one another. 16 + * However, I don't see that that's critical to the puzzle; k|mn 17 + * is the only really important constraint, and even that could 18 + * probably be dispensed with if some squares were marked as 19 + * unused.) 20 + */ 21 + 22 + /* 23 + * Current status: only the solver/generator is yet written, and 24 + * although working in principle it's _very_ slow. It generates 25 + * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and 26 + * 7x7n7 only with a serious strain. I haven't dared try it higher 27 + * than that yet. 28 + * 29 + * One idea to speed it up is to implement more of the solver. 30 + * Ideas I've so far had include: 31 + * 32 + * - Generalise the deduction currently expressed as `an 33 + * undersized chain with only one direction to extend must take 34 + * it'. More generally, the deduction should say `if all the 35 + * possible k-ominoes containing a given chain also contain 36 + * square x, then mark square x as part of that k-omino'. 37 + * + For example, consider this case: 38 + * 39 + * a ? b This represents the top left of a board; the letters 40 + * ? ? ? a,b,c do not represent the letters used in the puzzle, 41 + * c ? ? but indicate that those three squares are known to be 42 + * of different ominoes. Now if k >= 4, we can immediately 43 + * deduce that the square midway between b and c belongs to the 44 + * same omino as a, because there is no way we can make a 4-or- 45 + * more-omino containing a which does not also contain that square. 46 + * (Most easily seen by imagining cutting that square out of the 47 + * grid; then, clearly, the omino containing a has only two 48 + * squares to expand into, and needs at least three.) 49 + * 50 + * The key difficulty with this mode of reasoning is 51 + * identifying such squares. I can't immediately think of a 52 + * simple algorithm for finding them on a wholesale basis. 53 + * 54 + * - Bfs out from a chain looking for the letters it lacks. For 55 + * example, in this situation (top three rows of a 7x7n7 grid): 56 + * 57 + * +-----------+-+ 58 + * |E-A-F-B-C D|D| 59 + * +------- || 60 + * |E-C-G-D G|G E| 61 + * +-+--- | 62 + * |E|E G A B F A| 63 + * 64 + * In this situation we can be sure that the top left chain 65 + * E-A-F-B-C does extend rightwards to the D, because there is 66 + * no other D within reach of that chain. Note also that the 67 + * bfs can skip squares which are known to belong to other 68 + * ominoes than this one. 69 + * 70 + * (This deduction, I fear, should only be used in an 71 + * emergency, because it relies on _all_ squares within range 72 + * of the bfs having particular values and so using it during 73 + * incremental generation rather nails down a lot of the grid.) 74 + * 75 + * It's conceivable that another thing we could do would be to 76 + * increase the flexibility in the grid generator: instead of 77 + * nailing down the _value_ of any square depended on, merely nail 78 + * down its equivalence to other squares. Unfortunately this turns 79 + * the letter-selection phase of generation into a general graph 80 + * colouring problem (we must draw a graph with equivalence 81 + * classes of squares as the vertices, and an edge between any two 82 + * vertices representing equivalence classes which contain squares 83 + * that share an omino, and then k-colour the result) and hence 84 + * requires recursion, which bodes ill for something we're doing 85 + * that many times per generation. 86 + * 87 + * I suppose a simple thing I could try would be tuning the retry 88 + * count, just in case it's set too high or too low for efficient 89 + * generation. 90 + */ 91 + 92 + #include <stdio.h> 93 + #include <stdlib.h> 94 + #include <string.h> 95 + #include <assert.h> 96 + #include <ctype.h> 97 + #ifdef NO_TGMATH_H 98 + # include <math.h> 99 + #else 100 + # include <tgmath.h> 101 + #endif 102 + 103 + #include "puzzles.h" 104 + 105 + enum { 106 + COL_BACKGROUND, 107 + NCOLOURS 108 + }; 109 + 110 + struct game_params { 111 + int w, h, k; 112 + }; 113 + 114 + struct game_state { 115 + int FIXME; 116 + }; 117 + 118 + static game_params *default_params(void) 119 + { 120 + game_params *ret = snew(game_params); 121 + 122 + ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */ 123 + 124 + return ret; 125 + } 126 + 127 + static bool game_fetch_preset(int i, char **name, game_params **params) 128 + { 129 + return false; 130 + } 131 + 132 + static void free_params(game_params *params) 133 + { 134 + sfree(params); 135 + } 136 + 137 + static game_params *dup_params(const game_params *params) 138 + { 139 + game_params *ret = snew(game_params); 140 + *ret = *params; /* structure copy */ 141 + return ret; 142 + } 143 + 144 + static void decode_params(game_params *params, char const *string) 145 + { 146 + params->w = params->h = params->k = atoi(string); 147 + while (*string && isdigit((unsigned char)*string)) string++; 148 + if (*string == 'x') { 149 + string++; 150 + params->h = atoi(string); 151 + while (*string && isdigit((unsigned char)*string)) string++; 152 + } 153 + if (*string == 'n') { 154 + string++; 155 + params->k = atoi(string); 156 + while (*string && isdigit((unsigned char)*string)) string++; 157 + } 158 + } 159 + 160 + static char *encode_params(const game_params *params, bool full) 161 + { 162 + char buf[256]; 163 + sprintf(buf, "%dx%dn%d", params->w, params->h, params->k); 164 + return dupstr(buf); 165 + } 166 + 167 + static config_item *game_configure(const game_params *params) 168 + { 169 + return NULL; 170 + } 171 + 172 + static game_params *custom_params(const config_item *cfg) 173 + { 174 + return NULL; 175 + } 176 + 177 + static const char *validate_params(const game_params *params, bool full) 178 + { 179 + return NULL; 180 + } 181 + 182 + /* ---------------------------------------------------------------------- 183 + * Solver and generator. 184 + */ 185 + 186 + struct solver_scratch { 187 + int w, h, k; 188 + 189 + /* 190 + * Tracks connectedness between squares. 191 + */ 192 + DSF *dsf; 193 + 194 + /* 195 + * size[dsf_canonify(dsf, yx)] tracks the size of the 196 + * connected component containing yx. 197 + */ 198 + int *size; 199 + 200 + /* 201 + * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not 202 + * the connected component containing yx includes letter i. If 203 + * the value is -1, it doesn't; otherwise its value is the 204 + * index in the main grid of the square which contributes that 205 + * letter to the component. 206 + */ 207 + int *contents; 208 + 209 + /* 210 + * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)] 211 + * tracks whether or not the connected components containing 212 + * yx1 and yx2 are known to be distinct. 213 + */ 214 + bool *disconnect; 215 + 216 + /* 217 + * Temporary space used only inside particular solver loops. 218 + */ 219 + int *tmp; 220 + }; 221 + 222 + static struct solver_scratch *solver_scratch_new(int w, int h, int k) 223 + { 224 + int wh = w*h; 225 + struct solver_scratch *sc = snew(struct solver_scratch); 226 + 227 + sc->w = w; 228 + sc->h = h; 229 + sc->k = k; 230 + 231 + sc->dsf = dsf_new(wh); 232 + sc->size = snewn(wh, int); 233 + sc->contents = snewn(wh * k, int); 234 + sc->disconnect = snewn(wh*wh, bool); 235 + sc->tmp = snewn(wh, int); 236 + 237 + return sc; 238 + } 239 + 240 + static void solver_scratch_free(struct solver_scratch *sc) 241 + { 242 + dsf_free(sc->dsf); 243 + sfree(sc->size); 244 + sfree(sc->contents); 245 + sfree(sc->disconnect); 246 + sfree(sc->tmp); 247 + sfree(sc); 248 + } 249 + 250 + static void solver_connect(struct solver_scratch *sc, int yx1, int yx2) 251 + { 252 + int w = sc->w, h = sc->h, k = sc->k; 253 + int wh = w*h; 254 + int i, yxnew; 255 + 256 + yx1 = dsf_canonify(sc->dsf, yx1); 257 + yx2 = dsf_canonify(sc->dsf, yx2); 258 + assert(yx1 != yx2); 259 + 260 + /* 261 + * To connect two components together into a bigger one, we 262 + * start by merging them in the dsf itself. 263 + */ 264 + dsf_merge(sc->dsf, yx1, yx2); 265 + yxnew = dsf_canonify(sc->dsf, yx2); 266 + 267 + /* 268 + * The size of the new component is the sum of the sizes of the 269 + * old ones. 270 + */ 271 + sc->size[yxnew] = sc->size[yx1] + sc->size[yx2]; 272 + 273 + /* 274 + * The contents bitmap of the new component is the union of the 275 + * contents of the old ones. 276 + * 277 + * Given two numbers at most one of which is not -1, we can 278 + * find the other one by adding the two and adding 1; this 279 + * will yield -1 if both were -1 to begin with, otherwise the 280 + * other. 281 + * 282 + * (A neater approach would be to take their bitwise AND, but 283 + * this is unfortunately not well-defined standard C when done 284 + * to signed integers.) 285 + */ 286 + for (i = 0; i < k; i++) { 287 + assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0); 288 + sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] + 289 + sc->contents[yx2*k+i] + 1); 290 + } 291 + 292 + /* 293 + * We must combine the rows _and_ the columns in the disconnect 294 + * matrix. 295 + */ 296 + for (i = 0; i < wh; i++) 297 + sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] || 298 + sc->disconnect[yx2*wh+i]); 299 + for (i = 0; i < wh; i++) 300 + sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] || 301 + sc->disconnect[i*wh+yx2]); 302 + } 303 + 304 + static void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2) 305 + { 306 + int w = sc->w, h = sc->h; 307 + int wh = w*h; 308 + 309 + yx1 = dsf_canonify(sc->dsf, yx1); 310 + yx2 = dsf_canonify(sc->dsf, yx2); 311 + assert(yx1 != yx2); 312 + assert(!sc->disconnect[yx1*wh+yx2]); 313 + assert(!sc->disconnect[yx2*wh+yx1]); 314 + 315 + /* 316 + * Mark the components as disconnected from each other in the 317 + * disconnect matrix. 318 + */ 319 + sc->disconnect[yx1*wh+yx2] = true; 320 + sc->disconnect[yx2*wh+yx1] = true; 321 + } 322 + 323 + static void solver_init(struct solver_scratch *sc) 324 + { 325 + int w = sc->w, h = sc->h; 326 + int wh = w*h; 327 + int i; 328 + 329 + /* 330 + * Set up most of the scratch space. We don't set up the 331 + * contents array, however, because this will change if we 332 + * adjust the letter arrangement and re-run the solver. 333 + */ 334 + dsf_reinit(sc->dsf); 335 + for (i = 0; i < wh; i++) sc->size[i] = 1; 336 + memset(sc->disconnect, 0, wh*wh * sizeof(bool)); 337 + } 338 + 339 + static int solver_attempt(struct solver_scratch *sc, const unsigned char *grid, 340 + bool *gen_lock) 341 + { 342 + int w = sc->w, h = sc->h, k = sc->k; 343 + int wh = w*h; 344 + int i, x, y; 345 + bool done_something_overall = false; 346 + 347 + /* 348 + * Set up the contents array from the grid. 349 + */ 350 + for (i = 0; i < wh*k; i++) 351 + sc->contents[i] = -1; 352 + for (i = 0; i < wh; i++) 353 + sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i; 354 + 355 + while (1) { 356 + bool done_something = false; 357 + 358 + /* 359 + * Go over the grid looking for reasons to add to the 360 + * disconnect matrix. We're after pairs of squares which: 361 + * 362 + * - are adjacent in the grid 363 + * - belong to distinct dsf components 364 + * - their components are not already marked as 365 + * disconnected 366 + * - their components share a letter in common. 367 + */ 368 + for (y = 0; y < h; y++) { 369 + for (x = 0; x < w; x++) { 370 + int dir; 371 + for (dir = 0; dir < 2; dir++) { 372 + int x2 = x + dir, y2 = y + 1 - dir; 373 + int yx = y*w+x, yx2 = y2*w+x2; 374 + 375 + if (x2 >= w || y2 >= h) 376 + continue; /* one square is outside the grid */ 377 + 378 + yx = dsf_canonify(sc->dsf, yx); 379 + yx2 = dsf_canonify(sc->dsf, yx2); 380 + if (yx == yx2) 381 + continue; /* same dsf component */ 382 + 383 + if (sc->disconnect[yx*wh+yx2]) 384 + continue; /* already known disconnected */ 385 + 386 + for (i = 0; i < k; i++) 387 + if (sc->contents[yx*k+i] >= 0 && 388 + sc->contents[yx2*k+i] >= 0) 389 + break; 390 + if (i == k) 391 + continue; /* no letter in common */ 392 + 393 + /* 394 + * We've found one. Mark yx and yx2 as 395 + * disconnected from each other. 396 + */ 397 + #ifdef SOLVER_DIAGNOSTICS 398 + printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i); 399 + #endif 400 + solver_disconnect(sc, yx, yx2); 401 + done_something = done_something_overall = true; 402 + 403 + /* 404 + * We have just made a deduction which hinges 405 + * on two particular grid squares being the 406 + * same. If we are feeding back to a generator 407 + * loop, we must therefore mark those squares 408 + * as fixed in the generator, so that future 409 + * rearrangement of the grid will not break 410 + * the information on which we have already 411 + * based deductions. 412 + */ 413 + if (gen_lock) { 414 + gen_lock[sc->contents[yx*k+i]] = true; 415 + gen_lock[sc->contents[yx2*k+i]] = true; 416 + } 417 + } 418 + } 419 + } 420 + 421 + /* 422 + * Now go over the grid looking for dsf components which 423 + * are below maximum size and only have one way to extend, 424 + * and extending them. 425 + */ 426 + for (i = 0; i < wh; i++) 427 + sc->tmp[i] = -1; 428 + for (y = 0; y < h; y++) { 429 + for (x = 0; x < w; x++) { 430 + int yx = dsf_canonify(sc->dsf, y*w+x); 431 + int dir; 432 + 433 + if (sc->size[yx] == k) 434 + continue; 435 + 436 + for (dir = 0; dir < 4; dir++) { 437 + int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0); 438 + int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0); 439 + int yx2, yx2c; 440 + 441 + if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w) 442 + continue; 443 + yx2 = y2*w+x2; 444 + yx2c = dsf_canonify(sc->dsf, yx2); 445 + 446 + if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) { 447 + /* 448 + * Component yx can be extended into square 449 + * yx2. 450 + */ 451 + if (sc->tmp[yx] == -1) 452 + sc->tmp[yx] = yx2; 453 + else if (sc->tmp[yx] != yx2) 454 + sc->tmp[yx] = -2; /* multiple choices found */ 455 + } 456 + } 457 + } 458 + } 459 + for (i = 0; i < wh; i++) { 460 + if (sc->tmp[i] >= 0) { 461 + /* 462 + * Make sure we haven't connected the two already 463 + * during this loop (which could happen if for 464 + * _both_ components this was the only way to 465 + * extend them). 466 + */ 467 + if (dsf_canonify(sc->dsf, i) == 468 + dsf_canonify(sc->dsf, sc->tmp[i])) 469 + continue; 470 + 471 + #ifdef SOLVER_DIAGNOSTICS 472 + printf("Connecting %d and %d\n", i, sc->tmp[i]); 473 + #endif 474 + solver_connect(sc, i, sc->tmp[i]); 475 + done_something = done_something_overall = true; 476 + break; 477 + } 478 + } 479 + 480 + if (!done_something) 481 + break; 482 + } 483 + 484 + /* 485 + * Return 0 if we haven't made any progress; 1 if we've done 486 + * something but not solved it completely; 2 if we've solved 487 + * it completely. 488 + */ 489 + for (i = 0; i < wh; i++) 490 + if (sc->size[dsf_canonify(sc->dsf, i)] != k) 491 + break; 492 + if (i == wh) 493 + return 2; 494 + if (done_something_overall) 495 + return 1; 496 + return 0; 497 + } 498 + 499 + static unsigned char *generate(int w, int h, int k, random_state *rs) 500 + { 501 + int wh = w*h; 502 + int n = wh/k; 503 + struct solver_scratch *sc; 504 + unsigned char *grid; 505 + unsigned char *shuffled; 506 + int i, j, m, retries; 507 + int *permutation; 508 + bool *gen_lock; 509 + 510 + sc = solver_scratch_new(w, h, k); 511 + grid = snewn(wh, unsigned char); 512 + shuffled = snewn(k, unsigned char); 513 + permutation = snewn(wh, int); 514 + gen_lock = snewn(wh, bool); 515 + 516 + do { 517 + DSF *dsf = divvy_rectangle(w, h, k, rs); 518 + 519 + /* 520 + * Go through the dsf and find the indices of all the 521 + * squares involved in each omino, in a manner conducive 522 + * to per-omino indexing. We set permutation[i*k+j] to be 523 + * the index of the jth square (ordered arbitrarily) in 524 + * omino i. 525 + */ 526 + for (i = j = 0; i < wh; i++) 527 + if (dsf_canonify(dsf, i) == i) { 528 + sc->tmp[i] = j; 529 + /* 530 + * During this loop and the following one, we use 531 + * the last element of each row of permutation[] 532 + * as a counter of the number of indices so far 533 + * placed in it. When we place the final index of 534 + * an omino, that counter is overwritten, but that 535 + * doesn't matter because we'll never use it 536 + * again. Of course this depends critically on 537 + * divvy_rectangle() having returned correct 538 + * results, or else chaos would ensue. 539 + */ 540 + permutation[j*k+k-1] = 0; 541 + j++; 542 + } 543 + for (i = 0; i < wh; i++) { 544 + j = sc->tmp[dsf_canonify(dsf, i)]; 545 + m = permutation[j*k+k-1]++; 546 + permutation[j*k+m] = i; 547 + } 548 + 549 + /* 550 + * Track which squares' letters we have already depended 551 + * on for deductions. This is gradually updated by 552 + * solver_attempt(). 553 + */ 554 + memset(gen_lock, 0, wh * sizeof(bool)); 555 + 556 + /* 557 + * Now repeatedly fill the grid with letters, and attempt 558 + * to solve it. If the solver makes progress but does not 559 + * fail completely, then gen_lock will have been updated 560 + * and we try again. On a complete failure, though, we 561 + * have no option but to give up and abandon this set of 562 + * ominoes. 563 + */ 564 + solver_init(sc); 565 + retries = k*k; 566 + while (1) { 567 + /* 568 + * Fill the grid with letters. We can safely use 569 + * sc->tmp to hold the set of letters required at each 570 + * stage, since it's at least size k and is currently 571 + * unused. 572 + */ 573 + for (i = 0; i < n; i++) { 574 + /* 575 + * First, determine the set of letters already 576 + * placed in this omino by gen_lock. 577 + */ 578 + for (j = 0; j < k; j++) 579 + sc->tmp[j] = j; 580 + for (j = 0; j < k; j++) { 581 + int index = permutation[i*k+j]; 582 + int letter = grid[index]; 583 + if (gen_lock[index]) 584 + sc->tmp[letter] = -1; 585 + } 586 + /* 587 + * Now collect together all the remaining letters 588 + * and randomly shuffle them. 589 + */ 590 + for (j = m = 0; j < k; j++) 591 + if (sc->tmp[j] >= 0) 592 + sc->tmp[m++] = sc->tmp[j]; 593 + shuffle(sc->tmp, m, sizeof(*sc->tmp), rs); 594 + /* 595 + * Finally, write the shuffled letters into the 596 + * grid. 597 + */ 598 + for (j = 0; j < k; j++) { 599 + int index = permutation[i*k+j]; 600 + if (!gen_lock[index]) 601 + grid[index] = sc->tmp[--m]; 602 + } 603 + assert(m == 0); 604 + } 605 + 606 + /* 607 + * Now we have a candidate grid. Attempt to progress 608 + * the solution. 609 + */ 610 + m = solver_attempt(sc, grid, gen_lock); 611 + if (m == 2 || /* success */ 612 + (m == 0 && retries-- <= 0)) /* failure */ 613 + break; 614 + if (m == 1) 615 + retries = k*k; /* reset this counter, and continue */ 616 + } 617 + 618 + dsf_free(dsf); 619 + } while (m == 0); 620 + 621 + sfree(gen_lock); 622 + sfree(permutation); 623 + sfree(shuffled); 624 + solver_scratch_free(sc); 625 + 626 + return grid; 627 + } 628 + 629 + /* ---------------------------------------------------------------------- 630 + * End of solver/generator code. 631 + */ 632 + 633 + static char *new_game_desc(const game_params *params, random_state *rs, 634 + char **aux, bool interactive) 635 + { 636 + int w = params->w, h = params->h, wh = w*h, k = params->k; 637 + unsigned char *grid; 638 + char *desc; 639 + int i; 640 + 641 + grid = generate(w, h, k, rs); 642 + 643 + desc = snewn(wh+1, char); 644 + for (i = 0; i < wh; i++) 645 + desc[i] = 'A' + grid[i]; 646 + desc[wh] = '\0'; 647 + 648 + sfree(grid); 649 + 650 + return desc; 651 + } 652 + 653 + static const char *validate_desc(const game_params *params, const char *desc) 654 + { 655 + return NULL; 656 + } 657 + 658 + static game_state *new_game(midend *me, const game_params *params, 659 + const char *desc) 660 + { 661 + game_state *state = snew(game_state); 662 + 663 + state->FIXME = 0; 664 + 665 + return state; 666 + } 667 + 668 + static game_state *dup_game(const game_state *state) 669 + { 670 + game_state *ret = snew(game_state); 671 + 672 + ret->FIXME = state->FIXME; 673 + 674 + return ret; 675 + } 676 + 677 + static void free_game(game_state *state) 678 + { 679 + sfree(state); 680 + } 681 + 682 + static char *solve_game(const game_state *state, const game_state *currstate, 683 + const char *aux, const char **error) 684 + { 685 + return NULL; 686 + } 687 + 688 + static bool game_can_format_as_text_now(const game_params *params) 689 + { 690 + return true; 691 + } 692 + 693 + static char *game_text_format(const game_state *state) 694 + { 695 + return NULL; 696 + } 697 + 698 + static game_ui *new_ui(const game_state *state) 699 + { 700 + return NULL; 701 + } 702 + 703 + static void free_ui(game_ui *ui) 704 + { 705 + } 706 + 707 + static void game_changed_state(game_ui *ui, const game_state *oldstate, 708 + const game_state *newstate) 709 + { 710 + } 711 + 712 + struct game_drawstate { 713 + int tilesize; 714 + int FIXME; 715 + }; 716 + 717 + static char *interpret_move(const game_state *state, game_ui *ui, 718 + const game_drawstate *ds, 719 + int x, int y, int button) 720 + { 721 + return NULL; 722 + } 723 + 724 + static game_state *execute_move(const game_state *state, const char *move) 725 + { 726 + return NULL; 727 + } 728 + 729 + /* ---------------------------------------------------------------------- 730 + * Drawing routines. 731 + */ 732 + 733 + static void game_compute_size(const game_params *params, int tilesize, 734 + const game_ui *ui, int *x, int *y) 735 + { 736 + *x = *y = 10 * tilesize; /* FIXME */ 737 + } 738 + 739 + static void game_set_size(drawing *dr, game_drawstate *ds, 740 + const game_params *params, int tilesize) 741 + { 742 + ds->tilesize = tilesize; 743 + } 744 + 745 + static float *game_colours(frontend *fe, int *ncolours) 746 + { 747 + float *ret = snewn(3 * NCOLOURS, float); 748 + 749 + frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 750 + 751 + *ncolours = NCOLOURS; 752 + return ret; 753 + } 754 + 755 + static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 756 + { 757 + struct game_drawstate *ds = snew(struct game_drawstate); 758 + 759 + ds->tilesize = 0; 760 + ds->FIXME = 0; 761 + 762 + return ds; 763 + } 764 + 765 + static void game_free_drawstate(drawing *dr, game_drawstate *ds) 766 + { 767 + sfree(ds); 768 + } 769 + 770 + static void game_redraw(drawing *dr, game_drawstate *ds, 771 + const game_state *oldstate, const game_state *state, 772 + int dir, const game_ui *ui, 773 + float animtime, float flashtime) 774 + { 775 + } 776 + 777 + static float game_anim_length(const game_state *oldstate, 778 + const game_state *newstate, int dir, game_ui *ui) 779 + { 780 + return 0.0F; 781 + } 782 + 783 + static float game_flash_length(const game_state *oldstate, 784 + const game_state *newstate, int dir, game_ui *ui) 785 + { 786 + return 0.0F; 787 + } 788 + 789 + static void game_get_cursor_location(const game_ui *ui, 790 + const game_drawstate *ds, 791 + const game_state *state, 792 + const game_params *params, 793 + int *x, int *y, int *w, int *h) 794 + { 795 + } 796 + 797 + static int game_status(const game_state *state) 798 + { 799 + return 0; 800 + } 801 + 802 + static bool game_timing_state(const game_state *state, game_ui *ui) 803 + { 804 + return true; 805 + } 806 + 807 + static void game_print_size(const game_params *params, const game_ui *ui, 808 + float *x, float *y) 809 + { 810 + } 811 + 812 + static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 813 + int tilesize) 814 + { 815 + } 816 + 817 + #ifdef COMBINED 818 + #define thegame separate 819 + #endif 820 + 821 + const struct game thegame = { 822 + "Separate", NULL, NULL, 823 + default_params, 824 + game_fetch_preset, NULL, 825 + decode_params, 826 + encode_params, 827 + free_params, 828 + dup_params, 829 + false, game_configure, custom_params, 830 + validate_params, 831 + new_game_desc, 832 + validate_desc, 833 + new_game, 834 + dup_game, 835 + free_game, 836 + false, solve_game, 837 + false, game_can_format_as_text_now, game_text_format, 838 + NULL, NULL, /* get_prefs, set_prefs */ 839 + new_ui, 840 + free_ui, 841 + NULL, /* encode_ui */ 842 + NULL, /* decode_ui */ 843 + NULL, /* game_request_keys */ 844 + game_changed_state, 845 + NULL, /* current_key_label */ 846 + interpret_move, 847 + execute_move, 848 + 20 /* FIXME */, game_compute_size, game_set_size, 849 + game_colours, 850 + game_new_drawstate, 851 + game_free_drawstate, 852 + game_redraw, 853 + game_anim_length, 854 + game_flash_length, 855 + game_get_cursor_location, 856 + game_status, 857 + false, false, game_print_size, game_print, 858 + false, /* wants_statusbar */ 859 + false, game_timing_state, 860 + 0, /* flags */ 861 + };
+2444
apps/plugins/puzzles/src/unfinished/slide.c
··· 1 + /* 2 + * slide.c: Implementation of the block-sliding puzzle `Klotski'. 3 + */ 4 + 5 + /* 6 + * TODO: 7 + * 8 + * - Improve the generator. 9 + * * actually, we seem to be mostly sensible already now. I 10 + * want more choice over the type of main block and location 11 + * of the exit/target, and I think I probably ought to give 12 + * up on compactness and just bite the bullet and have the 13 + * target area right outside the main wall, but mostly I 14 + * think it's OK. 15 + * * the move limit tends to make the game _slower_ to 16 + * generate, which is odd. Perhaps investigate why. 17 + * 18 + * - Improve the graphics. 19 + * * All the colours are a bit wishy-washy. _Some_ dark 20 + * colours would surely not be excessive? Probably darken 21 + * the tiles, the walls and the main block, and leave the 22 + * target marker pale. 23 + * * The cattle grid effect is still disgusting. Think of 24 + * something completely different. 25 + * * The highlight for next-piece-to-move in the solver is 26 + * excessive, and the shadow blends in too well with the 27 + * piece lowlights. Adjust both. 28 + */ 29 + 30 + #include <stdio.h> 31 + #include <stdlib.h> 32 + #include <string.h> 33 + #include <assert.h> 34 + #include <ctype.h> 35 + #ifdef NO_TGMATH_H 36 + # include <math.h> 37 + #else 38 + # include <tgmath.h> 39 + #endif 40 + 41 + #include "puzzles.h" 42 + #include "tree234.h" 43 + 44 + /* 45 + * The implementation of this game revolves around the insight 46 + * which makes an exhaustive-search solver feasible: although 47 + * there are many blocks which can be rearranged in many ways, any 48 + * two blocks of the same shape are _indistinguishable_ and hence 49 + * the number of _distinct_ board layouts is generally much 50 + * smaller. So we adopt a representation for board layouts which 51 + * is inherently canonical, i.e. there are no two distinct 52 + * representations which encode indistinguishable layouts. 53 + * 54 + * The way we do this is to encode each square of the board, in 55 + * the normal left-to-right top-to-bottom order, as being one of 56 + * the following things: 57 + * - the first square (in the given order) of a block (`anchor') 58 + * - special case of the above: the anchor for the _main_ block 59 + * (i.e. the one which the aim of the game is to get to the 60 + * target position) 61 + * - a subsequent square of a block whose previous square was N 62 + * squares ago 63 + * - an impassable wall 64 + * 65 + * (We also separately store data about which board positions are 66 + * forcefields only passable by the main block. We can't encode 67 + * that in the main board data, because then the main block would 68 + * destroy forcefields as it went over them.) 69 + * 70 + * Hence, for example, a 2x2 square block would be encoded as 71 + * ANCHOR, followed by DIST(1), and w-2 squares later on there 72 + * would be DIST(w-1) followed by DIST(1). So if you start at the 73 + * last of those squares, the DIST numbers give you a linked list 74 + * pointing back through all the other squares in the same block. 75 + * 76 + * So the solver simply does a bfs over all reachable positions, 77 + * encoding them in this format and storing them in a tree234 to 78 + * ensure it doesn't ever revisit an already-analysed position. 79 + */ 80 + 81 + enum { 82 + /* 83 + * The colours are arranged here so that every base colour is 84 + * directly followed by its highlight colour and then its 85 + * lowlight colour. Do not break this, or draw_tile() will get 86 + * confused. 87 + */ 88 + COL_BACKGROUND, 89 + COL_HIGHLIGHT, 90 + COL_LOWLIGHT, 91 + COL_DRAGGING, 92 + COL_DRAGGING_HIGHLIGHT, 93 + COL_DRAGGING_LOWLIGHT, 94 + COL_MAIN, 95 + COL_MAIN_HIGHLIGHT, 96 + COL_MAIN_LOWLIGHT, 97 + COL_MAIN_DRAGGING, 98 + COL_MAIN_DRAGGING_HIGHLIGHT, 99 + COL_MAIN_DRAGGING_LOWLIGHT, 100 + COL_TARGET, 101 + COL_TARGET_HIGHLIGHT, 102 + COL_TARGET_LOWLIGHT, 103 + NCOLOURS 104 + }; 105 + 106 + /* 107 + * Board layout is a simple array of bytes. Each byte holds: 108 + */ 109 + #define ANCHOR 255 /* top-left-most square of some piece */ 110 + #define MAINANCHOR 254 /* anchor of _main_ piece */ 111 + #define EMPTY 253 /* empty square */ 112 + #define WALL 252 /* immovable wall */ 113 + #define MAXDIST 251 114 + /* all other values indicate distance back to previous square of same block */ 115 + #define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 ) 116 + #define DIST(x) (x) 117 + #define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR ) 118 + #define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) ) 119 + 120 + /* 121 + * MAXDIST is the largest DIST value we can encode. This must 122 + * therefore also be the maximum puzzle width in theory (although 123 + * solver running time will dictate a much smaller limit in 124 + * practice). 125 + */ 126 + #define MAXWID MAXDIST 127 + 128 + struct game_params { 129 + int w, h; 130 + int maxmoves; 131 + }; 132 + 133 + struct game_immutable_state { 134 + int refcount; 135 + bool *forcefield; 136 + }; 137 + 138 + struct game_solution { 139 + int nmoves; 140 + int *moves; /* just like from solve_board() */ 141 + int refcount; 142 + }; 143 + 144 + struct game_state { 145 + int w, h; 146 + unsigned char *board; 147 + int tx, ty; /* target coords for MAINANCHOR */ 148 + int minmoves; /* for display only */ 149 + int lastmoved, lastmoved_pos; /* for move counting */ 150 + int movecount; 151 + int completed; 152 + bool cheated; 153 + struct game_immutable_state *imm; 154 + struct game_solution *soln; 155 + int soln_index; 156 + }; 157 + 158 + static game_params *default_params(void) 159 + { 160 + game_params *ret = snew(game_params); 161 + 162 + ret->w = 7; 163 + ret->h = 6; 164 + ret->maxmoves = 40; 165 + 166 + return ret; 167 + } 168 + 169 + static const struct game_params slide_presets[] = { 170 + {7, 6, 25}, 171 + {7, 6, -1}, 172 + {8, 6, -1}, 173 + }; 174 + 175 + static bool game_fetch_preset(int i, char **name, game_params **params) 176 + { 177 + game_params *ret; 178 + char str[80]; 179 + 180 + if (i < 0 || i >= lenof(slide_presets)) 181 + return false; 182 + 183 + ret = snew(game_params); 184 + *ret = slide_presets[i]; 185 + 186 + sprintf(str, "%dx%d", ret->w, ret->h); 187 + if (ret->maxmoves >= 0) 188 + sprintf(str + strlen(str), ", max %d moves", ret->maxmoves); 189 + else 190 + sprintf(str + strlen(str), ", no move limit"); 191 + 192 + *name = dupstr(str); 193 + *params = ret; 194 + return true; 195 + } 196 + 197 + static void free_params(game_params *params) 198 + { 199 + sfree(params); 200 + } 201 + 202 + static game_params *dup_params(const game_params *params) 203 + { 204 + game_params *ret = snew(game_params); 205 + *ret = *params; /* structure copy */ 206 + return ret; 207 + } 208 + 209 + static void decode_params(game_params *params, char const *string) 210 + { 211 + params->w = params->h = atoi(string); 212 + while (*string && isdigit((unsigned char)*string)) string++; 213 + if (*string == 'x') { 214 + string++; 215 + params->h = atoi(string); 216 + while (*string && isdigit((unsigned char)*string)) string++; 217 + } 218 + if (*string == 'm') { 219 + string++; 220 + params->maxmoves = atoi(string); 221 + while (*string && isdigit((unsigned char)*string)) string++; 222 + } else if (*string == 'u') { 223 + string++; 224 + params->maxmoves = -1; 225 + } 226 + } 227 + 228 + static char *encode_params(const game_params *params, bool full) 229 + { 230 + char data[256]; 231 + 232 + sprintf(data, "%dx%d", params->w, params->h); 233 + if (params->maxmoves >= 0) 234 + sprintf(data + strlen(data), "m%d", params->maxmoves); 235 + else 236 + sprintf(data + strlen(data), "u"); 237 + 238 + return dupstr(data); 239 + } 240 + 241 + static config_item *game_configure(const game_params *params) 242 + { 243 + config_item *ret; 244 + char buf[80]; 245 + 246 + ret = snewn(4, config_item); 247 + 248 + ret[0].name = "Width"; 249 + ret[0].type = C_STRING; 250 + sprintf(buf, "%d", params->w); 251 + ret[0].u.string.sval = dupstr(buf); 252 + 253 + ret[1].name = "Height"; 254 + ret[1].type = C_STRING; 255 + sprintf(buf, "%d", params->h); 256 + ret[1].u.string.sval = dupstr(buf); 257 + 258 + ret[2].name = "Solution length limit"; 259 + ret[2].type = C_STRING; 260 + sprintf(buf, "%d", params->maxmoves); 261 + ret[2].u.string.sval = dupstr(buf); 262 + 263 + ret[3].name = NULL; 264 + ret[3].type = C_END; 265 + 266 + return ret; 267 + } 268 + 269 + static game_params *custom_params(const config_item *cfg) 270 + { 271 + game_params *ret = snew(game_params); 272 + 273 + ret->w = atoi(cfg[0].u.string.sval); 274 + ret->h = atoi(cfg[1].u.string.sval); 275 + ret->maxmoves = atoi(cfg[2].u.string.sval); 276 + 277 + return ret; 278 + } 279 + 280 + static const char *validate_params(const game_params *params, bool full) 281 + { 282 + if (params->w > MAXWID) 283 + return "Width must be at most " STR(MAXWID); 284 + 285 + if (params->w < 5) 286 + return "Width must be at least 5"; 287 + if (params->h < 4) 288 + return "Height must be at least 4"; 289 + 290 + return NULL; 291 + } 292 + 293 + static char *board_text_format(int w, int h, unsigned char *data, 294 + bool *forcefield) 295 + { 296 + int wh = w*h; 297 + DSF *dsf = dsf_new(wh); 298 + int i, x, y; 299 + int retpos, retlen = (w*2+2)*(h*2+1)+1; 300 + char *ret = snewn(retlen, char); 301 + 302 + for (i = 0; i < wh; i++) 303 + if (ISDIST(data[i])) 304 + dsf_merge(dsf, i - data[i], i); 305 + retpos = 0; 306 + for (y = 0; y < 2*h+1; y++) { 307 + for (x = 0; x < 2*w+1; x++) { 308 + int v; 309 + int i = (y/2)*w+(x/2); 310 + 311 + #define dtype(i) (ISBLOCK(data[i]) ? \ 312 + dsf_canonify(dsf, i) : data[i]) 313 + #define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \ 314 + data[t] == MAINANCHOR ? '*' : '%') 315 + 316 + if (y % 2 && x % 2) { 317 + int j = dtype(i); 318 + v = dchar(j); 319 + } else if (y % 2 && !(x % 2)) { 320 + int j1 = (x > 0 ? dtype(i-1) : -1); 321 + int j2 = (x < 2*w ? dtype(i) : -1); 322 + if (j1 != j2) 323 + v = '|'; 324 + else 325 + v = dchar(j1); 326 + } else if (!(y % 2) && (x % 2)) { 327 + int j1 = (y > 0 ? dtype(i-w) : -1); 328 + int j2 = (y < 2*h ? dtype(i) : -1); 329 + if (j1 != j2) 330 + v = '-'; 331 + else 332 + v = dchar(j1); 333 + } else { 334 + int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1); 335 + int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1); 336 + int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1); 337 + int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1); 338 + if (j1 == j2 && j2 == j3 && j3 == j4) 339 + v = dchar(j1); 340 + else if (j1 == j2 && j3 == j4) 341 + v = '|'; 342 + else if (j1 == j3 && j2 == j4) 343 + v = '-'; 344 + else 345 + v = '+'; 346 + } 347 + 348 + assert(retpos < retlen); 349 + ret[retpos++] = v; 350 + } 351 + assert(retpos < retlen); 352 + ret[retpos++] = '\n'; 353 + } 354 + assert(retpos < retlen); 355 + ret[retpos++] = '\0'; 356 + assert(retpos == retlen); 357 + 358 + return ret; 359 + } 360 + 361 + /* ---------------------------------------------------------------------- 362 + * Solver. 363 + */ 364 + 365 + /* 366 + * During solver execution, the set of visited board positions is 367 + * stored as a tree234 of the following structures. `w', `h' and 368 + * `data' are obvious in meaning; `dist' represents the minimum 369 + * distance to reach this position from the starting point. 370 + * 371 + * `prev' links each board to the board position from which it was 372 + * most efficiently derived. 373 + */ 374 + struct board { 375 + int w, h; 376 + int dist; 377 + struct board *prev; 378 + unsigned char *data; 379 + }; 380 + 381 + static int boardcmp(void *av, void *bv) 382 + { 383 + struct board *a = (struct board *)av; 384 + struct board *b = (struct board *)bv; 385 + return memcmp(a->data, b->data, a->w * a->h); 386 + } 387 + 388 + static struct board *newboard(int w, int h, unsigned char *data) 389 + { 390 + struct board *b = malloc(sizeof(struct board) + w*h); 391 + b->data = (unsigned char *)b + sizeof(struct board); 392 + memcpy(b->data, data, w*h); 393 + b->w = w; 394 + b->h = h; 395 + b->dist = -1; 396 + b->prev = NULL; 397 + return b; 398 + } 399 + 400 + /* 401 + * The actual solver. Given a board, attempt to find the minimum 402 + * length of move sequence which moves MAINANCHOR to (tx,ty), or 403 + * -1 if no solution exists. Returns that minimum length. 404 + * 405 + * Also, if `moveout' is provided, writes out the moves in the 406 + * form of a sequence of pairs of integers indicating the source 407 + * and destination points of the anchor of the moved piece in each 408 + * move. Exactly twice as many integers are written as the number 409 + * returned from solve_board(), and `moveout' receives an int * 410 + * which is a pointer to a dynamically allocated array. 411 + */ 412 + static int solve_board(int w, int h, unsigned char *board, 413 + bool *forcefield, int tx, int ty, 414 + int movelimit, int **moveout) 415 + { 416 + int wh = w*h; 417 + struct board *b, *b2, *b3; 418 + int *next, *which; 419 + bool *anchors, *movereached; 420 + int *movequeue, mqhead, mqtail; 421 + tree234 *sorted, *queue; 422 + int i, j, dir; 423 + int qlen, lastdist; 424 + int ret; 425 + 426 + #ifdef SOLVER_DIAGNOSTICS 427 + { 428 + char *t = board_text_format(w, h, board); 429 + for (i = 0; i < h; i++) { 430 + for (j = 0; j < w; j++) { 431 + int c = board[i*w+j]; 432 + if (ISDIST(c)) 433 + printf("D%-3d", c); 434 + else if (c == MAINANCHOR) 435 + printf("M "); 436 + else if (c == ANCHOR) 437 + printf("A "); 438 + else if (c == WALL) 439 + printf("W "); 440 + else if (c == EMPTY) 441 + printf("E "); 442 + } 443 + printf("\n"); 444 + } 445 + 446 + printf("Starting solver for:\n%s\n", t); 447 + sfree(t); 448 + } 449 + #endif 450 + 451 + sorted = newtree234(boardcmp); 452 + queue = newtree234(NULL); 453 + 454 + b = newboard(w, h, board); 455 + b->dist = 0; 456 + add234(sorted, b); 457 + addpos234(queue, b, 0); 458 + qlen = 1; 459 + 460 + next = snewn(wh, int); 461 + anchors = snewn(wh, bool); 462 + which = snewn(wh, int); 463 + movereached = snewn(wh, bool); 464 + movequeue = snewn(wh, int); 465 + lastdist = -1; 466 + 467 + while ((b = delpos234(queue, 0)) != NULL) { 468 + qlen--; 469 + if (movelimit >= 0 && b->dist >= movelimit) { 470 + /* 471 + * The problem is not soluble in under `movelimit' 472 + * moves, so we can quit right now. 473 + */ 474 + b2 = NULL; 475 + goto done; 476 + } 477 + if (b->dist != lastdist) { 478 + #ifdef SOLVER_DIAGNOSTICS 479 + printf("dist %d (%d)\n", b->dist, count234(sorted)); 480 + #endif 481 + lastdist = b->dist; 482 + } 483 + /* 484 + * Find all the anchors and form a linked list of the 485 + * squares within each block. 486 + */ 487 + for (i = 0; i < wh; i++) { 488 + next[i] = -1; 489 + anchors[i] = false; 490 + which[i] = -1; 491 + if (ISANCHOR(b->data[i])) { 492 + anchors[i] = true; 493 + which[i] = i; 494 + } else if (ISDIST(b->data[i])) { 495 + j = i - b->data[i]; 496 + next[j] = i; 497 + which[i] = which[j]; 498 + } 499 + } 500 + 501 + /* 502 + * For each anchor, do an array-based BFS to find all the 503 + * places we can slide it to. 504 + */ 505 + for (i = 0; i < wh; i++) { 506 + if (!anchors[i]) 507 + continue; 508 + 509 + mqhead = mqtail = 0; 510 + for (j = 0; j < wh; j++) 511 + movereached[j] = false; 512 + movequeue[mqtail++] = i; 513 + while (mqhead < mqtail) { 514 + int pos = movequeue[mqhead++]; 515 + 516 + /* 517 + * Try to move in each direction from here. 518 + */ 519 + for (dir = 0; dir < 4; dir++) { 520 + int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); 521 + int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); 522 + int offset = dy*w + dx; 523 + int newpos = pos + offset; 524 + int d = newpos - i; 525 + 526 + /* 527 + * For each square involved in this block, 528 + * check to see if the square d spaces away 529 + * from it is either empty or part of the same 530 + * block. 531 + */ 532 + for (j = i; j >= 0; j = next[j]) { 533 + int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx; 534 + if (jy >= 0 && jy < h && jx >= 0 && jx < w && 535 + ((b->data[j+d] == EMPTY || which[j+d] == i) && 536 + (b->data[i] == MAINANCHOR || !forcefield[j+d]))) 537 + /* ok */; 538 + else 539 + break; 540 + } 541 + if (j >= 0) 542 + continue; /* this direction wasn't feasible */ 543 + 544 + /* 545 + * If we've already tried moving this piece 546 + * here, leave it. 547 + */ 548 + if (movereached[newpos]) 549 + continue; 550 + movereached[newpos] = true; 551 + movequeue[mqtail++] = newpos; 552 + 553 + /* 554 + * We have a viable move. Make it. 555 + */ 556 + b2 = newboard(w, h, b->data); 557 + for (j = i; j >= 0; j = next[j]) 558 + b2->data[j] = EMPTY; 559 + for (j = i; j >= 0; j = next[j]) 560 + b2->data[j+d] = b->data[j]; 561 + 562 + b3 = add234(sorted, b2); 563 + if (b3 != b2) { 564 + sfree(b2); /* we already got one */ 565 + } else { 566 + b2->dist = b->dist + 1; 567 + b2->prev = b; 568 + addpos234(queue, b2, qlen++); 569 + if (b2->data[ty*w+tx] == MAINANCHOR) 570 + goto done; /* search completed! */ 571 + } 572 + } 573 + } 574 + } 575 + } 576 + b2 = NULL; 577 + 578 + done: 579 + 580 + if (b2) { 581 + ret = b2->dist; 582 + if (moveout) { 583 + /* 584 + * Now b2 represents the solved position. Backtrack to 585 + * output the solution. 586 + */ 587 + *moveout = snewn(ret * 2, int); 588 + j = ret * 2; 589 + 590 + while (b2->prev) { 591 + int from = -1, to = -1; 592 + 593 + b = b2->prev; 594 + 595 + /* 596 + * Scan b and b2 to find out which piece has 597 + * moved. 598 + */ 599 + for (i = 0; i < wh; i++) { 600 + if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) { 601 + assert(from == -1); 602 + from = i; 603 + } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){ 604 + assert(to == -1); 605 + to = i; 606 + } 607 + } 608 + 609 + assert(from >= 0 && to >= 0); 610 + assert(j >= 2); 611 + (*moveout)[--j] = to; 612 + (*moveout)[--j] = from; 613 + 614 + b2 = b; 615 + } 616 + assert(j == 0); 617 + } 618 + } else { 619 + ret = -1; /* no solution */ 620 + if (moveout) 621 + *moveout = NULL; 622 + } 623 + 624 + freetree234(queue); 625 + 626 + while ((b = delpos234(sorted, 0)) != NULL) 627 + sfree(b); 628 + freetree234(sorted); 629 + 630 + sfree(next); 631 + sfree(anchors); 632 + sfree(movereached); 633 + sfree(movequeue); 634 + sfree(which); 635 + 636 + return ret; 637 + } 638 + 639 + /* ---------------------------------------------------------------------- 640 + * Random board generation. 641 + */ 642 + 643 + static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves, 644 + random_state *rs, unsigned char **rboard, 645 + bool **rforcefield, int movelimit) 646 + { 647 + int wh = w*h; 648 + unsigned char *board, *board2; 649 + bool *forcefield; 650 + bool *tried_merge; 651 + DSF *dsf; 652 + int *list, nlist, pos; 653 + int tx, ty; 654 + int i, j; 655 + int moves = 0; /* placate optimiser */ 656 + 657 + /* 658 + * Set up a board and fill it with singletons, except for a 659 + * border of walls. 660 + */ 661 + board = snewn(wh, unsigned char); 662 + forcefield = snewn(wh, bool); 663 + board2 = snewn(wh, unsigned char); 664 + memset(board, ANCHOR, wh); 665 + memset(forcefield, 0, wh * sizeof(bool)); 666 + for (i = 0; i < w; i++) 667 + board[i] = board[i+w*(h-1)] = WALL; 668 + for (i = 0; i < h; i++) 669 + board[i*w] = board[i*w+(w-1)] = WALL; 670 + 671 + tried_merge = snewn(wh * wh, bool); 672 + memset(tried_merge, 0, wh*wh * sizeof(bool)); 673 + dsf = dsf_new(wh); 674 + 675 + /* 676 + * Invent a main piece at one extreme. (FIXME: vary the 677 + * extreme, and the piece.) 678 + */ 679 + board[w+1] = MAINANCHOR; 680 + board[w+2] = DIST(1); 681 + board[w*2+1] = DIST(w-1); 682 + board[w*2+2] = DIST(1); 683 + 684 + /* 685 + * Invent a target position. (FIXME: vary this too.) 686 + */ 687 + tx = w-2; 688 + ty = h-3; 689 + forcefield[ty*w+tx+1] = true; 690 + forcefield[(ty+1)*w+tx+1] = true; 691 + board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY; 692 + 693 + /* 694 + * Gradually remove singletons until the game becomes soluble. 695 + */ 696 + for (j = w; j-- > 0 ;) 697 + for (i = h; i-- > 0 ;) 698 + if (board[i*w+j] == ANCHOR) { 699 + /* 700 + * See if the board is already soluble. 701 + */ 702 + if ((moves = solve_board(w, h, board, forcefield, 703 + tx, ty, movelimit, NULL)) >= 0) 704 + goto soluble; 705 + 706 + /* 707 + * Otherwise, remove this piece. 708 + */ 709 + board[i*w+j] = EMPTY; 710 + } 711 + assert(!"We shouldn't get here"); 712 + soluble: 713 + 714 + /* 715 + * Make a list of all the inter-block edges on the board. 716 + */ 717 + list = snewn(wh*2, int); 718 + nlist = 0; 719 + for (i = 0; i+1 < w; i++) 720 + for (j = 0; j < h; j++) 721 + list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */ 722 + for (j = 0; j+1 < h; j++) 723 + for (i = 0; i < w; i++) 724 + list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */ 725 + 726 + /* 727 + * Now go through that list in random order, trying to merge 728 + * the blocks on each side of each edge. 729 + */ 730 + shuffle(list, nlist, sizeof(*list), rs); 731 + while (nlist > 0) { 732 + int x1, y1, p1, c1; 733 + int x2, y2, p2, c2; 734 + 735 + pos = list[--nlist]; 736 + y1 = y2 = pos / (w*2); 737 + x1 = x2 = (pos / 2) % w; 738 + if (pos % 2) 739 + y2++; 740 + else 741 + x2++; 742 + p1 = y1*w+x1; 743 + p2 = y2*w+x2; 744 + 745 + /* 746 + * Immediately abandon the attempt if we've already tried 747 + * to merge the same pair of blocks along a different 748 + * edge. 749 + */ 750 + c1 = dsf_canonify(dsf, p1); 751 + c2 = dsf_canonify(dsf, p2); 752 + if (tried_merge[c1 * wh + c2]) 753 + continue; 754 + 755 + /* 756 + * In order to be mergeable, these two squares must each 757 + * either be, or belong to, a non-main anchor, and their 758 + * anchors must also be distinct. 759 + */ 760 + if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2])) 761 + continue; 762 + while (ISDIST(board[p1])) 763 + p1 -= board[p1]; 764 + while (ISDIST(board[p2])) 765 + p2 -= board[p2]; 766 + if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2) 767 + continue; 768 + 769 + /* 770 + * We can merge these blocks. Try it, and see if the 771 + * puzzle remains soluble. 772 + */ 773 + memcpy(board2, board, wh); 774 + j = -1; 775 + while (p1 < wh || p2 < wh) { 776 + /* 777 + * p1 and p2 are the squares at the head of each block 778 + * list. Pick the smaller one and put it on the output 779 + * block list. 780 + */ 781 + i = min(p1, p2); 782 + if (j < 0) { 783 + board[i] = ANCHOR; 784 + } else { 785 + assert(i - j <= MAXDIST); 786 + board[i] = DIST(i - j); 787 + } 788 + j = i; 789 + 790 + /* 791 + * Now advance whichever list that came from. 792 + */ 793 + if (i == p1) { 794 + do { 795 + p1++; 796 + } while (p1 < wh && board[p1] != DIST(p1-i)); 797 + } else { 798 + do { 799 + p2++; 800 + } while (p2 < wh && board[p2] != DIST(p2-i)); 801 + } 802 + } 803 + j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL); 804 + if (j < 0) { 805 + /* 806 + * Didn't work. Revert the merge. 807 + */ 808 + memcpy(board, board2, wh); 809 + tried_merge[c1 * wh + c2] = true; 810 + tried_merge[c2 * wh + c1] = true; 811 + } else { 812 + int c; 813 + 814 + moves = j; 815 + 816 + dsf_merge(dsf, c1, c2); 817 + c = dsf_canonify(dsf, c1); 818 + for (i = 0; i < wh; i++) 819 + tried_merge[c*wh+i] = (tried_merge[c1*wh+i] || 820 + tried_merge[c2*wh+i]); 821 + for (i = 0; i < wh; i++) 822 + tried_merge[i*wh+c] = (tried_merge[i*wh+c1] || 823 + tried_merge[i*wh+c2]); 824 + } 825 + } 826 + 827 + dsf_free(dsf); 828 + sfree(list); 829 + sfree(tried_merge); 830 + sfree(board2); 831 + 832 + *rtx = tx; 833 + *rty = ty; 834 + *rboard = board; 835 + *rforcefield = forcefield; 836 + *minmoves = moves; 837 + } 838 + 839 + /* ---------------------------------------------------------------------- 840 + * End of solver/generator code. 841 + */ 842 + 843 + static char *new_game_desc(const game_params *params, random_state *rs, 844 + char **aux, bool interactive) 845 + { 846 + int w = params->w, h = params->h, wh = w*h; 847 + int tx, ty, minmoves; 848 + unsigned char *board; 849 + bool *forcefield; 850 + char *ret, *p; 851 + int i; 852 + 853 + generate_board(params->w, params->h, &tx, &ty, &minmoves, rs, 854 + &board, &forcefield, params->maxmoves); 855 + #ifdef GENERATOR_DIAGNOSTICS 856 + { 857 + char *t = board_text_format(params->w, params->h, board); 858 + printf("%s\n", t); 859 + sfree(t); 860 + } 861 + #endif 862 + 863 + /* 864 + * Encode as a game ID. 865 + */ 866 + ret = snewn(wh * 6 + 40, char); 867 + p = ret; 868 + i = 0; 869 + while (i < wh) { 870 + if (ISDIST(board[i])) { 871 + p += sprintf(p, "d%d", board[i]); 872 + i++; 873 + } else { 874 + int count = 1; 875 + int b = board[i]; 876 + bool f = forcefield[i]; 877 + int c = (b == ANCHOR ? 'a' : 878 + b == MAINANCHOR ? 'm' : 879 + b == EMPTY ? 'e' : 880 + /* b == WALL ? */ 'w'); 881 + if (f) *p++ = 'f'; 882 + *p++ = c; 883 + i++; 884 + while (i < wh && board[i] == b && forcefield[i] == f) 885 + i++, count++; 886 + if (count > 1) 887 + p += sprintf(p, "%d", count); 888 + } 889 + } 890 + p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves); 891 + ret = sresize(ret, p+1 - ret, char); 892 + 893 + sfree(board); 894 + sfree(forcefield); 895 + 896 + return ret; 897 + } 898 + 899 + static const char *validate_desc(const game_params *params, const char *desc) 900 + { 901 + int w = params->w, h = params->h, wh = w*h; 902 + bool *active; 903 + int *link; 904 + int mains = 0; 905 + int i, tx, ty, minmoves; 906 + const char *ret; 907 + 908 + active = snewn(wh, bool); 909 + link = snewn(wh, int); 910 + i = 0; 911 + 912 + while (*desc && *desc != ',') { 913 + if (i >= wh) { 914 + ret = "Too much data in game description"; 915 + goto done; 916 + } 917 + link[i] = -1; 918 + active[i] = false; 919 + if (*desc == 'f' || *desc == 'F') { 920 + desc++; 921 + if (!*desc) { 922 + ret = "Expected another character after 'f' in game " 923 + "description"; 924 + goto done; 925 + } 926 + } 927 + 928 + if (*desc == 'd' || *desc == 'D') { 929 + int dist; 930 + 931 + desc++; 932 + if (!isdigit((unsigned char)*desc)) { 933 + ret = "Expected a number after 'd' in game description"; 934 + goto done; 935 + } 936 + dist = atoi(desc); 937 + while (*desc && isdigit((unsigned char)*desc)) desc++; 938 + 939 + if (dist <= 0 || dist > i) { 940 + ret = "Out-of-range number after 'd' in game description"; 941 + goto done; 942 + } 943 + 944 + if (!active[i - dist]) { 945 + ret = "Invalid back-reference in game description"; 946 + goto done; 947 + } 948 + 949 + link[i] = i - dist; 950 + 951 + active[i] = true; 952 + active[link[i]] = false; 953 + i++; 954 + } else { 955 + int c = *desc++; 956 + int count = 1; 957 + 958 + if (!strchr("aAmMeEwW", c)) { 959 + ret = "Invalid character in game description"; 960 + goto done; 961 + } 962 + if (isdigit((unsigned char)*desc)) { 963 + count = atoi(desc); 964 + while (*desc && isdigit((unsigned char)*desc)) desc++; 965 + } 966 + if (i + count > wh) { 967 + ret = "Too much data in game description"; 968 + goto done; 969 + } 970 + while (count-- > 0) { 971 + active[i] = (strchr("aAmM", c) != NULL); 972 + link[i] = -1; 973 + if (strchr("mM", c) != NULL) { 974 + mains++; 975 + } 976 + i++; 977 + } 978 + } 979 + } 980 + if (mains != 1) { 981 + ret = (mains == 0 ? "No main piece specified in game description" : 982 + "More than one main piece specified in game description"); 983 + goto done; 984 + } 985 + if (i < wh) { 986 + ret = "Not enough data in game description"; 987 + goto done; 988 + } 989 + 990 + /* 991 + * Now read the target coordinates. 992 + */ 993 + i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves); 994 + if (i < 2) { 995 + ret = "No target coordinates specified"; 996 + goto done; 997 + /* 998 + * (but minmoves is optional) 999 + */ 1000 + } 1001 + 1002 + ret = NULL; 1003 + 1004 + done: 1005 + sfree(active); 1006 + sfree(link); 1007 + return ret; 1008 + } 1009 + 1010 + static game_state *new_game(midend *me, const game_params *params, 1011 + const char *desc) 1012 + { 1013 + int w = params->w, h = params->h, wh = w*h; 1014 + game_state *state; 1015 + int i; 1016 + 1017 + state = snew(game_state); 1018 + state->w = w; 1019 + state->h = h; 1020 + state->board = snewn(wh, unsigned char); 1021 + state->lastmoved = state->lastmoved_pos = -1; 1022 + state->movecount = 0; 1023 + state->imm = snew(struct game_immutable_state); 1024 + state->imm->refcount = 1; 1025 + state->imm->forcefield = snewn(wh, bool); 1026 + 1027 + i = 0; 1028 + 1029 + while (*desc && *desc != ',') { 1030 + bool f = false; 1031 + 1032 + assert(i < wh); 1033 + 1034 + if (*desc == 'f') { 1035 + f = true; 1036 + desc++; 1037 + assert(*desc); 1038 + } 1039 + 1040 + if (*desc == 'd' || *desc == 'D') { 1041 + int dist; 1042 + 1043 + desc++; 1044 + dist = atoi(desc); 1045 + while (*desc && isdigit((unsigned char)*desc)) desc++; 1046 + 1047 + state->board[i] = DIST(dist); 1048 + state->imm->forcefield[i] = f; 1049 + 1050 + i++; 1051 + } else { 1052 + int c = *desc++; 1053 + int count = 1; 1054 + 1055 + if (isdigit((unsigned char)*desc)) { 1056 + count = atoi(desc); 1057 + while (*desc && isdigit((unsigned char)*desc)) desc++; 1058 + } 1059 + assert(i + count <= wh); 1060 + 1061 + c = (c == 'a' || c == 'A' ? ANCHOR : 1062 + c == 'm' || c == 'M' ? MAINANCHOR : 1063 + c == 'e' || c == 'E' ? EMPTY : 1064 + /* c == 'w' || c == 'W' ? */ WALL); 1065 + 1066 + while (count-- > 0) { 1067 + state->board[i] = c; 1068 + state->imm->forcefield[i] = f; 1069 + i++; 1070 + } 1071 + } 1072 + } 1073 + 1074 + /* 1075 + * Now read the target coordinates. 1076 + */ 1077 + state->tx = state->ty = 0; 1078 + state->minmoves = -1; 1079 + i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves); 1080 + 1081 + if (state->board[state->ty*w+state->tx] == MAINANCHOR) 1082 + state->completed = 0; /* already complete! */ 1083 + else 1084 + state->completed = -1; 1085 + 1086 + state->cheated = false; 1087 + state->soln = NULL; 1088 + state->soln_index = -1; 1089 + 1090 + return state; 1091 + } 1092 + 1093 + static game_state *dup_game(const game_state *state) 1094 + { 1095 + int w = state->w, h = state->h, wh = w*h; 1096 + game_state *ret = snew(game_state); 1097 + 1098 + ret->w = state->w; 1099 + ret->h = state->h; 1100 + ret->board = snewn(wh, unsigned char); 1101 + memcpy(ret->board, state->board, wh); 1102 + ret->tx = state->tx; 1103 + ret->ty = state->ty; 1104 + ret->minmoves = state->minmoves; 1105 + ret->lastmoved = state->lastmoved; 1106 + ret->lastmoved_pos = state->lastmoved_pos; 1107 + ret->movecount = state->movecount; 1108 + ret->completed = state->completed; 1109 + ret->cheated = state->cheated; 1110 + ret->imm = state->imm; 1111 + ret->imm->refcount++; 1112 + ret->soln = state->soln; 1113 + ret->soln_index = state->soln_index; 1114 + if (ret->soln) 1115 + ret->soln->refcount++; 1116 + 1117 + return ret; 1118 + } 1119 + 1120 + static void free_game(game_state *state) 1121 + { 1122 + if (--state->imm->refcount <= 0) { 1123 + sfree(state->imm->forcefield); 1124 + sfree(state->imm); 1125 + } 1126 + if (state->soln && --state->soln->refcount <= 0) { 1127 + sfree(state->soln->moves); 1128 + sfree(state->soln); 1129 + } 1130 + sfree(state->board); 1131 + sfree(state); 1132 + } 1133 + 1134 + static char *solve_game(const game_state *state, const game_state *currstate, 1135 + const char *aux, const char **error) 1136 + { 1137 + int *moves; 1138 + int nmoves; 1139 + int i; 1140 + char *ret, *p, sep; 1141 + 1142 + /* 1143 + * Run the solver and attempt to find the shortest solution 1144 + * from the current position. 1145 + */ 1146 + nmoves = solve_board(state->w, state->h, state->board, 1147 + state->imm->forcefield, state->tx, state->ty, 1148 + -1, &moves); 1149 + 1150 + if (nmoves < 0) { 1151 + *error = "Unable to find a solution to this puzzle"; 1152 + return NULL; 1153 + } 1154 + if (nmoves == 0) { 1155 + *error = "Puzzle is already solved"; 1156 + return NULL; 1157 + } 1158 + 1159 + /* 1160 + * Encode the resulting solution as a move string. 1161 + */ 1162 + ret = snewn(nmoves * 40, char); 1163 + p = ret; 1164 + sep = 'S'; 1165 + 1166 + for (i = 0; i < nmoves; i++) { 1167 + p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]); 1168 + sep = ','; 1169 + } 1170 + 1171 + sfree(moves); 1172 + assert(p - ret < nmoves * 40); 1173 + ret = sresize(ret, p+1 - ret, char); 1174 + 1175 + return ret; 1176 + } 1177 + 1178 + static bool game_can_format_as_text_now(const game_params *params) 1179 + { 1180 + return true; 1181 + } 1182 + 1183 + static char *game_text_format(const game_state *state) 1184 + { 1185 + return board_text_format(state->w, state->h, state->board, 1186 + state->imm->forcefield); 1187 + } 1188 + 1189 + struct game_ui { 1190 + bool dragging; 1191 + int drag_anchor; 1192 + int drag_offset_x, drag_offset_y; 1193 + int drag_currpos; 1194 + bool *reachable; 1195 + int *bfs_queue; /* used as scratch in interpret_move */ 1196 + }; 1197 + 1198 + static game_ui *new_ui(const game_state *state) 1199 + { 1200 + int w = state->w, h = state->h, wh = w*h; 1201 + game_ui *ui = snew(game_ui); 1202 + 1203 + ui->dragging = false; 1204 + ui->drag_anchor = ui->drag_currpos = -1; 1205 + ui->drag_offset_x = ui->drag_offset_y = -1; 1206 + ui->reachable = snewn(wh, bool); 1207 + memset(ui->reachable, 0, wh * sizeof(bool)); 1208 + ui->bfs_queue = snewn(wh, int); 1209 + 1210 + return ui; 1211 + } 1212 + 1213 + static void free_ui(game_ui *ui) 1214 + { 1215 + sfree(ui->bfs_queue); 1216 + sfree(ui->reachable); 1217 + sfree(ui); 1218 + } 1219 + 1220 + static void game_changed_state(game_ui *ui, const game_state *oldstate, 1221 + const game_state *newstate) 1222 + { 1223 + } 1224 + 1225 + #define PREFERRED_TILESIZE 32 1226 + #define TILESIZE (ds->tilesize) 1227 + #define BORDER (TILESIZE/2) 1228 + #define COORD(x) ( (x) * TILESIZE + BORDER ) 1229 + #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) 1230 + #define BORDER_WIDTH (1 + TILESIZE/20) 1231 + #define HIGHLIGHT_WIDTH (1 + TILESIZE/16) 1232 + 1233 + #define FLASH_INTERVAL 0.10F 1234 + #define FLASH_TIME 3*FLASH_INTERVAL 1235 + 1236 + struct game_drawstate { 1237 + int tilesize; 1238 + int w, h; 1239 + unsigned long *grid; /* what's currently displayed */ 1240 + }; 1241 + 1242 + static char *interpret_move(const game_state *state, game_ui *ui, 1243 + const game_drawstate *ds, 1244 + int x, int y, int button) 1245 + { 1246 + int w = state->w, h = state->h, wh = w*h; 1247 + int tx, ty, i, j; 1248 + int qhead, qtail; 1249 + 1250 + if (button == LEFT_BUTTON) { 1251 + tx = FROMCOORD(x); 1252 + ty = FROMCOORD(y); 1253 + 1254 + if (tx < 0 || tx >= w || ty < 0 || ty >= h || 1255 + !ISBLOCK(state->board[ty*w+tx])) 1256 + return NULL; /* this click has no effect */ 1257 + 1258 + /* 1259 + * User has clicked on a block. Find the block's anchor 1260 + * and register that we've started dragging it. 1261 + */ 1262 + i = ty*w+tx; 1263 + while (ISDIST(state->board[i])) 1264 + i -= state->board[i]; 1265 + assert(i >= 0 && i < wh); 1266 + 1267 + ui->dragging = true; 1268 + ui->drag_anchor = i; 1269 + ui->drag_offset_x = tx - (i % w); 1270 + ui->drag_offset_y = ty - (i / w); 1271 + ui->drag_currpos = i; 1272 + 1273 + /* 1274 + * Now we immediately bfs out from the current location of 1275 + * the anchor, to find all the places to which this block 1276 + * can be dragged. 1277 + */ 1278 + memset(ui->reachable, 0, wh * sizeof(bool)); 1279 + qhead = qtail = 0; 1280 + ui->reachable[i] = true; 1281 + ui->bfs_queue[qtail++] = i; 1282 + for (j = i; j < wh; j++) 1283 + if (state->board[j] == DIST(j - i)) 1284 + i = j; 1285 + while (qhead < qtail) { 1286 + int pos = ui->bfs_queue[qhead++]; 1287 + int x = pos % w, y = pos / w; 1288 + int dir; 1289 + 1290 + for (dir = 0; dir < 4; dir++) { 1291 + int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); 1292 + int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); 1293 + int newpos; 1294 + 1295 + if (x + dx < 0 || x + dx >= w || 1296 + y + dy < 0 || y + dy >= h) 1297 + continue; 1298 + 1299 + newpos = pos + dy*w + dx; 1300 + if (ui->reachable[newpos]) 1301 + continue; /* already done this one */ 1302 + 1303 + /* 1304 + * Now search the grid to see if the block we're 1305 + * dragging could fit into this space. 1306 + */ 1307 + for (j = i; j >= 0; j = (ISDIST(state->board[j]) ? 1308 + j - state->board[j] : -1)) { 1309 + int jx = (j+pos-ui->drag_anchor) % w; 1310 + int jy = (j+pos-ui->drag_anchor) / w; 1311 + int j2; 1312 + 1313 + if (jx + dx < 0 || jx + dx >= w || 1314 + jy + dy < 0 || jy + dy >= h) 1315 + break; /* this position isn't valid at all */ 1316 + 1317 + j2 = (j+pos-ui->drag_anchor) + dy*w + dx; 1318 + 1319 + if (state->board[j2] == EMPTY && 1320 + (!state->imm->forcefield[j2] || 1321 + state->board[ui->drag_anchor] == MAINANCHOR)) 1322 + continue; 1323 + while (ISDIST(state->board[j2])) 1324 + j2 -= state->board[j2]; 1325 + assert(j2 >= 0 && j2 < wh); 1326 + if (j2 == ui->drag_anchor) 1327 + continue; 1328 + else 1329 + break; 1330 + } 1331 + 1332 + if (j < 0) { 1333 + /* 1334 + * If we got to the end of that loop without 1335 + * disqualifying this position, mark it as 1336 + * reachable for this drag. 1337 + */ 1338 + ui->reachable[newpos] = true; 1339 + ui->bfs_queue[qtail++] = newpos; 1340 + } 1341 + } 1342 + } 1343 + 1344 + /* 1345 + * And that's it. Update the display to reflect the start 1346 + * of a drag. 1347 + */ 1348 + return MOVE_UI_UPDATE; 1349 + } else if (button == LEFT_DRAG && ui->dragging) { 1350 + int dist, distlimit, dx, dy, s, px, py; 1351 + 1352 + tx = FROMCOORD(x); 1353 + ty = FROMCOORD(y); 1354 + 1355 + tx -= ui->drag_offset_x; 1356 + ty -= ui->drag_offset_y; 1357 + 1358 + /* 1359 + * Now search outwards from (tx,ty), in order of Manhattan 1360 + * distance, until we find a reachable square. 1361 + */ 1362 + distlimit = w+tx; 1363 + distlimit = max(distlimit, h+ty); 1364 + distlimit = max(distlimit, tx); 1365 + distlimit = max(distlimit, ty); 1366 + for (dist = 0; dist <= distlimit; dist++) { 1367 + for (dx = -dist; dx <= dist; dx++) 1368 + for (s = -1; s <= +1; s += 2) { 1369 + dy = s * (dist - abs(dx)); 1370 + px = tx + dx; 1371 + py = ty + dy; 1372 + if (px >= 0 && px < w && py >= 0 && py < h && 1373 + ui->reachable[py*w+px]) { 1374 + ui->drag_currpos = py*w+px; 1375 + return MOVE_UI_UPDATE; 1376 + } 1377 + } 1378 + } 1379 + return NULL; /* give up - this drag has no effect */ 1380 + } else if (button == LEFT_RELEASE && ui->dragging) { 1381 + char data[256], *str; 1382 + 1383 + /* 1384 + * Terminate the drag, and if the piece has actually moved 1385 + * then return a move string quoting the old and new 1386 + * locations of the piece's anchor. 1387 + */ 1388 + if (ui->drag_anchor != ui->drag_currpos) { 1389 + sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos); 1390 + str = dupstr(data); 1391 + } else 1392 + str = MOVE_UI_UPDATE; 1393 + 1394 + ui->dragging = false; 1395 + ui->drag_anchor = ui->drag_currpos = -1; 1396 + ui->drag_offset_x = ui->drag_offset_y = -1; 1397 + memset(ui->reachable, 0, wh * sizeof(bool)); 1398 + 1399 + return str; 1400 + } else if (button == ' ' && state->soln) { 1401 + /* 1402 + * Make the next move in the stored solution. 1403 + */ 1404 + char data[256]; 1405 + int a1, a2; 1406 + 1407 + a1 = state->soln->moves[state->soln_index*2]; 1408 + a2 = state->soln->moves[state->soln_index*2+1]; 1409 + if (a1 == state->lastmoved_pos) 1410 + a1 = state->lastmoved; 1411 + 1412 + sprintf(data, "M%d-%d", a1, a2); 1413 + return dupstr(data); 1414 + } 1415 + 1416 + return NULL; 1417 + } 1418 + 1419 + static bool move_piece(int w, int h, const unsigned char *src, 1420 + unsigned char *dst, bool *ff, int from, int to) 1421 + { 1422 + int wh = w*h; 1423 + int i, j; 1424 + 1425 + if (!ISANCHOR(dst[from])) 1426 + return false; 1427 + 1428 + /* 1429 + * Scan to the far end of the piece's linked list. 1430 + */ 1431 + for (i = j = from; j < wh; j++) 1432 + if (src[j] == DIST(j - i)) 1433 + i = j; 1434 + 1435 + /* 1436 + * Remove the piece from its old location in the new 1437 + * game state. 1438 + */ 1439 + for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) 1440 + dst[j] = EMPTY; 1441 + 1442 + /* 1443 + * And put it back in at the new location. 1444 + */ 1445 + for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) { 1446 + int jn = j + to - from; 1447 + if (jn < 0 || jn >= wh) 1448 + return false; 1449 + if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) { 1450 + dst[jn] = src[j]; 1451 + } else { 1452 + return false; 1453 + } 1454 + } 1455 + 1456 + return true; 1457 + } 1458 + 1459 + static game_state *execute_move(const game_state *state, const char *move) 1460 + { 1461 + int w = state->w, h = state->h /* , wh = w*h */; 1462 + char c; 1463 + int a1, a2, n, movesize; 1464 + game_state *ret = dup_game(state); 1465 + 1466 + while (*move) { 1467 + c = *move; 1468 + if (c == 'S') { 1469 + /* 1470 + * This is a solve move, so we just set up a stored 1471 + * solution path. 1472 + */ 1473 + if (ret->soln && --ret->soln->refcount <= 0) { 1474 + sfree(ret->soln->moves); 1475 + sfree(ret->soln); 1476 + } 1477 + ret->soln = snew(struct game_solution); 1478 + ret->soln->nmoves = 0; 1479 + ret->soln->moves = NULL; 1480 + ret->soln->refcount = 1; 1481 + ret->soln_index = 0; 1482 + ret->cheated = true; 1483 + 1484 + movesize = 0; 1485 + move++; 1486 + while (1) { 1487 + if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) { 1488 + free_game(ret); 1489 + return NULL; 1490 + } 1491 + 1492 + /* 1493 + * Special case: if the first move in the solution 1494 + * involves the piece for which we already have a 1495 + * partial stored move, adjust the source point to 1496 + * the original starting point of that piece. 1497 + */ 1498 + if (ret->soln->nmoves == 0 && a1 == ret->lastmoved) 1499 + a1 = ret->lastmoved_pos; 1500 + 1501 + if (ret->soln->nmoves >= movesize) { 1502 + movesize = (ret->soln->nmoves + 48) * 4 / 3; 1503 + ret->soln->moves = sresize(ret->soln->moves, 1504 + 2*movesize, int); 1505 + } 1506 + 1507 + ret->soln->moves[2*ret->soln->nmoves] = a1; 1508 + ret->soln->moves[2*ret->soln->nmoves+1] = a2; 1509 + ret->soln->nmoves++; 1510 + move += n; 1511 + if (*move != ',') 1512 + break; 1513 + move++; /* eat comma */ 1514 + } 1515 + } else if (c == 'M') { 1516 + move++; 1517 + if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 || 1518 + !move_piece(w, h, state->board, ret->board, 1519 + state->imm->forcefield, a1, a2)) { 1520 + free_game(ret); 1521 + return NULL; 1522 + } 1523 + if (a1 == ret->lastmoved) { 1524 + /* 1525 + * If the player has moved the same piece as they 1526 + * moved last time, don't increment the move 1527 + * count. In fact, if they've put the piece back 1528 + * where it started from, _decrement_ the move 1529 + * count. 1530 + */ 1531 + if (a2 == ret->lastmoved_pos) { 1532 + ret->movecount--; /* reverted last move */ 1533 + ret->lastmoved = ret->lastmoved_pos = -1; 1534 + } else { 1535 + ret->lastmoved = a2; 1536 + /* don't change lastmoved_pos */ 1537 + } 1538 + } else { 1539 + ret->lastmoved = a2; 1540 + ret->lastmoved_pos = a1; 1541 + ret->movecount++; 1542 + } 1543 + 1544 + /* 1545 + * If we have a stored solution path, see if we've 1546 + * strayed from it or successfully made the next move 1547 + * along it. 1548 + */ 1549 + if (ret->soln && ret->lastmoved_pos >= 0) { 1550 + if (ret->lastmoved_pos != 1551 + ret->soln->moves[ret->soln_index*2]) { 1552 + /* strayed from the path */ 1553 + ret->soln->refcount--; 1554 + assert(ret->soln->refcount > 0); 1555 + /* `state' at least still exists */ 1556 + ret->soln = NULL; 1557 + ret->soln_index = -1; 1558 + } else if (ret->lastmoved == 1559 + ret->soln->moves[ret->soln_index*2+1]) { 1560 + /* advanced along the path */ 1561 + ret->soln_index++; 1562 + if (ret->soln_index >= ret->soln->nmoves) { 1563 + /* finished the path! */ 1564 + ret->soln->refcount--; 1565 + assert(ret->soln->refcount > 0); 1566 + /* `state' at least still exists */ 1567 + ret->soln = NULL; 1568 + ret->soln_index = -1; 1569 + } 1570 + } 1571 + } 1572 + 1573 + if (ret->board[a2] == MAINANCHOR && 1574 + a2 == ret->ty * w + ret->tx && ret->completed < 0) 1575 + ret->completed = ret->movecount; 1576 + move += n; 1577 + } else { 1578 + free_game(ret); 1579 + return NULL; 1580 + } 1581 + if (*move == ';') 1582 + move++; 1583 + else if (*move) { 1584 + free_game(ret); 1585 + return NULL; 1586 + } 1587 + } 1588 + 1589 + return ret; 1590 + } 1591 + 1592 + /* ---------------------------------------------------------------------- 1593 + * Drawing routines. 1594 + */ 1595 + 1596 + static void game_compute_size(const game_params *params, int tilesize, 1597 + const game_ui *ui, int *x, int *y) 1598 + { 1599 + /* fool the macros */ 1600 + struct dummy { int tilesize; } dummy, *ds = &dummy; 1601 + dummy.tilesize = tilesize; 1602 + 1603 + *x = params->w * TILESIZE + 2*BORDER; 1604 + *y = params->h * TILESIZE + 2*BORDER; 1605 + } 1606 + 1607 + static void game_set_size(drawing *dr, game_drawstate *ds, 1608 + const game_params *params, int tilesize) 1609 + { 1610 + ds->tilesize = tilesize; 1611 + } 1612 + 1613 + static void raise_colour(float *target, float *src, float *limit) 1614 + { 1615 + int i; 1616 + for (i = 0; i < 3; i++) 1617 + target[i] = (2*src[i] + limit[i]) / 3; 1618 + } 1619 + 1620 + static float *game_colours(frontend *fe, int *ncolours) 1621 + { 1622 + float *ret = snewn(3 * NCOLOURS, float); 1623 + 1624 + game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 1625 + 1626 + /* 1627 + * When dragging a tile, we light it up a bit. 1628 + */ 1629 + raise_colour(ret+3*COL_DRAGGING, 1630 + ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT); 1631 + raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT, 1632 + ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT); 1633 + raise_colour(ret+3*COL_DRAGGING_LOWLIGHT, 1634 + ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT); 1635 + 1636 + /* 1637 + * The main tile is tinted blue. 1638 + */ 1639 + ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; 1640 + ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1]; 1641 + ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2]; 1642 + game_mkhighlight_specific(fe, ret, COL_MAIN, 1643 + COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT); 1644 + 1645 + /* 1646 + * And we light that up a bit too when dragging. 1647 + */ 1648 + raise_colour(ret+3*COL_MAIN_DRAGGING, 1649 + ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT); 1650 + raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT, 1651 + ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT); 1652 + raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT, 1653 + ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT); 1654 + 1655 + /* 1656 + * The target area on the floor is tinted green. 1657 + */ 1658 + ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; 1659 + ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1]; 1660 + ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; 1661 + game_mkhighlight_specific(fe, ret, COL_TARGET, 1662 + COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT); 1663 + 1664 + *ncolours = NCOLOURS; 1665 + return ret; 1666 + } 1667 + 1668 + static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 1669 + { 1670 + int w = state->w, h = state->h, wh = w*h; 1671 + struct game_drawstate *ds = snew(struct game_drawstate); 1672 + int i; 1673 + 1674 + ds->tilesize = 0; 1675 + ds->w = w; 1676 + ds->h = h; 1677 + ds->grid = snewn(wh, unsigned long); 1678 + for (i = 0; i < wh; i++) 1679 + ds->grid[i] = ~(unsigned long)0; 1680 + 1681 + return ds; 1682 + } 1683 + 1684 + static void game_free_drawstate(drawing *dr, game_drawstate *ds) 1685 + { 1686 + sfree(ds->grid); 1687 + sfree(ds); 1688 + } 1689 + 1690 + #define BG_NORMAL 0x00000001UL 1691 + #define BG_TARGET 0x00000002UL 1692 + #define BG_FORCEFIELD 0x00000004UL 1693 + #define FLASH_LOW 0x00000008UL 1694 + #define FLASH_HIGH 0x00000010UL 1695 + #define FG_WALL 0x00000020UL 1696 + #define FG_MAIN 0x00000040UL 1697 + #define FG_NORMAL 0x00000080UL 1698 + #define FG_DRAGGING 0x00000100UL 1699 + #define FG_SHADOW 0x00000200UL 1700 + #define FG_SOLVEPIECE 0x00000400UL 1701 + #define FG_MAINPIECESH 11 1702 + #define FG_SHADOWSH 19 1703 + 1704 + #define PIECE_LBORDER 0x00000001UL 1705 + #define PIECE_TBORDER 0x00000002UL 1706 + #define PIECE_RBORDER 0x00000004UL 1707 + #define PIECE_BBORDER 0x00000008UL 1708 + #define PIECE_TLCORNER 0x00000010UL 1709 + #define PIECE_TRCORNER 0x00000020UL 1710 + #define PIECE_BLCORNER 0x00000040UL 1711 + #define PIECE_BRCORNER 0x00000080UL 1712 + #define PIECE_MASK 0x000000FFUL 1713 + 1714 + /* 1715 + * Utility function. 1716 + */ 1717 + #define TYPE_MASK 0xF000 1718 + #define COL_MASK 0x0FFF 1719 + #define TYPE_RECT 0x0000 1720 + #define TYPE_TLCIRC 0x4000 1721 + #define TYPE_TRCIRC 0x5000 1722 + #define TYPE_BLCIRC 0x6000 1723 + #define TYPE_BRCIRC 0x7000 1724 + static void maybe_rect(drawing *dr, int x, int y, int w, int h, 1725 + int coltype, int col2) 1726 + { 1727 + int colour = coltype & COL_MASK, type = coltype & TYPE_MASK; 1728 + 1729 + if (colour > NCOLOURS) 1730 + return; 1731 + if (type == TYPE_RECT) { 1732 + draw_rect(dr, x, y, w, h, colour); 1733 + } else { 1734 + int cx, cy, r; 1735 + 1736 + clip(dr, x, y, w, h); 1737 + 1738 + cx = x; 1739 + cy = y; 1740 + r = w-1; 1741 + if (type & 0x1000) 1742 + cx += r; 1743 + if (type & 0x2000) 1744 + cy += r; 1745 + 1746 + if (col2 == -1 || col2 == coltype) { 1747 + assert(w == h); 1748 + draw_circle(dr, cx, cy, r, colour, colour); 1749 + } else { 1750 + /* 1751 + * We aim to draw a quadrant of a circle in two 1752 + * different colours. We do this using Bresenham's 1753 + * algorithm directly, because the Puzzles drawing API 1754 + * doesn't have a draw-sector primitive. 1755 + */ 1756 + int bx, by, bd, bd2; 1757 + int xm = (type & 0x1000 ? -1 : +1); 1758 + int ym = (type & 0x2000 ? -1 : +1); 1759 + 1760 + by = r; 1761 + bx = 0; 1762 + bd = 0; 1763 + while (by >= bx) { 1764 + /* 1765 + * Plot the point. 1766 + */ 1767 + { 1768 + int x1 = cx+xm*bx, y1 = cy+ym*bx; 1769 + int x2, y2; 1770 + 1771 + x2 = cx+xm*by; y2 = y1; 1772 + draw_rect(dr, min(x1,x2), min(y1,y2), 1773 + abs(x1-x2)+1, abs(y1-y2)+1, colour); 1774 + x2 = x1; y2 = cy+ym*by; 1775 + draw_rect(dr, min(x1,x2), min(y1,y2), 1776 + abs(x1-x2)+1, abs(y1-y2)+1, col2); 1777 + } 1778 + 1779 + bd += 2*bx + 1; 1780 + bd2 = bd - (2*by - 1); 1781 + if (abs(bd2) < abs(bd)) { 1782 + bd = bd2; 1783 + by--; 1784 + } 1785 + bx++; 1786 + } 1787 + } 1788 + 1789 + unclip(dr); 1790 + } 1791 + } 1792 + 1793 + static void draw_wallpart(drawing *dr, game_drawstate *ds, 1794 + int tx, int ty, unsigned long val, 1795 + int cl, int cc, int ch) 1796 + { 1797 + int coords[6]; 1798 + 1799 + draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); 1800 + if (val & PIECE_LBORDER) 1801 + draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE, 1802 + ch); 1803 + if (val & PIECE_RBORDER) 1804 + draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, 1805 + HIGHLIGHT_WIDTH, TILESIZE, cl); 1806 + if (val & PIECE_TBORDER) 1807 + draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch); 1808 + if (val & PIECE_BBORDER) 1809 + draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, 1810 + TILESIZE, HIGHLIGHT_WIDTH, cl); 1811 + if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) { 1812 + draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, 1813 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); 1814 + clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, 1815 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); 1816 + coords[0] = tx - 1; 1817 + coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; 1818 + coords[2] = tx + HIGHLIGHT_WIDTH; 1819 + coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; 1820 + coords[4] = tx - 1; 1821 + coords[5] = ty + TILESIZE; 1822 + draw_polygon(dr, coords, 3, ch, ch); 1823 + unclip(dr); 1824 + } else if (val & PIECE_BLCORNER) { 1825 + draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, 1826 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); 1827 + clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, 1828 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); 1829 + coords[0] = tx - 1; 1830 + coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; 1831 + coords[2] = tx + HIGHLIGHT_WIDTH; 1832 + coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; 1833 + coords[4] = tx - 1; 1834 + coords[5] = ty + TILESIZE; 1835 + draw_polygon(dr, coords, 3, cl, cl); 1836 + unclip(dr); 1837 + } 1838 + if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) { 1839 + draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, 1840 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); 1841 + clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, 1842 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); 1843 + coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; 1844 + coords[1] = ty - 1; 1845 + coords[2] = tx + TILESIZE; 1846 + coords[3] = ty - 1; 1847 + coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; 1848 + coords[5] = ty + HIGHLIGHT_WIDTH; 1849 + draw_polygon(dr, coords, 3, ch, ch); 1850 + unclip(dr); 1851 + } else if (val & PIECE_TRCORNER) { 1852 + draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, 1853 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); 1854 + clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, 1855 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); 1856 + coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; 1857 + coords[1] = ty - 1; 1858 + coords[2] = tx + TILESIZE; 1859 + coords[3] = ty - 1; 1860 + coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; 1861 + coords[5] = ty + HIGHLIGHT_WIDTH; 1862 + draw_polygon(dr, coords, 3, cl, cl); 1863 + unclip(dr); 1864 + } 1865 + if (val & PIECE_TLCORNER) 1866 + draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); 1867 + if (val & PIECE_BRCORNER) 1868 + draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, 1869 + ty+TILESIZE-HIGHLIGHT_WIDTH, 1870 + HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); 1871 + } 1872 + 1873 + static void draw_piecepart(drawing *dr, game_drawstate *ds, 1874 + int tx, int ty, unsigned long val, 1875 + int cl, int cc, int ch) 1876 + { 1877 + int x[6], y[6]; 1878 + 1879 + /* 1880 + * Drawing the blocks is hellishly fiddly. The blocks don't 1881 + * stretch to the full size of the tile; there's a border 1882 + * around them of size BORDER_WIDTH. Then they have bevelled 1883 + * borders of size HIGHLIGHT_WIDTH, and also rounded corners. 1884 + * 1885 + * I tried for some time to find a clean and clever way to 1886 + * figure out what needed drawing from the corner and border 1887 + * flags, but in the end the cleanest way I could find was the 1888 + * following. We divide the grid square into 25 parts by 1889 + * ruling four horizontal and four vertical lines across it; 1890 + * those lines are at BORDER_WIDTH and BORDER_WIDTH + 1891 + * HIGHLIGHT_WIDTH from the top, from the bottom, from the 1892 + * left and from the right. Then we carefully consider each of 1893 + * the resulting 25 sections of square, and decide separately 1894 + * what needs to go in it based on the flags. In complicated 1895 + * cases there can be up to five possibilities affecting any 1896 + * given section (no corner or border flags, just the corner 1897 + * flag, one border flag, the other border flag, both border 1898 + * flags). So there's a lot of very fiddly logic here and all 1899 + * I could really think to do was give it my best shot and 1900 + * then test it and correct all the typos. Not fun to write, 1901 + * and I'm sure it isn't fun to read either, but it seems to 1902 + * work. 1903 + */ 1904 + 1905 + x[0] = tx; 1906 + x[1] = x[0] + BORDER_WIDTH; 1907 + x[2] = x[1] + HIGHLIGHT_WIDTH; 1908 + x[5] = tx + TILESIZE; 1909 + x[4] = x[5] - BORDER_WIDTH; 1910 + x[3] = x[4] - HIGHLIGHT_WIDTH; 1911 + 1912 + y[0] = ty; 1913 + y[1] = y[0] + BORDER_WIDTH; 1914 + y[2] = y[1] + HIGHLIGHT_WIDTH; 1915 + y[5] = ty + TILESIZE; 1916 + y[4] = y[5] - BORDER_WIDTH; 1917 + y[3] = y[4] - HIGHLIGHT_WIDTH; 1918 + 1919 + #define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q] 1920 + 1921 + maybe_rect(dr, RECT(0,0), 1922 + (val & (PIECE_TLCORNER | PIECE_TBORDER | 1923 + PIECE_LBORDER)) ? -1 : cc, -1); 1924 + maybe_rect(dr, RECT(1,0), 1925 + (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 : 1926 + (val & PIECE_LBORDER) ? ch : cc, -1); 1927 + maybe_rect(dr, RECT(2,0), 1928 + (val & PIECE_TBORDER) ? -1 : cc, -1); 1929 + maybe_rect(dr, RECT(3,0), 1930 + (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 : 1931 + (val & PIECE_RBORDER) ? cl : cc, -1); 1932 + maybe_rect(dr, RECT(4,0), 1933 + (val & (PIECE_TRCORNER | PIECE_TBORDER | 1934 + PIECE_RBORDER)) ? -1 : cc, -1); 1935 + maybe_rect(dr, RECT(0,1), 1936 + (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 : 1937 + (val & PIECE_TBORDER) ? ch : cc, -1); 1938 + maybe_rect(dr, RECT(1,1), 1939 + (val & PIECE_TLCORNER) ? cc : -1, -1); 1940 + maybe_rect(dr, RECT(1,1), 1941 + (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC : 1942 + !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC : 1943 + (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1); 1944 + maybe_rect(dr, RECT(2,1), 1945 + (val & PIECE_TBORDER) ? ch : cc, -1); 1946 + maybe_rect(dr, RECT(3,1), 1947 + (val & PIECE_TRCORNER) ? cc : -1, -1); 1948 + maybe_rect(dr, RECT(3,1), 1949 + (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch : 1950 + (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl : 1951 + !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC : 1952 + (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC : 1953 + cc, ch); 1954 + maybe_rect(dr, RECT(4,1), 1955 + (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 : 1956 + (val & PIECE_TBORDER) ? ch : cc, -1); 1957 + maybe_rect(dr, RECT(0,2), 1958 + (val & PIECE_LBORDER) ? -1 : cc, -1); 1959 + maybe_rect(dr, RECT(1,2), 1960 + (val & PIECE_LBORDER) ? ch : cc, -1); 1961 + maybe_rect(dr, RECT(2,2), 1962 + cc, -1); 1963 + maybe_rect(dr, RECT(3,2), 1964 + (val & PIECE_RBORDER) ? cl : cc, -1); 1965 + maybe_rect(dr, RECT(4,2), 1966 + (val & PIECE_RBORDER) ? -1 : cc, -1); 1967 + maybe_rect(dr, RECT(0,3), 1968 + (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 : 1969 + (val & PIECE_BBORDER) ? cl : cc, -1); 1970 + maybe_rect(dr, RECT(1,3), 1971 + (val & PIECE_BLCORNER) ? cc : -1, -1); 1972 + maybe_rect(dr, RECT(1,3), 1973 + (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl : 1974 + (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch : 1975 + !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC : 1976 + (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC : 1977 + cc, cl); 1978 + maybe_rect(dr, RECT(2,3), 1979 + (val & PIECE_BBORDER) ? cl : cc, -1); 1980 + maybe_rect(dr, RECT(3,3), 1981 + (val & PIECE_BRCORNER) ? cc : -1, -1); 1982 + maybe_rect(dr, RECT(3,3), 1983 + (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC : 1984 + !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC : 1985 + (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1); 1986 + maybe_rect(dr, RECT(4,3), 1987 + (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 : 1988 + (val & PIECE_BBORDER) ? cl : cc, -1); 1989 + maybe_rect(dr, RECT(0,4), 1990 + (val & (PIECE_BLCORNER | PIECE_BBORDER | 1991 + PIECE_LBORDER)) ? -1 : cc, -1); 1992 + maybe_rect(dr, RECT(1,4), 1993 + (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 : 1994 + (val & PIECE_LBORDER) ? ch : cc, -1); 1995 + maybe_rect(dr, RECT(2,4), 1996 + (val & PIECE_BBORDER) ? -1 : cc, -1); 1997 + maybe_rect(dr, RECT(3,4), 1998 + (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 : 1999 + (val & PIECE_RBORDER) ? cl : cc, -1); 2000 + maybe_rect(dr, RECT(4,4), 2001 + (val & (PIECE_BRCORNER | PIECE_BBORDER | 2002 + PIECE_RBORDER)) ? -1 : cc, -1); 2003 + 2004 + #undef RECT 2005 + } 2006 + 2007 + static void draw_tile(drawing *dr, game_drawstate *ds, 2008 + int x, int y, unsigned long val) 2009 + { 2010 + int tx = COORD(x), ty = COORD(y); 2011 + int cc, ch, cl; 2012 + 2013 + /* 2014 + * Draw the tile background. 2015 + */ 2016 + if (val & BG_TARGET) 2017 + cc = COL_TARGET; 2018 + else 2019 + cc = COL_BACKGROUND; 2020 + ch = cc+1; 2021 + cl = cc+2; 2022 + if (val & FLASH_LOW) 2023 + cc = cl; 2024 + else if (val & FLASH_HIGH) 2025 + cc = ch; 2026 + 2027 + draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); 2028 + if (val & BG_FORCEFIELD) { 2029 + /* 2030 + * Cattle-grid effect to indicate that nothing but the 2031 + * main block can slide over this square. 2032 + */ 2033 + int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH)); 2034 + int i; 2035 + 2036 + for (i = 1; i < n; i += 3) { 2037 + draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl); 2038 + draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl); 2039 + } 2040 + } 2041 + 2042 + /* 2043 + * Draw the tile midground: a shadow of a block, for 2044 + * displaying partial solutions. 2045 + */ 2046 + if (val & FG_SHADOW) { 2047 + draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK, 2048 + cl, cl, cl); 2049 + } 2050 + 2051 + /* 2052 + * Draw the tile foreground, i.e. some section of a block or 2053 + * wall. 2054 + */ 2055 + if (val & FG_WALL) { 2056 + cc = COL_BACKGROUND; 2057 + ch = cc+1; 2058 + cl = cc+2; 2059 + if (val & FLASH_LOW) 2060 + cc = cl; 2061 + else if (val & FLASH_HIGH) 2062 + cc = ch; 2063 + 2064 + draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, 2065 + cl, cc, ch); 2066 + } else if (val & (FG_MAIN | FG_NORMAL)) { 2067 + if (val & FG_DRAGGING) 2068 + cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING); 2069 + else 2070 + cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND); 2071 + ch = cc+1; 2072 + cl = cc+2; 2073 + 2074 + if (val & FLASH_LOW) 2075 + cc = cl; 2076 + else if (val & (FLASH_HIGH | FG_SOLVEPIECE)) 2077 + cc = ch; 2078 + 2079 + draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, 2080 + cl, cc, ch); 2081 + } 2082 + 2083 + draw_update(dr, tx, ty, TILESIZE, TILESIZE); 2084 + } 2085 + 2086 + static unsigned long find_piecepart(int w, int h, DSF *dsf, int x, int y) 2087 + { 2088 + int i = y*w+x; 2089 + int canon = dsf_canonify(dsf, i); 2090 + unsigned long val = 0; 2091 + 2092 + if (x == 0 || canon != dsf_canonify(dsf, i-1)) 2093 + val |= PIECE_LBORDER; 2094 + if (y== 0 || canon != dsf_canonify(dsf, i-w)) 2095 + val |= PIECE_TBORDER; 2096 + if (x == w-1 || canon != dsf_canonify(dsf, i+1)) 2097 + val |= PIECE_RBORDER; 2098 + if (y == h-1 || canon != dsf_canonify(dsf, i+w)) 2099 + val |= PIECE_BBORDER; 2100 + if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) && 2101 + canon != dsf_canonify(dsf, i-1-w)) 2102 + val |= PIECE_TLCORNER; 2103 + if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) && 2104 + canon != dsf_canonify(dsf, i+1-w)) 2105 + val |= PIECE_TRCORNER; 2106 + if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) && 2107 + canon != dsf_canonify(dsf, i-1+w)) 2108 + val |= PIECE_BLCORNER; 2109 + if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) && 2110 + canon != dsf_canonify(dsf, i+1+w)) 2111 + val |= PIECE_BRCORNER; 2112 + return val; 2113 + } 2114 + 2115 + static void game_redraw(drawing *dr, game_drawstate *ds, 2116 + const game_state *oldstate, const game_state *state, 2117 + int dir, const game_ui *ui, 2118 + float animtime, float flashtime) 2119 + { 2120 + int w = state->w, h = state->h, wh = w*h; 2121 + unsigned char *board; 2122 + DSF *dsf; 2123 + int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst; 2124 + 2125 + /* 2126 + * Construct the board we'll be displaying (which may be 2127 + * different from the one in state if ui describes a drag in 2128 + * progress). 2129 + */ 2130 + board = snewn(wh, unsigned char); 2131 + memcpy(board, state->board, wh); 2132 + if (ui->dragging) { 2133 + bool mpret = move_piece(w, h, state->board, board, 2134 + state->imm->forcefield, 2135 + ui->drag_anchor, ui->drag_currpos); 2136 + assert(mpret); 2137 + } 2138 + 2139 + if (state->soln) { 2140 + solvesrc = state->soln->moves[state->soln_index*2]; 2141 + solvedst = state->soln->moves[state->soln_index*2+1]; 2142 + if (solvesrc == state->lastmoved_pos) 2143 + solvesrc = state->lastmoved; 2144 + if (solvesrc == ui->drag_anchor) 2145 + solvesrc = ui->drag_currpos; 2146 + } else 2147 + solvesrc = solvedst = -1; 2148 + 2149 + /* 2150 + * Build a dsf out of that board, so we can conveniently tell 2151 + * which edges are connected and which aren't. 2152 + */ 2153 + dsf = dsf_new(wh); 2154 + mainanchor = -1; 2155 + for (y = 0; y < h; y++) 2156 + for (x = 0; x < w; x++) { 2157 + int i = y*w+x; 2158 + 2159 + if (ISDIST(board[i])) 2160 + dsf_merge(dsf, i, i - board[i]); 2161 + if (board[i] == MAINANCHOR) 2162 + mainanchor = i; 2163 + if (board[i] == WALL) { 2164 + if (x > 0 && board[i-1] == WALL) 2165 + dsf_merge(dsf, i, i-1); 2166 + if (y > 0 && board[i-w] == WALL) 2167 + dsf_merge(dsf, i, i-w); 2168 + } 2169 + } 2170 + assert(mainanchor >= 0); 2171 + mainpos = dsf_canonify(dsf, mainanchor); 2172 + dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1; 2173 + solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1; 2174 + 2175 + /* 2176 + * Now we can construct the data about what we want to draw. 2177 + */ 2178 + for (y = 0; y < h; y++) 2179 + for (x = 0; x < w; x++) { 2180 + int i = y*w+x; 2181 + int j; 2182 + unsigned long val; 2183 + int canon; 2184 + 2185 + /* 2186 + * See if this square is part of the target area. 2187 + */ 2188 + j = i + mainanchor - (state->ty * w + state->tx); 2189 + while (j >= 0 && j < wh && ISDIST(board[j])) 2190 + j -= board[j]; 2191 + if (j == mainanchor) 2192 + val = BG_TARGET; 2193 + else 2194 + val = BG_NORMAL; 2195 + 2196 + if (state->imm->forcefield[i]) 2197 + val |= BG_FORCEFIELD; 2198 + 2199 + if (flashtime > 0) { 2200 + int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1; 2201 + val |= (flashtype ? FLASH_LOW : FLASH_HIGH); 2202 + } 2203 + 2204 + if (board[i] != EMPTY) { 2205 + canon = dsf_canonify(dsf, i); 2206 + 2207 + if (board[i] == WALL) 2208 + val |= FG_WALL; 2209 + else if (canon == mainpos) 2210 + val |= FG_MAIN; 2211 + else 2212 + val |= FG_NORMAL; 2213 + if (canon == dragpos) 2214 + val |= FG_DRAGGING; 2215 + if (canon == solvepos) 2216 + val |= FG_SOLVEPIECE; 2217 + 2218 + /* 2219 + * Now look around to see if other squares 2220 + * belonging to the same block are adjacent to us. 2221 + */ 2222 + val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH; 2223 + } 2224 + 2225 + /* 2226 + * If we're in the middle of showing a solution, 2227 + * display a shadow piece for the target of the 2228 + * current move. 2229 + */ 2230 + if (solvepos >= 0) { 2231 + int si = i - solvedst + solvesrc; 2232 + if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) { 2233 + val |= find_piecepart(w, h, dsf, 2234 + si % w, si / w) << FG_SHADOWSH; 2235 + val |= FG_SHADOW; 2236 + } 2237 + } 2238 + 2239 + if (val != ds->grid[i]) { 2240 + draw_tile(dr, ds, x, y, val); 2241 + ds->grid[i] = val; 2242 + } 2243 + } 2244 + 2245 + /* 2246 + * Update the status bar. 2247 + */ 2248 + { 2249 + char statusbuf[256]; 2250 + 2251 + sprintf(statusbuf, "%sMoves: %d", 2252 + (state->completed >= 0 ? 2253 + (state->cheated ? "Auto-solved. " : "COMPLETED! ") : 2254 + (state->cheated ? "Auto-solver used. " : "")), 2255 + (state->completed >= 0 ? state->completed : state->movecount)); 2256 + if (state->minmoves >= 0) 2257 + sprintf(statusbuf+strlen(statusbuf), " (min %d)", 2258 + state->minmoves); 2259 + 2260 + status_bar(dr, statusbuf); 2261 + } 2262 + 2263 + dsf_free(dsf); 2264 + sfree(board); 2265 + } 2266 + 2267 + static float game_anim_length(const game_state *oldstate, 2268 + const game_state *newstate, int dir, game_ui *ui) 2269 + { 2270 + return 0.0F; 2271 + } 2272 + 2273 + static float game_flash_length(const game_state *oldstate, 2274 + const game_state *newstate, int dir, game_ui *ui) 2275 + { 2276 + if (oldstate->completed < 0 && newstate->completed >= 0) 2277 + return FLASH_TIME; 2278 + 2279 + return 0.0F; 2280 + } 2281 + 2282 + static void game_get_cursor_location(const game_ui *ui, 2283 + const game_drawstate *ds, 2284 + const game_state *state, 2285 + const game_params *params, 2286 + int *x, int *y, int *w, int *h) 2287 + { 2288 + } 2289 + 2290 + static int game_status(const game_state *state) 2291 + { 2292 + return state->completed ? +1 : 0; 2293 + } 2294 + 2295 + static bool game_timing_state(const game_state *state, game_ui *ui) 2296 + { 2297 + return true; 2298 + } 2299 + 2300 + static void game_print_size(const game_params *params, const game_ui *ui, 2301 + float *x, float *y) 2302 + { 2303 + } 2304 + 2305 + static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 2306 + int tilesize) 2307 + { 2308 + } 2309 + 2310 + #ifdef COMBINED 2311 + #define thegame slide 2312 + #endif 2313 + 2314 + const struct game thegame = { 2315 + "Slide", NULL, NULL, 2316 + default_params, 2317 + game_fetch_preset, NULL, 2318 + decode_params, 2319 + encode_params, 2320 + free_params, 2321 + dup_params, 2322 + true, game_configure, custom_params, 2323 + validate_params, 2324 + new_game_desc, 2325 + validate_desc, 2326 + new_game, 2327 + dup_game, 2328 + free_game, 2329 + true, solve_game, 2330 + true, game_can_format_as_text_now, game_text_format, 2331 + NULL, NULL, /* get_prefs, set_prefs */ 2332 + new_ui, 2333 + free_ui, 2334 + NULL, /* encode_ui */ 2335 + NULL, /* decode_ui */ 2336 + NULL, /* game_request_keys */ 2337 + game_changed_state, 2338 + NULL, /* current_key_label */ 2339 + interpret_move, 2340 + execute_move, 2341 + PREFERRED_TILESIZE, game_compute_size, game_set_size, 2342 + game_colours, 2343 + game_new_drawstate, 2344 + game_free_drawstate, 2345 + game_redraw, 2346 + game_anim_length, 2347 + game_flash_length, 2348 + game_get_cursor_location, 2349 + game_status, 2350 + false, false, game_print_size, game_print, 2351 + true, /* wants_statusbar */ 2352 + false, game_timing_state, 2353 + 0, /* flags */ 2354 + }; 2355 + 2356 + #ifdef STANDALONE_SOLVER 2357 + 2358 + #include <stdarg.h> 2359 + 2360 + int main(int argc, char **argv) 2361 + { 2362 + game_params *p; 2363 + game_state *s; 2364 + char *id = NULL, *desc; 2365 + const char *err; 2366 + bool count = false; 2367 + int ret; 2368 + int *moves; 2369 + 2370 + while (--argc > 0) { 2371 + char *p = *++argv; 2372 + /* 2373 + if (!strcmp(p, "-v")) { 2374 + verbose = true; 2375 + } else 2376 + */ 2377 + if (!strcmp(p, "-c")) { 2378 + count = true; 2379 + } else if (*p == '-') { 2380 + fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); 2381 + return 1; 2382 + } else { 2383 + id = p; 2384 + } 2385 + } 2386 + 2387 + if (!id) { 2388 + fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]); 2389 + return 1; 2390 + } 2391 + 2392 + desc = strchr(id, ':'); 2393 + if (!desc) { 2394 + fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); 2395 + return 1; 2396 + } 2397 + *desc++ = '\0'; 2398 + 2399 + p = default_params(); 2400 + decode_params(p, id); 2401 + err = validate_desc(p, desc); 2402 + if (err) { 2403 + fprintf(stderr, "%s: %s\n", argv[0], err); 2404 + return 1; 2405 + } 2406 + s = new_game(NULL, p, desc); 2407 + 2408 + ret = solve_board(s->w, s->h, s->board, s->imm->forcefield, 2409 + s->tx, s->ty, -1, &moves); 2410 + if (ret < 0) { 2411 + printf("No solution found\n"); 2412 + } else { 2413 + int index = 0; 2414 + if (count) { 2415 + printf("%d moves required\n", ret); 2416 + return 0; 2417 + } 2418 + while (1) { 2419 + bool moveret; 2420 + char *text = board_text_format(s->w, s->h, s->board, 2421 + s->imm->forcefield); 2422 + game_state *s2; 2423 + 2424 + printf("position %d:\n%s", index, text); 2425 + 2426 + if (index >= ret) 2427 + break; 2428 + 2429 + s2 = dup_game(s); 2430 + moveret = move_piece(s->w, s->h, s->board, 2431 + s2->board, s->imm->forcefield, 2432 + moves[index*2], moves[index*2+1]); 2433 + assert(moveret); 2434 + 2435 + free_game(s); 2436 + s = s2; 2437 + index++; 2438 + } 2439 + } 2440 + 2441 + return 0; 2442 + } 2443 + 2444 + #endif
+1476
apps/plugins/puzzles/src/unfinished/sokoban.c
··· 1 + /* 2 + * sokoban.c: An implementation of the well-known Sokoban barrel- 3 + * pushing game. Random generation is too simplistic to be 4 + * credible, but the rest of the gameplay works well enough to use 5 + * it with hand-written level descriptions. 6 + */ 7 + 8 + /* 9 + * TODO: 10 + * 11 + * - I think it would be better to ditch the `prev' array, and 12 + * instead make the `dist' array strictly monotonic (by having 13 + * each distance be something like I*A+S, where A is the grid 14 + * area, I the number of INITIAL squares trampled on, and S the 15 + * number of harmless spaces moved through). This would permit 16 + * the path-tracing when a pull is actually made to choose 17 + * randomly from all the possible shortest routes, which would 18 + * be superior in terms of eliminating directional bias. 19 + * + So when tracing the path back to the current px,py, we 20 + * look at all four adjacent squares, find the minimum 21 + * distance, check that it's _strictly smaller_ than that of 22 + * the current square, and restrict our choice to precisely 23 + * those squares with that minimum distance. 24 + * + The other place `prev' is currently used is in the check 25 + * for consistency of a pull. We would have to replace the 26 + * check for whether prev[ny*w+nx]==oy*w+ox with a check that 27 + * made sure there was at least one adjacent square with a 28 + * smaller distance which _wasn't_ oy*w+ox. Then when we did 29 + * the path-tracing we'd also have to take this special case 30 + * into account. 31 + * 32 + * - More discriminating choice of pull. (Snigger.) 33 + * + favour putting targets in clumps 34 + * + try to shoot for a reasonably consistent number of barrels 35 + * (adjust willingness to generate a new barrel depending on 36 + * how many are already present) 37 + * + adjust willingness to break new ground depending on how 38 + * much is already broken 39 + * 40 + * - generation time parameters: 41 + * + enable NetHack mode (and find a better place for the hole) 42 + * + decide how many of the remaining Is should be walls 43 + * 44 + * - at the end of generation, randomly position the starting 45 + * player coordinates, probably by (somehow) reusing the same 46 + * bfs currently inside the loop. 47 + * 48 + * - possible backtracking? 49 + * 50 + * - IWBNI we could spot completely unreachable bits of level at 51 + * the outside, and not bother drawing grid lines for them. The 52 + * NH levels currently look a bit weird with grid lines on the 53 + * outside of the boundary. 54 + */ 55 + 56 + #include <stdio.h> 57 + #include <stdlib.h> 58 + #include <string.h> 59 + #include <assert.h> 60 + #include <ctype.h> 61 + #ifdef NO_TGMATH_H 62 + # include <math.h> 63 + #else 64 + # include <tgmath.h> 65 + #endif 66 + 67 + #include "puzzles.h" 68 + 69 + /* 70 + * Various subsets of these constants are used during game 71 + * generation, game play, game IDs and the game_drawstate. 72 + */ 73 + #define INITIAL 'i' /* used only in game generation */ 74 + #define SPACE 's' 75 + #define WALL 'w' 76 + #define PIT 'p' 77 + #define DEEP_PIT 'd' 78 + #define TARGET 't' 79 + #define BARREL 'b' 80 + #define BARRELTARGET 'f' /* target is 'f'illed */ 81 + #define PLAYER 'u' /* yo'u'; used in game IDs */ 82 + #define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */ 83 + #define INVALID '!' /* used in drawstate to force redraw */ 84 + /* 85 + * We also support the use of any capital letter as a barrel, which 86 + * will be displayed with that letter as a label. (This facilitates 87 + * people distributing annotated game IDs for particular Sokoban 88 + * levels, so they can accompany them with verbal instructions 89 + * about pushing particular barrels in particular ways.) Therefore, 90 + * to find out whether something is a barrel, we need a test 91 + * function which does a bit more than just comparing to BARREL. 92 + * 93 + * When resting on target squares, capital-letter barrels are 94 + * replaced with their control-character value (A -> ^A). 95 + */ 96 + #define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET ) 97 + #define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \ 98 + ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) ) 99 + #define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \ 100 + (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) ) 101 + #define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) ) 102 + #define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) ) 103 + #define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \ 104 + (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 ) 105 + 106 + #define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0) 107 + #define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0) 108 + 109 + #define FLASH_LENGTH 0.3F 110 + 111 + enum { 112 + COL_BACKGROUND, 113 + COL_TARGET, 114 + COL_PIT, 115 + COL_DEEP_PIT, 116 + COL_BARREL, 117 + COL_PLAYER, 118 + COL_TEXT, 119 + COL_GRID, 120 + COL_OUTLINE, 121 + COL_HIGHLIGHT, 122 + COL_LOWLIGHT, 123 + COL_WALL, 124 + NCOLOURS 125 + }; 126 + 127 + struct game_params { 128 + int w, h; 129 + /* 130 + * FIXME: a parameter involving degree of filling in? 131 + */ 132 + }; 133 + 134 + struct game_state { 135 + game_params p; 136 + unsigned char *grid; 137 + int px, py; 138 + bool completed; 139 + }; 140 + 141 + static game_params *default_params(void) 142 + { 143 + game_params *ret = snew(game_params); 144 + 145 + ret->w = 12; 146 + ret->h = 10; 147 + 148 + return ret; 149 + } 150 + 151 + static void free_params(game_params *params) 152 + { 153 + sfree(params); 154 + } 155 + 156 + static game_params *dup_params(const game_params *params) 157 + { 158 + game_params *ret = snew(game_params); 159 + *ret = *params; /* structure copy */ 160 + return ret; 161 + } 162 + 163 + static const struct game_params sokoban_presets[] = { 164 + { 12, 10 }, 165 + { 16, 12 }, 166 + { 20, 16 }, 167 + }; 168 + 169 + static bool game_fetch_preset(int i, char **name, game_params **params) 170 + { 171 + game_params p, *ret; 172 + char *retname; 173 + char namebuf[80]; 174 + 175 + if (i < 0 || i >= lenof(sokoban_presets)) 176 + return false; 177 + 178 + p = sokoban_presets[i]; 179 + ret = dup_params(&p); 180 + sprintf(namebuf, "%dx%d", ret->w, ret->h); 181 + retname = dupstr(namebuf); 182 + 183 + *params = ret; 184 + *name = retname; 185 + return true; 186 + } 187 + 188 + static void decode_params(game_params *params, char const *string) 189 + { 190 + params->w = params->h = atoi(string); 191 + while (*string && isdigit((unsigned char)*string)) string++; 192 + if (*string == 'x') { 193 + string++; 194 + params->h = atoi(string); 195 + } 196 + } 197 + 198 + static char *encode_params(const game_params *params, bool full) 199 + { 200 + char data[256]; 201 + 202 + sprintf(data, "%dx%d", params->w, params->h); 203 + 204 + return dupstr(data); 205 + } 206 + 207 + static config_item *game_configure(const game_params *params) 208 + { 209 + config_item *ret; 210 + char buf[80]; 211 + 212 + ret = snewn(3, config_item); 213 + 214 + ret[0].name = "Width"; 215 + ret[0].type = C_STRING; 216 + sprintf(buf, "%d", params->w); 217 + ret[0].u.string.sval = dupstr(buf); 218 + 219 + ret[1].name = "Height"; 220 + ret[1].type = C_STRING; 221 + sprintf(buf, "%d", params->h); 222 + ret[1].u.string.sval = dupstr(buf); 223 + 224 + ret[2].name = NULL; 225 + ret[2].type = C_END; 226 + 227 + return ret; 228 + } 229 + 230 + static game_params *custom_params(const config_item *cfg) 231 + { 232 + game_params *ret = snew(game_params); 233 + 234 + ret->w = atoi(cfg[0].u.string.sval); 235 + ret->h = atoi(cfg[1].u.string.sval); 236 + 237 + return ret; 238 + } 239 + 240 + static const char *validate_params(const game_params *params, bool full) 241 + { 242 + if (params->w < 4 || params->h < 4) 243 + return "Width and height must both be at least 4"; 244 + 245 + return NULL; 246 + } 247 + 248 + /* ---------------------------------------------------------------------- 249 + * Game generation mechanism. 250 + * 251 + * To generate a Sokoban level, we begin with a completely blank 252 + * grid and make valid inverse moves. Grid squares can be in a 253 + * number of states. The states are: 254 + * 255 + * - INITIAL: this square has not as yet been touched by any 256 + * inverse move, which essentially means we haven't decided what 257 + * it is yet. 258 + * 259 + * - SPACE: this square is a space. 260 + * 261 + * - TARGET: this square is a space which is also the target for a 262 + * barrel. 263 + * 264 + * - BARREL: this square contains a barrel. 265 + * 266 + * - BARRELTARGET: this square contains a barrel _on_ a target. 267 + * 268 + * - WALL: this square is a wall. 269 + * 270 + * - PLAYER: this square contains the player. 271 + * 272 + * - PLAYERTARGET: this square contains the player on a target. 273 + * 274 + * We begin with every square of the in state INITIAL, apart from a 275 + * solid ring of WALLs around the edge. We randomly position the 276 + * PLAYER somewhere. Thereafter our valid moves are: 277 + * 278 + * - to move the PLAYER in one direction _pulling_ a barrel after 279 + * us. For this to work, we must have SPACE or INITIAL in the 280 + * direction we're moving, and BARREL or BARRELTARGET in the 281 + * direction we're moving away from. We leave SPACE or TARGET 282 + * respectively in the vacated square. 283 + * 284 + * - to create a new barrel by transforming an INITIAL square into 285 + * BARRELTARGET. 286 + * 287 + * - to move the PLAYER freely through SPACE and TARGET squares, 288 + * leaving SPACE or TARGET where it started. 289 + * 290 + * - to move the player through INITIAL squares, carving a tunnel 291 + * of SPACEs as it goes. 292 + * 293 + * We try to avoid destroying INITIAL squares wherever possible (if 294 + * there's a path to where we want to be using only SPACE, then we 295 + * should always use that). At the end of generation, every square 296 + * still in state INITIAL is one which was not required at any 297 + * point during generation, which means we can randomly choose 298 + * whether to make it SPACE or WALL. 299 + * 300 + * It's unclear as yet what the right strategy for wall placement 301 + * should be. Too few WALLs will yield many alternative solutions 302 + * to the puzzle, whereas too many might rule out so many 303 + * possibilities that the intended solution becomes obvious. 304 + */ 305 + 306 + static void sokoban_generate(int w, int h, unsigned char *grid, int moves, 307 + bool nethack, random_state *rs) 308 + { 309 + struct pull { 310 + int ox, oy, nx, ny, score; 311 + }; 312 + 313 + struct pull *pulls; 314 + int *dist, *prev, *heap; 315 + int x, y, px, py, i, j, d, heapsize, npulls; 316 + 317 + pulls = snewn(w * h * 4, struct pull); 318 + dist = snewn(w * h, int); 319 + prev = snewn(w * h, int); 320 + heap = snewn(w * h, int); 321 + 322 + /* 323 + * Configure the initial grid. 324 + */ 325 + for (y = 0; y < h; y++) 326 + for (x = 0; x < w; x++) 327 + grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ? 328 + WALL : INITIAL); 329 + if (nethack) 330 + grid[1] = DEEP_PIT; 331 + 332 + /* 333 + * Place the player. 334 + */ 335 + i = random_upto(rs, (w-2) * (h-2)); 336 + x = 1 + i % (w-2); 337 + y = 1 + i / (w-2); 338 + grid[y*w+x] = SPACE; 339 + px = x; 340 + py = y; 341 + 342 + /* 343 + * Now loop around making random inverse Sokoban moves. In this 344 + * loop we aim to make one actual barrel-pull per iteration, 345 + * plus as many free moves as are necessary to get into 346 + * position for that pull. 347 + */ 348 + while (moves-- >= 0) { 349 + /* 350 + * First enumerate all the viable barrel-pulls we can 351 + * possibly make, counting two pulls of the same barrel in 352 + * different directions as different. We also include pulls 353 + * we can perform by creating a new barrel. Each pull is 354 + * marked with the amount of violence it would have to do 355 + * to the grid. 356 + */ 357 + npulls = 0; 358 + for (y = 0; y < h; y++) 359 + for (x = 0; x < w; x++) 360 + for (d = 0; d < 4; d++) { 361 + int dx = DX(d); 362 + int dy = DY(d); 363 + int nx = x + dx, ny = y + dy; 364 + int npx = nx + dx, npy = ny + dy; 365 + int score = 0; 366 + 367 + /* 368 + * The candidate move is to put the player at 369 + * (nx,ny), and move him to (npx,npy), pulling 370 + * a barrel at (x,y) to (nx,ny). So first we 371 + * must check that all those squares are within 372 + * the boundaries of the grid. For this it is 373 + * sufficient to check npx,npy. 374 + */ 375 + if (npx < 0 || npx >= w || npy < 0 || npy >= h) 376 + continue; 377 + 378 + /* 379 + * (x,y) must either be a barrel, or a square 380 + * which we can convert into a barrel. 381 + */ 382 + switch (grid[y*w+x]) { 383 + case BARREL: case BARRELTARGET: 384 + break; 385 + case INITIAL: 386 + if (nethack) 387 + continue; 388 + score += 10 /* new_barrel_score */; 389 + break; 390 + case DEEP_PIT: 391 + if (!nethack) 392 + continue; 393 + break; 394 + default: 395 + continue; 396 + } 397 + 398 + /* 399 + * (nx,ny) must either be a space, or a square 400 + * which we can convert into a space. 401 + */ 402 + switch (grid[ny*w+nx]) { 403 + case SPACE: case TARGET: 404 + break; 405 + case INITIAL: 406 + score += 3 /* new_space_score */; 407 + break; 408 + default: 409 + continue; 410 + } 411 + 412 + /* 413 + * (npx,npy) must also either be a space, or a 414 + * square which we can convert into a space. 415 + */ 416 + switch (grid[npy*w+npx]) { 417 + case SPACE: case TARGET: 418 + break; 419 + case INITIAL: 420 + score += 3 /* new_space_score */; 421 + break; 422 + default: 423 + continue; 424 + } 425 + 426 + /* 427 + * That's sufficient to tag this as a possible 428 + * pull right now. We still don't know if we 429 + * can reach the required player position, but 430 + * that's a job for the subsequent BFS phase to 431 + * tell us. 432 + */ 433 + pulls[npulls].ox = x; 434 + pulls[npulls].oy = y; 435 + pulls[npulls].nx = nx; 436 + pulls[npulls].ny = ny; 437 + pulls[npulls].score = score; 438 + #ifdef GENERATION_DIAGNOSTICS 439 + printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n", 440 + pulls[npulls].ox, pulls[npulls].oy, 441 + pulls[npulls].nx, pulls[npulls].ny, 442 + pulls[npulls].score); 443 + #endif 444 + npulls++; 445 + } 446 + #ifdef GENERATION_DIAGNOSTICS 447 + printf("found %d potential pulls\n", npulls); 448 + #endif 449 + 450 + /* 451 + * If there are no pulls available at all, we give up. 452 + * 453 + * (FIXME: or perhaps backtrack?) 454 + */ 455 + if (npulls == 0) 456 + break; 457 + 458 + /* 459 + * Now we do a BFS from our current position, to find all 460 + * the squares we can get the player into. 461 + * 462 + * This BFS is unusually tricky. We want to give a positive 463 + * distance only to squares which we have to carve through 464 + * INITIALs to get to, which means we can't just stick 465 + * every square we reach on the end of our to-do list. 466 + * Instead, we must maintain our list as a proper priority 467 + * queue. 468 + */ 469 + for (i = 0; i < w*h; i++) 470 + dist[i] = prev[i] = -1; 471 + 472 + heap[0] = py*w+px; 473 + heapsize = 1; 474 + dist[py*w+px] = 0; 475 + 476 + #define PARENT(n) ( ((n)-1)/2 ) 477 + #define LCHILD(n) ( 2*(n)+1 ) 478 + #define RCHILD(n) ( 2*(n)+2 ) 479 + #define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0) 480 + 481 + while (heapsize > 0) { 482 + /* 483 + * Pull the smallest element off the heap: it's at 484 + * position 0. Move the arbitrary element from the very 485 + * end of the heap into position 0. 486 + */ 487 + y = heap[0] / w; 488 + x = heap[0] % w; 489 + 490 + heapsize--; 491 + heap[0] = heap[heapsize]; 492 + 493 + /* 494 + * Now repeatedly move that arbitrary element down the 495 + * heap by swapping it with the more suitable of its 496 + * children. 497 + */ 498 + i = 0; 499 + while (1) { 500 + int lc, rc; 501 + 502 + lc = LCHILD(i); 503 + rc = RCHILD(i); 504 + 505 + if (lc >= heapsize) 506 + break; /* we've hit bottom */ 507 + 508 + if (rc >= heapsize) { 509 + /* 510 + * Special case: there is only one child to 511 + * check. 512 + */ 513 + if (dist[heap[i]] > dist[heap[lc]]) 514 + SWAP(heap[i], heap[lc]); 515 + 516 + /* _Now_ we've hit bottom. */ 517 + break; 518 + } else { 519 + /* 520 + * The common case: there are two children and 521 + * we must check them both. 522 + */ 523 + if (dist[heap[i]] > dist[heap[lc]] || 524 + dist[heap[i]] > dist[heap[rc]]) { 525 + /* 526 + * Pick the more appropriate child to swap with 527 + * (i.e. the one which would want to be the 528 + * parent if one were above the other - as one 529 + * is about to be). 530 + */ 531 + if (dist[heap[lc]] > dist[heap[rc]]) { 532 + SWAP(heap[i], heap[rc]); 533 + i = rc; 534 + } else { 535 + SWAP(heap[i], heap[lc]); 536 + i = lc; 537 + } 538 + } else { 539 + /* This element is in the right place; we're done. */ 540 + break; 541 + } 542 + } 543 + } 544 + 545 + /* 546 + * OK, that's given us (x,y) for this phase of the 547 + * search. Now try all directions from here. 548 + */ 549 + 550 + for (d = 0; d < 4; d++) { 551 + int dx = DX(d); 552 + int dy = DY(d); 553 + int nx = x + dx, ny = y + dy; 554 + if (nx < 0 || nx >= w || ny < 0 || ny >= h) 555 + continue; 556 + if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET && 557 + grid[ny*w+nx] != INITIAL) 558 + continue; 559 + if (dist[ny*w+nx] == -1) { 560 + dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL); 561 + prev[ny*w+nx] = y*w+x; 562 + 563 + /* 564 + * Now insert ny*w+nx at the end of the heap, 565 + * and move it down to its appropriate resting 566 + * place. 567 + */ 568 + i = heapsize; 569 + heap[heapsize++] = ny*w+nx; 570 + 571 + /* 572 + * Swap element n with its parent repeatedly to 573 + * preserve the heap property. 574 + */ 575 + 576 + while (i > 0) { 577 + int p = PARENT(i); 578 + 579 + if (dist[heap[p]] > dist[heap[i]]) { 580 + SWAP(heap[p], heap[i]); 581 + i = p; 582 + } else 583 + break; 584 + } 585 + } 586 + } 587 + } 588 + 589 + #undef PARENT 590 + #undef LCHILD 591 + #undef RCHILD 592 + #undef SWAP 593 + 594 + #ifdef GENERATION_DIAGNOSTICS 595 + printf("distance map:\n"); 596 + for (i = 0; i < h; i++) { 597 + for (j = 0; j < w; j++) { 598 + int d = dist[i*w+j]; 599 + int c; 600 + if (d < 0) 601 + c = '#'; 602 + else if (d >= 36) 603 + c = '!'; 604 + else if (d >= 10) 605 + c = 'A' - 10 + d; 606 + else 607 + c = '0' + d; 608 + putchar(c); 609 + } 610 + putchar('\n'); 611 + } 612 + #endif 613 + 614 + /* 615 + * Now we can go back through the `pulls' array, adjusting 616 + * the score for each pull depending on how hard it is to 617 + * reach its starting point, and also throwing out any 618 + * whose starting points are genuinely unreachable even 619 + * with the possibility of carving through INITIAL squares. 620 + */ 621 + for (i = j = 0; i < npulls; i++) { 622 + #ifdef GENERATION_DIAGNOSTICS 623 + printf("potential pull (%d,%d)-(%d,%d)", 624 + pulls[i].ox, pulls[i].oy, 625 + pulls[i].nx, pulls[i].ny); 626 + #endif 627 + x = pulls[i].nx; 628 + y = pulls[i].ny; 629 + if (dist[y*w+x] < 0) { 630 + #ifdef GENERATION_DIAGNOSTICS 631 + printf(" unreachable\n"); 632 + #endif 633 + continue; /* this pull isn't feasible at all */ 634 + } else { 635 + /* 636 + * Another nasty special case we have to check is 637 + * whether the initial barrel location (ox,oy) is 638 + * on the path used to reach the square. This can 639 + * occur if that square is in state INITIAL: the 640 + * pull is initially considered valid on the basis 641 + * that the INITIAL can become BARRELTARGET, and 642 + * it's also considered reachable on the basis that 643 + * INITIAL can be turned into SPACE, but it can't 644 + * be both at once. 645 + * 646 + * Fortunately, if (ox,oy) is on the path at all, 647 + * it must be only one space from the end, so this 648 + * is easy to spot and rule out. 649 + */ 650 + if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) { 651 + #ifdef GENERATION_DIAGNOSTICS 652 + printf(" goes through itself\n"); 653 + #endif 654 + continue; /* this pull isn't feasible at all */ 655 + } 656 + pulls[j] = pulls[i]; /* structure copy */ 657 + pulls[j].score += dist[y*w+x] * 3 /* new_space_score */; 658 + #ifdef GENERATION_DIAGNOSTICS 659 + printf(" reachable at distance %d (cost now %d)\n", 660 + dist[y*w+x], pulls[j].score); 661 + #endif 662 + j++; 663 + } 664 + } 665 + npulls = j; 666 + 667 + /* 668 + * Again, if there are no pulls available at all, we give 669 + * up. 670 + * 671 + * (FIXME: or perhaps backtrack?) 672 + */ 673 + if (npulls == 0) 674 + break; 675 + 676 + /* 677 + * Now choose which pull to make. On the one hand we should 678 + * prefer pulls which do less damage to the INITIAL squares 679 + * (thus, ones for which we can already get into position 680 + * via existing SPACEs, and for which the barrel already 681 + * exists and doesn't have to be invented); on the other, 682 + * we want to avoid _always_ preferring such pulls, on the 683 + * grounds that that will lead to levels without very much 684 + * stuff in. 685 + * 686 + * When creating new barrels, we prefer creations which are 687 + * next to existing TARGET squares. 688 + * 689 + * FIXME: for the moment I'll make this very simple indeed. 690 + */ 691 + i = random_upto(rs, npulls); 692 + 693 + /* 694 + * Actually make the pull, including carving a path to get 695 + * to the site if necessary. 696 + */ 697 + x = pulls[i].nx; 698 + y = pulls[i].ny; 699 + while (prev[y*w+x] >= 0) { 700 + int p; 701 + 702 + if (grid[y*w+x] == INITIAL) 703 + grid[y*w+x] = SPACE; 704 + 705 + p = prev[y*w+x]; 706 + y = p / w; 707 + x = p % w; 708 + } 709 + px = 2*pulls[i].nx - pulls[i].ox; 710 + py = 2*pulls[i].ny - pulls[i].oy; 711 + if (grid[py*w+px] == INITIAL) 712 + grid[py*w+px] = SPACE; 713 + if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET) 714 + grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET; 715 + else 716 + grid[pulls[i].ny*w+pulls[i].nx] = BARREL; 717 + if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL) 718 + grid[pulls[i].oy*w+pulls[i].ox] = SPACE; 719 + else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT) 720 + grid[pulls[i].oy*w+pulls[i].ox] = TARGET; 721 + } 722 + 723 + sfree(heap); 724 + sfree(prev); 725 + sfree(dist); 726 + sfree(pulls); 727 + 728 + if (grid[py*w+px] == TARGET) 729 + grid[py*w+px] = PLAYERTARGET; 730 + else 731 + grid[py*w+px] = PLAYER; 732 + } 733 + 734 + static char *new_game_desc(const game_params *params, random_state *rs, 735 + char **aux, bool interactive) 736 + { 737 + int w = params->w, h = params->h; 738 + char *desc; 739 + int desclen, descpos, descsize, prev, count; 740 + unsigned char *grid; 741 + int i, j; 742 + 743 + /* 744 + * FIXME: perhaps some more interesting means of choosing how 745 + * many moves to try? 746 + */ 747 + grid = snewn(w*h, unsigned char); 748 + sokoban_generate(w, h, grid, w*h, false, rs); 749 + 750 + desclen = descpos = descsize = 0; 751 + desc = NULL; 752 + prev = -1; 753 + count = 0; 754 + for (i = 0; i < w*h; i++) { 755 + if (descsize < desclen + 40) { 756 + descsize = desclen + 100; 757 + desc = sresize(desc, descsize, char); 758 + desc[desclen] = '\0'; 759 + } 760 + switch (grid[i]) { 761 + case INITIAL: 762 + j = 'w'; /* FIXME: make some of these 's'? */ 763 + break; 764 + case SPACE: 765 + j = 's'; 766 + break; 767 + case WALL: 768 + j = 'w'; 769 + break; 770 + case TARGET: 771 + j = 't'; 772 + break; 773 + case BARREL: 774 + j = 'b'; 775 + break; 776 + case BARRELTARGET: 777 + j = 'f'; 778 + break; 779 + case DEEP_PIT: 780 + j = 'd'; 781 + break; 782 + case PLAYER: 783 + j = 'u'; 784 + break; 785 + case PLAYERTARGET: 786 + j = 'v'; 787 + break; 788 + default: 789 + j = '?'; 790 + break; 791 + } 792 + assert(j != '?'); 793 + if (j != prev) { 794 + desc[desclen++] = j; 795 + descpos = desclen; 796 + prev = j; 797 + count = 1; 798 + } else { 799 + count++; 800 + desclen = descpos + sprintf(desc+descpos, "%d", count); 801 + } 802 + } 803 + 804 + sfree(grid); 805 + 806 + return desc; 807 + } 808 + 809 + static const char *validate_desc(const game_params *params, const char *desc) 810 + { 811 + int w = params->w, h = params->h; 812 + int area = 0; 813 + int nplayers = 0; 814 + 815 + while (*desc) { 816 + int c = *desc++; 817 + int n = 1; 818 + if (*desc && isdigit((unsigned char)*desc)) { 819 + n = atoi(desc); 820 + while (*desc && isdigit((unsigned char)*desc)) desc++; 821 + } 822 + 823 + area += n; 824 + 825 + if (c == PLAYER || c == PLAYERTARGET) 826 + nplayers += n; 827 + else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET || 828 + c == PIT || c == DEEP_PIT || IS_BARREL(c)) 829 + /* ok */; 830 + else 831 + return "Invalid character in game description"; 832 + } 833 + 834 + if (area > w*h) 835 + return "Too much data in game description"; 836 + if (area < w*h) 837 + return "Too little data in game description"; 838 + if (nplayers < 1) 839 + return "No starting player position specified"; 840 + if (nplayers > 1) 841 + return "More than one starting player position specified"; 842 + 843 + return NULL; 844 + } 845 + 846 + static game_state *new_game(midend *me, const game_params *params, 847 + const char *desc) 848 + { 849 + int w = params->w, h = params->h; 850 + game_state *state = snew(game_state); 851 + int i; 852 + 853 + state->p = *params; /* structure copy */ 854 + state->grid = snewn(w*h, unsigned char); 855 + state->px = state->py = -1; 856 + state->completed = false; 857 + 858 + i = 0; 859 + 860 + while (*desc) { 861 + int c = *desc++; 862 + int n = 1; 863 + if (*desc && isdigit((unsigned char)*desc)) { 864 + n = atoi(desc); 865 + while (*desc && isdigit((unsigned char)*desc)) desc++; 866 + } 867 + 868 + if (c == PLAYER || c == PLAYERTARGET) { 869 + state->py = i / w; 870 + state->px = i % w; 871 + c = IS_ON_TARGET(c) ? TARGET : SPACE; 872 + } 873 + 874 + while (n-- > 0) 875 + state->grid[i++] = c; 876 + } 877 + 878 + assert(i == w*h); 879 + assert(state->px != -1 && state->py != -1); 880 + 881 + return state; 882 + } 883 + 884 + static game_state *dup_game(const game_state *state) 885 + { 886 + int w = state->p.w, h = state->p.h; 887 + game_state *ret = snew(game_state); 888 + 889 + ret->p = state->p; /* structure copy */ 890 + ret->grid = snewn(w*h, unsigned char); 891 + memcpy(ret->grid, state->grid, w*h); 892 + ret->px = state->px; 893 + ret->py = state->py; 894 + ret->completed = state->completed; 895 + 896 + return ret; 897 + } 898 + 899 + static void free_game(game_state *state) 900 + { 901 + sfree(state->grid); 902 + sfree(state); 903 + } 904 + 905 + static char *solve_game(const game_state *state, const game_state *currstate, 906 + const char *aux, const char **error) 907 + { 908 + return NULL; 909 + } 910 + 911 + static bool game_can_format_as_text_now(const game_params *params) 912 + { 913 + return true; 914 + } 915 + 916 + static char *game_text_format(const game_state *state) 917 + { 918 + return NULL; 919 + } 920 + 921 + static game_ui *new_ui(const game_state *state) 922 + { 923 + return NULL; 924 + } 925 + 926 + static void free_ui(game_ui *ui) 927 + { 928 + } 929 + 930 + static void game_changed_state(game_ui *ui, const game_state *oldstate, 931 + const game_state *newstate) 932 + { 933 + } 934 + 935 + struct game_drawstate { 936 + game_params p; 937 + int tilesize; 938 + bool started; 939 + unsigned short *grid; 940 + }; 941 + 942 + #define PREFERRED_TILESIZE 32 943 + #define TILESIZE (ds->tilesize) 944 + #define BORDER (TILESIZE) 945 + #define HIGHLIGHT_WIDTH (TILESIZE / 10) 946 + #define COORD(x) ( (x) * TILESIZE + BORDER ) 947 + #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) 948 + 949 + /* 950 + * I'm going to need to do most of the move-type analysis in both 951 + * interpret_move and execute_move, so I'll abstract it out into a 952 + * subfunction. move_type() returns -1 for an illegal move, 0 for a 953 + * movement, and 1 for a push. 954 + */ 955 + static int move_type(const game_state *state, int dx, int dy) 956 + { 957 + int w = state->p.w, h = state->p.h; 958 + int px = state->px, py = state->py; 959 + int nx, ny, nbx, nby; 960 + 961 + assert(dx >= -1 && dx <= +1); 962 + assert(dy >= -1 && dy <= +1); 963 + assert(dx || dy); 964 + 965 + nx = px + dx; 966 + ny = py + dy; 967 + 968 + /* 969 + * Disallow any move that goes off the grid. 970 + */ 971 + if (nx < 0 || nx >= w || ny < 0 || ny >= h) 972 + return -1; 973 + 974 + /* 975 + * Examine the target square of the move to see whether it's a 976 + * space, a barrel, or a wall. 977 + */ 978 + 979 + if (state->grid[ny*w+nx] == WALL || 980 + state->grid[ny*w+nx] == PIT || 981 + state->grid[ny*w+nx] == DEEP_PIT) 982 + return -1; /* this one's easy; just disallow it */ 983 + 984 + if (IS_BARREL(state->grid[ny*w+nx])) { 985 + /* 986 + * This is a push move. For a start, that means it must not 987 + * be diagonal. 988 + */ 989 + if (dy && dx) 990 + return -1; 991 + 992 + /* 993 + * Now find the location of the third square involved in 994 + * the push, and stop if it's off the edge. 995 + */ 996 + nbx = nx + dx; 997 + nby = ny + dy; 998 + if (nbx < 0 || nbx >= w || nby < 0 || nby >= h) 999 + return -1; 1000 + 1001 + /* 1002 + * That third square must be able to accept a barrel. 1003 + */ 1004 + if (state->grid[nby*w+nbx] == SPACE || 1005 + state->grid[nby*w+nbx] == TARGET || 1006 + state->grid[nby*w+nbx] == PIT || 1007 + state->grid[nby*w+nbx] == DEEP_PIT) { 1008 + /* 1009 + * The push is valid. 1010 + */ 1011 + return 1; 1012 + } else { 1013 + return -1; 1014 + } 1015 + } else { 1016 + /* 1017 + * This is just an ordinary move. We've already checked the 1018 + * target square, so the only thing left to check is that a 1019 + * diagonal move has a space on one side to have notionally 1020 + * gone through. 1021 + */ 1022 + if (dx && dy && 1023 + state->grid[(py+dy)*w+px] != SPACE && 1024 + state->grid[(py+dy)*w+px] != TARGET && 1025 + state->grid[py*w+(px+dx)] != SPACE && 1026 + state->grid[py*w+(px+dx)] != TARGET) 1027 + return -1; 1028 + 1029 + /* 1030 + * Otherwise, the move is valid. 1031 + */ 1032 + return 0; 1033 + } 1034 + } 1035 + 1036 + static char *interpret_move(const game_state *state, game_ui *ui, 1037 + const game_drawstate *ds, 1038 + int x, int y, int button) 1039 + { 1040 + int dx=0, dy=0; 1041 + char *move; 1042 + 1043 + /* 1044 + * Diagonal movement is supported as it is in NetHack: it's 1045 + * for movement only (never pushing), and one of the two 1046 + * squares adjacent to both the source and destination 1047 + * squares must be free to move through. In other words, it 1048 + * is only a shorthand for two orthogonal moves and cannot 1049 + * change the nature of the actual puzzle game. 1050 + */ 1051 + if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8')) 1052 + dx = 0, dy = -1; 1053 + else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2')) 1054 + dx = 0, dy = +1; 1055 + else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4')) 1056 + dx = -1, dy = 0; 1057 + else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6')) 1058 + dx = +1, dy = 0; 1059 + else if (button == (MOD_NUM_KEYPAD | '7')) 1060 + dx = -1, dy = -1; 1061 + else if (button == (MOD_NUM_KEYPAD | '9')) 1062 + dx = +1, dy = -1; 1063 + else if (button == (MOD_NUM_KEYPAD | '1')) 1064 + dx = -1, dy = +1; 1065 + else if (button == (MOD_NUM_KEYPAD | '3')) 1066 + dx = +1, dy = +1; 1067 + else if (button == LEFT_BUTTON) 1068 + { 1069 + if(x < COORD(state->px)) 1070 + dx = -1; 1071 + else if (x > COORD(state->px + 1)) 1072 + dx = 1; 1073 + if(y < COORD(state->py)) 1074 + dy = -1; 1075 + else if (y > COORD(state->py + 1)) 1076 + dy = 1; 1077 + } 1078 + else 1079 + return NULL; 1080 + 1081 + if((dx == 0) && (dy == 0)) 1082 + return(NULL); 1083 + 1084 + if (move_type(state, dx, dy) < 0) 1085 + return NULL; 1086 + 1087 + move = snewn(2, char); 1088 + move[1] = '\0'; 1089 + move[0] = '5' - 3*dy + dx; 1090 + return move; 1091 + } 1092 + 1093 + static game_state *execute_move(const game_state *state, const char *move) 1094 + { 1095 + int w = state->p.w, h = state->p.h; 1096 + int px = state->px, py = state->py; 1097 + int dx, dy, nx, ny, nbx, nby, type, m, i; 1098 + bool freebarrels, freetargets; 1099 + game_state *ret; 1100 + 1101 + if (*move < '1' || *move == '5' || *move > '9' || move[1]) 1102 + return NULL; /* invalid move string */ 1103 + 1104 + m = *move - '0'; 1105 + dx = (m + 2) % 3 - 1; 1106 + dy = 2 - (m + 2) / 3; 1107 + type = move_type(state, dx, dy); 1108 + if (type < 0) 1109 + return NULL; 1110 + 1111 + ret = dup_game(state); 1112 + 1113 + nx = px + dx; 1114 + ny = py + dy; 1115 + nbx = nx + dx; 1116 + nby = ny + dy; 1117 + 1118 + if (type) { 1119 + int b; 1120 + 1121 + /* 1122 + * Push. 1123 + */ 1124 + b = ret->grid[ny*w+nx]; 1125 + if (IS_ON_TARGET(b)) { 1126 + ret->grid[ny*w+nx] = TARGET; 1127 + b = DETARGETISE(b); 1128 + } else 1129 + ret->grid[ny*w+nx] = SPACE; 1130 + 1131 + if (ret->grid[nby*w+nbx] == PIT) 1132 + ret->grid[nby*w+nbx] = SPACE; 1133 + else if (ret->grid[nby*w+nbx] == DEEP_PIT) 1134 + /* do nothing - the pit eats the barrel and remains there */; 1135 + else if (ret->grid[nby*w+nbx] == TARGET) 1136 + ret->grid[nby*w+nbx] = TARGETISE(b); 1137 + else 1138 + ret->grid[nby*w+nbx] = b; 1139 + } 1140 + 1141 + ret->px = nx; 1142 + ret->py = ny; 1143 + 1144 + /* 1145 + * Check for completion. This is surprisingly complicated, 1146 + * given the presence of pits and deep pits, and also the fact 1147 + * that some Sokoban levels with pits have fewer pits than 1148 + * barrels (due to providing spares, e.g. NetHack's). I think 1149 + * the completion condition in fact must be that the game 1150 + * cannot become any _more_ complete. That is, _either_ there 1151 + * are no remaining barrels not on targets, _or_ there is a 1152 + * good reason why any such barrels cannot be placed. The only 1153 + * available good reason is that there are no remaining pits, 1154 + * no free target squares, and no deep pits at all. 1155 + */ 1156 + if (!ret->completed) { 1157 + freebarrels = false; 1158 + freetargets = false; 1159 + for (i = 0; i < w*h; i++) { 1160 + int v = ret->grid[i]; 1161 + 1162 + if (IS_BARREL(v) && !IS_ON_TARGET(v)) 1163 + freebarrels = true; 1164 + if (v == DEEP_PIT || v == PIT || 1165 + (!IS_BARREL(v) && IS_ON_TARGET(v))) 1166 + freetargets = true; 1167 + } 1168 + 1169 + if (!freebarrels || !freetargets) 1170 + ret->completed = true; 1171 + } 1172 + 1173 + return ret; 1174 + } 1175 + 1176 + /* ---------------------------------------------------------------------- 1177 + * Drawing routines. 1178 + */ 1179 + 1180 + static void game_compute_size(const game_params *params, int tilesize, 1181 + const game_ui *ui, int *x, int *y) 1182 + { 1183 + /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 1184 + struct { int tilesize; } ads, *ds = &ads; 1185 + ads.tilesize = tilesize; 1186 + 1187 + *x = 2 * BORDER + 1 + params->w * TILESIZE; 1188 + *y = 2 * BORDER + 1 + params->h * TILESIZE; 1189 + } 1190 + 1191 + static void game_set_size(drawing *dr, game_drawstate *ds, 1192 + const game_params *params, int tilesize) 1193 + { 1194 + ds->tilesize = tilesize; 1195 + } 1196 + 1197 + static float *game_colours(frontend *fe, int *ncolours) 1198 + { 1199 + float *ret = snewn(3 * NCOLOURS, float); 1200 + int i; 1201 + 1202 + game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 1203 + 1204 + ret[COL_OUTLINE * 3 + 0] = 0.0F; 1205 + ret[COL_OUTLINE * 3 + 1] = 0.0F; 1206 + ret[COL_OUTLINE * 3 + 2] = 0.0F; 1207 + 1208 + ret[COL_PLAYER * 3 + 0] = 0.0F; 1209 + ret[COL_PLAYER * 3 + 1] = 1.0F; 1210 + ret[COL_PLAYER * 3 + 2] = 0.0F; 1211 + 1212 + ret[COL_BARREL * 3 + 0] = 0.6F; 1213 + ret[COL_BARREL * 3 + 1] = 0.3F; 1214 + ret[COL_BARREL * 3 + 2] = 0.0F; 1215 + 1216 + ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; 1217 + ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; 1218 + ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; 1219 + 1220 + ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2; 1221 + ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2; 1222 + ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2; 1223 + 1224 + ret[COL_DEEP_PIT * 3 + 0] = 0.0F; 1225 + ret[COL_DEEP_PIT * 3 + 1] = 0.0F; 1226 + ret[COL_DEEP_PIT * 3 + 2] = 0.0F; 1227 + 1228 + ret[COL_TEXT * 3 + 0] = 1.0F; 1229 + ret[COL_TEXT * 3 + 1] = 1.0F; 1230 + ret[COL_TEXT * 3 + 2] = 1.0F; 1231 + 1232 + ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; 1233 + ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; 1234 + ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; 1235 + 1236 + ret[COL_OUTLINE * 3 + 0] = 0.0F; 1237 + ret[COL_OUTLINE * 3 + 1] = 0.0F; 1238 + ret[COL_OUTLINE * 3 + 2] = 0.0F; 1239 + 1240 + for (i = 0; i < 3; i++) { 1241 + ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] + 1242 + 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4; 1243 + } 1244 + 1245 + *ncolours = NCOLOURS; 1246 + return ret; 1247 + } 1248 + 1249 + static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 1250 + { 1251 + int w = state->p.w, h = state->p.h; 1252 + struct game_drawstate *ds = snew(struct game_drawstate); 1253 + int i; 1254 + 1255 + ds->tilesize = 0; 1256 + ds->p = state->p; /* structure copy */ 1257 + ds->grid = snewn(w*h, unsigned short); 1258 + for (i = 0; i < w*h; i++) 1259 + ds->grid[i] = INVALID; 1260 + ds->started = false; 1261 + 1262 + return ds; 1263 + } 1264 + 1265 + static void game_free_drawstate(drawing *dr, game_drawstate *ds) 1266 + { 1267 + sfree(ds->grid); 1268 + sfree(ds); 1269 + } 1270 + 1271 + static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) 1272 + { 1273 + int tx = COORD(x), ty = COORD(y); 1274 + int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND); 1275 + 1276 + v &= 0xFF; 1277 + 1278 + clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1); 1279 + draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg); 1280 + 1281 + if (v == WALL) { 1282 + int coords[6]; 1283 + 1284 + coords[0] = tx + TILESIZE; 1285 + coords[1] = ty + TILESIZE; 1286 + coords[2] = tx + TILESIZE; 1287 + coords[3] = ty + 1; 1288 + coords[4] = tx + 1; 1289 + coords[5] = ty + TILESIZE; 1290 + draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); 1291 + 1292 + coords[0] = tx + 1; 1293 + coords[1] = ty + 1; 1294 + draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); 1295 + 1296 + draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH, 1297 + TILESIZE - 2*HIGHLIGHT_WIDTH, 1298 + TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL); 1299 + } else if (v == PIT) { 1300 + draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1301 + TILESIZE*3/7, COL_PIT, COL_OUTLINE); 1302 + } else if (v == DEEP_PIT) { 1303 + draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1304 + TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE); 1305 + } else { 1306 + if (IS_ON_TARGET(v)) { 1307 + draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1308 + TILESIZE*3/7, COL_TARGET, COL_OUTLINE); 1309 + } 1310 + if (IS_PLAYER(v)) { 1311 + draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1312 + TILESIZE/3, COL_PLAYER, COL_OUTLINE); 1313 + } else if (IS_BARREL(v)) { 1314 + char str[2]; 1315 + 1316 + draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1317 + TILESIZE/3, COL_BARREL, COL_OUTLINE); 1318 + str[1] = '\0'; 1319 + str[0] = BARREL_LABEL(v); 1320 + if (str[0]) { 1321 + draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, 1322 + FONT_VARIABLE, TILESIZE/2, 1323 + ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); 1324 + } 1325 + } 1326 + } 1327 + 1328 + unclip(dr); 1329 + draw_update(dr, tx, ty, TILESIZE, TILESIZE); 1330 + } 1331 + 1332 + static void game_redraw(drawing *dr, game_drawstate *ds, 1333 + const game_state *oldstate, const game_state *state, 1334 + int dir, const game_ui *ui, 1335 + float animtime, float flashtime) 1336 + { 1337 + int w = state->p.w, h = state->p.h /*, wh = w*h */; 1338 + int x, y; 1339 + int flashtype; 1340 + 1341 + if (flashtime && 1342 + !((int)(flashtime * 3 / FLASH_LENGTH) % 2)) 1343 + flashtype = 0x100; 1344 + else 1345 + flashtype = 0; 1346 + 1347 + /* 1348 + * Initialise a fresh drawstate. 1349 + */ 1350 + if (!ds->started) { 1351 + /* 1352 + * Draw the grid lines. 1353 + */ 1354 + for (y = 0; y <= h; y++) 1355 + draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), 1356 + COL_LOWLIGHT); 1357 + for (x = 0; x <= w; x++) 1358 + draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), 1359 + COL_LOWLIGHT); 1360 + 1361 + ds->started = true; 1362 + } 1363 + 1364 + /* 1365 + * Draw the grid contents. 1366 + */ 1367 + for (y = 0; y < h; y++) 1368 + for (x = 0; x < w; x++) { 1369 + int v = state->grid[y*w+x]; 1370 + if (y == state->py && x == state->px) { 1371 + if (v == TARGET) 1372 + v = PLAYERTARGET; 1373 + else { 1374 + assert(v == SPACE); 1375 + v = PLAYER; 1376 + } 1377 + } 1378 + 1379 + v |= flashtype; 1380 + 1381 + if (ds->grid[y*w+x] != v) { 1382 + draw_tile(dr, ds, x, y, v); 1383 + ds->grid[y*w+x] = v; 1384 + } 1385 + } 1386 + 1387 + } 1388 + 1389 + static float game_anim_length(const game_state *oldstate, 1390 + const game_state *newstate, int dir, game_ui *ui) 1391 + { 1392 + return 0.0F; 1393 + } 1394 + 1395 + static float game_flash_length(const game_state *oldstate, 1396 + const game_state *newstate, int dir, game_ui *ui) 1397 + { 1398 + if (!oldstate->completed && newstate->completed) 1399 + return FLASH_LENGTH; 1400 + else 1401 + return 0.0F; 1402 + } 1403 + 1404 + static void game_get_cursor_location(const game_ui *ui, 1405 + const game_drawstate *ds, 1406 + const game_state *state, 1407 + const game_params *params, 1408 + int *x, int *y, int *w, int *h) 1409 + { 1410 + } 1411 + 1412 + static int game_status(const game_state *state) 1413 + { 1414 + return state->completed ? +1 : 0; 1415 + } 1416 + 1417 + static bool game_timing_state(const game_state *state, game_ui *ui) 1418 + { 1419 + return true; 1420 + } 1421 + 1422 + static void game_print_size(const game_params *params, const game_ui *ui, 1423 + float *x, float *y) 1424 + { 1425 + } 1426 + 1427 + static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 1428 + int tilesize) 1429 + { 1430 + } 1431 + 1432 + #ifdef COMBINED 1433 + #define thegame sokoban 1434 + #endif 1435 + 1436 + const struct game thegame = { 1437 + "Sokoban", NULL, NULL, 1438 + default_params, 1439 + game_fetch_preset, NULL, 1440 + decode_params, 1441 + encode_params, 1442 + free_params, 1443 + dup_params, 1444 + true, game_configure, custom_params, 1445 + validate_params, 1446 + new_game_desc, 1447 + validate_desc, 1448 + new_game, 1449 + dup_game, 1450 + free_game, 1451 + false, solve_game, 1452 + false, game_can_format_as_text_now, game_text_format, 1453 + NULL, NULL, /* get_prefs, set_prefs */ 1454 + new_ui, 1455 + free_ui, 1456 + NULL, /* encode_ui */ 1457 + NULL, /* decode_ui */ 1458 + NULL, /* game_request_keys */ 1459 + game_changed_state, 1460 + NULL, /* current_key_label */ 1461 + interpret_move, 1462 + execute_move, 1463 + PREFERRED_TILESIZE, game_compute_size, game_set_size, 1464 + game_colours, 1465 + game_new_drawstate, 1466 + game_free_drawstate, 1467 + game_redraw, 1468 + game_anim_length, 1469 + game_flash_length, 1470 + game_get_cursor_location, 1471 + game_status, 1472 + false, false, game_print_size, game_print, 1473 + false, /* wants_statusbar */ 1474 + false, game_timing_state, 1475 + 0, /* flags */ 1476 + };