New puzzle: `Slant', picked from the Japanese-language section of

nikoli.co.jp (which has quite a few puzzles that they don't seem to
have bothered to translate into English).

Minor structural change: the disjoint set forest code used in the
Net solver has come in handy again, so I've moved it out into its
own module dsf.c.

[originally from svn r6155]
This commit is contained in:
Simon Tatham
2005-08-02 23:16:46 +00:00
parent 207c847553
commit afe80030e4
8 changed files with 1413 additions and 36 deletions

9
Recipe
View File

@ -15,15 +15,16 @@
WINDOWS = windows user32.lib gdi32.lib comctl32.lib comdlg32.lib WINDOWS = windows user32.lib gdi32.lib comctl32.lib comdlg32.lib
COMMON = midend misc malloc random version COMMON = midend misc malloc random version
NET = net tree234 NET = net tree234 dsf
NETSLIDE = netslide tree234 NETSLIDE = netslide tree234
MINES = mines tree234 MINES = mines tree234
FLIP = flip tree234 FLIP = flip tree234
PEGS = pegs tree234 PEGS = pegs tree234
UNTANGLE = untangle tree234 UNTANGLE = untangle tree234
SLANT = slant dsf
ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle ALL = list NET NETSLIDE cube fifteen sixteen rect pattern solo twiddle
+ MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox + MINES samegame FLIP guess PEGS dominosa UNTANGLE blackbox SLANT
net : [X] gtk COMMON NET net : [X] gtk COMMON NET
netslide : [X] gtk COMMON NETSLIDE netslide : [X] gtk COMMON NETSLIDE
@ -42,6 +43,7 @@ pegs : [X] gtk COMMON PEGS
dominosa : [X] gtk COMMON dominosa dominosa : [X] gtk COMMON dominosa
untangle : [X] gtk COMMON UNTANGLE untangle : [X] gtk COMMON UNTANGLE
blackbox : [X] gtk COMMON blackbox blackbox : [X] gtk COMMON blackbox
slant : [X] gtk COMMON SLANT
# Auxiliary command-line programs. # Auxiliary command-line programs.
solosolver : [U] solo[STANDALONE_SOLVER] malloc solosolver : [U] solo[STANDALONE_SOLVER] malloc
@ -71,6 +73,7 @@ pegs : [G] WINDOWS COMMON PEGS
dominosa : [G] WINDOWS COMMON dominosa dominosa : [G] WINDOWS COMMON dominosa
untangle : [G] WINDOWS COMMON UNTANGLE untangle : [G] WINDOWS COMMON UNTANGLE
blackbox : [G] WINDOWS COMMON blackbox blackbox : [G] WINDOWS COMMON blackbox
slant : [G] WINDOWS COMMON SLANT
# Mac OS X unified application containing all the puzzles. # Mac OS X unified application containing all the puzzles.
Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
@ -162,7 +165,7 @@ FORCE:
install: install:
for i in cube net netslide fifteen sixteen twiddle \ for i in cube net netslide fifteen sixteen twiddle \
pattern rect solo mines samegame flip guess \ pattern rect solo mines samegame flip guess \
pegs dominosa untangle blackbox; do \ pegs dominosa untangle blackbox slant; do \
$(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i; \ $(INSTALL_PROGRAM) -m 755 $$i $(DESTDIR)$(gamesdir)/$$i; \
done done
!end !end

28
dsf.c Normal file
View File

@ -0,0 +1,28 @@
/*
* dsf.c: two small functions to handle a disjoint set forest,
* which is a data structure useful in any solver which has to
* worry about avoiding closed loops.
*/
int dsf_canonify(int *dsf, int val)
{
int v2 = val;
while (dsf[val] != val)
val = dsf[val];
while (v2 != val) {
int tmp = dsf[v2];
dsf[v2] = val;
v2 = tmp;
}
return val;
}
void dsf_merge(int *dsf, int v1, int v2)
{
v1 = dsf_canonify(dsf, v1);
v2 = dsf_canonify(dsf, v2);
dsf[v2] = v1;
}

2
list.c
View File

@ -31,6 +31,7 @@ extern const game pegs;
extern const game rect; extern const game rect;
extern const game samegame; extern const game samegame;
extern const game sixteen; extern const game sixteen;
extern const game slant;
extern const game solo; extern const game solo;
extern const game twiddle; extern const game twiddle;
extern const game untangle; extern const game untangle;
@ -50,6 +51,7 @@ const game *gamelist[] = {
&rect, &rect,
&samegame, &samegame,
&sixteen, &sixteen,
&slant,
&solo, &solo,
&twiddle, &twiddle,
&untangle, &untangle,

23
net.c
View File

@ -382,29 +382,6 @@ static char *validate_params(game_params *params, int full)
* avoidance is required. * avoidance is required.
*/ */
static int dsf_canonify(int *dsf, int val)
{
int v2 = val;
while (dsf[val] != val)
val = dsf[val];
while (v2 != val) {
int tmp = dsf[v2];
dsf[v2] = val;
v2 = tmp;
}
return val;
}
static void dsf_merge(int *dsf, int v1, int v2)
{
v1 = dsf_canonify(dsf, v1);
v2 = dsf_canonify(dsf, v2);
dsf[v2] = v1;
}
struct todo { struct todo {
unsigned char *marked; unsigned char *marked;
int *buffer; int *buffer;

View File

@ -365,13 +365,67 @@ def dominosa_format(s):
((x+0.5)*gridpitch, (h-y-0.5)*gridpitch, grid[y*w+x])) ((x+0.5)*gridpitch, (h-y-0.5)*gridpitch, grid[y*w+x]))
return ret.coords, ret.s return ret.coords, ret.s
def slant_format(s):
# Parse the game ID.
ret = Holder()
ret.s = ""
params, seed = string.split(s, ":")
w, h = map(string.atoi, string.split(params, "x"))
W = w+1
H = h+1
grid = []
while len(seed) > 0:
if seed[0] in string.lowercase:
grid.extend([-1] * (ord(seed[0]) - ord('a') + 1))
seed = seed[1:]
elif seed[0] in "01234":
grid.append(string.atoi(seed[0]))
seed = seed[1:]
assert W * H == len(grid)
# I'm going to arbitrarily choose to use 7pt text for the
# numbers, and a 14pt grid pitch.
textht = 7
gridpitch = 14
radius = textht * 2.0 / 3.0
# Set up coordinate system.
pw = gridpitch * w
ph = gridpitch * h
ret.coords = (pw/2, pw/2, ph/2, ph/2)
psprint(ret, "%g %g translate" % (-ret.coords[0], -ret.coords[2]))
# Draw round the grid exterior, thickly.
psprint(ret, "newpath 1 setlinewidth")
psprint(ret, "0 0 moveto 0 %g rlineto %g 0 rlineto 0 %g rlineto" % \
(h * gridpitch, w * gridpitch, -h * gridpitch))
psprint(ret, "closepath stroke")
# Draw the internal grid lines, _very_ thin (the player will
# need to draw over them visibly).
psprint(ret, "newpath 0.01 setlinewidth")
for x in xrange(1,w):
psprint(ret, "%g 0 moveto 0 %g rlineto" % (x * gridpitch, h * gridpitch))
for y in xrange(1,h):
psprint(ret, "0 %g moveto %g 0 rlineto" % (y * gridpitch, w * gridpitch))
psprint(ret, "stroke")
# And draw the numbers.
psprint(ret, "/Helvetica findfont %g scalefont setfont" % textht)
for y in xrange(H):
for x in xrange(W):
n = grid[y*W+x]
if n >= 0:
psprint(ret, "newpath %g %g %g 0 360 arc" % \
((x)*gridpitch, (h-y)*gridpitch, radius),
"gsave 1 setgray fill grestore stroke")
psprint(ret, "%g %g (%d) ctshow" % \
((x)*gridpitch, (h-y)*gridpitch, n))
return ret.coords, ret.s
formatters = { formatters = {
"net": net_format, "net": net_format,
"rect": rect_format, "rect": rect_format,
"rectangles": rect_format, "rectangles": rect_format,
"pattern": pattern_format, "pattern": pattern_format,
"solo": solo_format, "solo": solo_format,
"dominosa": dominosa_format "dominosa": dominosa_format,
"slant": slant_format
} }
if len(sys.argv) < 3: if len(sys.argv) < 3:

View File

@ -20,6 +20,8 @@
\define{by} \u00D7{x} \define{by} \u00D7{x}
\define{dash} \u2013{-}
This is a collection of small one-player puzzle games. This is a collection of small one-player puzzle games.
\copyright This manual is copyright 2004-5 Simon Tatham. All rights \copyright This manual is copyright 2004-5 Simon Tatham. All rights
@ -43,9 +45,9 @@ both, and have more recently done a port to Mac OS X as well. When I
find (or perhaps invent) further puzzle games that I like, they'll find (or perhaps invent) further puzzle games that I like, they'll
be added to this collection and will immediately be available on be added to this collection and will immediately be available on
both platforms. And if anyone feels like writing any other front both platforms. And if anyone feels like writing any other front
ends - PocketPC, Mac OS pre-10, or whatever it might be - then all ends \dash PocketPC, Mac OS pre-10, or whatever it might be \dash
the games in this framework will immediately become available on then all the games in this framework will immediately become
another platform as well. available on another platform as well.
The actual games in this collection were mostly not my invention; they The actual games in this collection were mostly not my invention; they
are re-implementations of existing game concepts within my portable are re-implementations of existing game concepts within my portable
@ -1208,12 +1210,12 @@ time (but always one that is known to have a solution).
\cfg{winhelp-topic}{games.dominosa} \cfg{winhelp-topic}{games.dominosa}
A normal set of dominoes - that is, one instance of every (unordered) A normal set of dominoes \dash that is, one instance of every
pair of numbers from 0 to 6 - has been arranged irregularly into a (unordered) pair of numbers from 0 to 6 \dash has been arranged
rectangle; then the number in each square has been written down and irregularly into a rectangle; then the number in each square has
the dominoes themselves removed. Your task is to reconstruct the been written down and the dominoes themselves removed. Your task is
pattern by arranging the set of dominoes to match the provided array to reconstruct the pattern by arranging the set of dominoes to match
of numbers. the provided array of numbers.
This puzzle is widely credited to O. S. Adler, and takes part of its This puzzle is widely credited to O. S. Adler, and takes part of its
name from those initials. name from those initials.
@ -1430,6 +1432,60 @@ using a different number to the original solution is still acceptable,
if all the laser inputs and outputs match. if all the laser inputs and outputs match.
\C{slant} \i{Slant}
\cfg{winhelp-topic}{games.slant}
You have a grid of squares. Your aim is to draw a diagonal line
through each square, and choose which way each line slants so that
the following conditions are met:
\b The diagonal lines never form a loop.
\b Any point with a circled number has precisely that many lines
meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a
zero is the centre of a diamond shape \dash or rather, a partial
diamond shape, because a zero can never appear in the middle of the
grid because that would immediately cause a loop.)
Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}.
\B{nikoli-slant}
\W{http://www.nikoli.co.jp/puzzles/39/index.htm}\cw{http://www.nikoli.co.jp/puzzles/39/index.htm}
(in Japanese)
\H{slant-controls} \i{Slant controls}
\IM{Slant controls} controls, for Slant
\IM{Slant controls} keys, for Slant
\IM{Slant controls} shortcuts (keyboard), for Slant
Left-clicking in a blank square will place a \cw{\\} in it (a line
leaning to the left, i.e. running from the top left of the square to
the bottom right). Right-clicking in a blank square will place a
\cw{/} in it (leaning to the right, running from top right to bottom
left).
Continuing to click either button will cycle between the three
possible square contents. Thus, if you left-click repeatedly in a
blank square it will change from blank to \cw{\\} to \cw{/} back to
blank, and if you right-click repeatedly the square will change from
blank to \cw{/} to \cw{\\} back to blank. (Therefore, you can play
the game entirely with one button if you need to.)
(All the actions described in \k{common-actions} are also available.)
\H{slant-parameters} \I{parameters, for slant}Slant parameters
These parameters are available from the \q{Custom...} option on the
\q{Type} menu.
\dt \e{Width}, \e{Height}
\dd Size of grid in squares.
\A{licence} \I{MIT licence}\ii{Licence} \A{licence} \I{MIT licence}\ii{Licence}
This software is \i{copyright} 2004-2005 Simon Tatham. This software is \i{copyright} 2004-2005 Simon Tatham.

View File

@ -234,6 +234,12 @@ void shuffle(void *array, int nelts, int eltsize, random_state *rs);
void draw_rect_outline(frontend *fe, int x, int y, int w, int h, void draw_rect_outline(frontend *fe, int x, int y, int w, int h,
int colour); int colour);
/*
* dsf.c
*/
int dsf_canonify(int *dsf, int val);
void dsf_merge(int *dsf, int v1, int v2);
/* /*
* version.c * version.c
*/ */

1251
slant.c Normal file

File diff suppressed because it is too large Load Diff