This is my implementation of flowfree, a puzzle game based on forming connections in a square grid. Building it is relatively straightforward. The Makefile isn't going to work out of the box for you unless you have various other bits of my infrastructure set up, but it _is_ a reasonable guide to, eg, what files you will need to compile. At this writing, there is only the one source file, play.c, and it needs linking with -lX11 -lm, but the Makefile is more likely to be kept updated than this README. The source does use labeled control structure. This means you will need lcs-cvt or something like it (assuming you don't have LCS support in your compiler, which is unlikely); at this writing, see ftp.rodents-montreal.org:/mouse/local/src/lcs-cvt for that. It also uses nested functions, so you will most likely need gcc - I'm not aware of any other compiler that supports nested functions, at least not for any language that is otherwise basically C. (In particular, clang/llvm is under the impression they are little-used; however true that may be in general, it is not true of my code.) It also uses anonymous unions, which as far as I know are a gcc feature; while they may be present in other compilers, there are enough gccisms already that investigating that in detail doesn't seem particularly worthwhile. Once built, basic usage is to just run it with a puzzle specified as an argument to -puzzle. The syntax of the argument: SIZE PAIR PAIR PAIR... SIZE is either a single number, for a square board, or two numbers joined by an x, for a rectangular board (the first number is the X dimension). The separators, shown as whitespace above, may also be commas, as in % ./play -puzzle 6,24,66,86,164,212,386 This avoids the need to quote the -puzzle argument with most shells. Each PAIR specifies one pair of endpoints you have to link to solve the puzzle. If the board is X by Y, and the endpoints are at (x1,y1) and (x2,y2), then the number is x1 + (X * (y1 + (Y * (x2 + (X * y2))))). For square grids, this can be thought of as a four-digit number in the base which is the grid size (eg, for a 7x7 grid, base 7); for rectangular grids, this calls for using different bases for different digits, but with that note it's still a consistent point of view. To solve a level, not only does every pair of coloured endpoints have to be connected, but also every cell of the grid must be occupied by either endpoints or links. At this writing, the game does not do anything in particular when the puzzle is solved, though I hope to fix that relatively soon. The file "levels" contains a number (300 at this writing) of levels. Each line is a valid puzzle spec, ordered roughly by difficulty; a reasonable first test after building might be % ./play -puzzle `head -1 < levels` I can confirm that every puzzle in levels is solvable; I have personally solved each of them. When making connections, you can use either click-drag-click or push-drag-release; the game determines which you're using dynamically based on the DragPixels, IgnoreDragTime, and MaxClickTime resource values, the defaults for which you can find early in the code by searching for those strings. (The time values are in milliseconds.) MaxClickTime is the longest time that can elapse between a press and a release for the combination to be considered a click; if the time is less than IgnoreDragTime, it will be considered a click anyway. If the time is greater than IgnoreDragTime but less than MaxClickTime, it is considered a click if the pointer moves no more than DragPixels between the press and the release. The final authority on play's command line is, of course, the source. But here's what it accepts as of this writing. Further discussion follows, including explanation of some things which are incompletely specified in the list of arguments but which are important. -display DISP Specifies the X display to contact. Defaults to whatever the X libraries do when XOpenDisplay is passed a nil pointer (usually this means to use $DISPLAY). -geometry GEOM Specifies the window's geometry. -font FONT Accepted but does nothing. This exists in anticipation of having an interface more elaborate than just the game window. -foreground COLOUR -fg COLOUR -background COLOUR -bg COLOUR Specifies foreground (-foreground, -fg) and background (-background, -bg) colours for things which don't have colours specified otherwise. -bordercolour COLOUR -bd COLOUR Specifies the colour of the window's border. -gridcolour COLOUR Specifies the colour of the grid lines in colour mode. -borderwidth N -bw N Specifies the window's border's width. -mag N Specifies the size of individual board cells, in pixels. -visual SPEC Specifies what visual to use. -sync Specifies that all X calls are to be done synchronously. This impacts performance severely but greatly simplifies some types of debugging. -force-bw Forces the game to run in black-and-white mode even if the display does provide sufficient colour facilities for colour mode. -puzzle PUZZLE Specifies the puzzle, as outlined above. -trace Prints a debugging trace. -dump FILE Dumps debugging information to FILE if the program crashes. The game can run in either of two modes: colour mode or black-and-white mode. It prefers colour mode; it can be forced into black-and-white mode regardless of the colour facilities available with -force-bw. It can use any visual, falling back to B&W mode if necessary, so if an argument is specified to -visual, it is obeyed. The argument to -visual can be the word "default" (which forces use of the default visual even if some othe visual would otherwise be preferred), one of the visual class names (StaticGray, GrayScale, StaticColor, PseudoColor, TrueColor, or DirectColor), or a visual ID (as printed by xdpyinfo, typically something like 0x23). A list of visuals is obtained; it can range from just one (if "default" or a numerical ID is specified) through a list of visuals of a particular class (if a class name is given) through all visuals (if -visual is not given at all). This is the `list' referred to below. The game is happiest with (a) TrueColor with bits-per-rgb >= 5 (b) PseudoColor with bits-per-rgb >= 5 and colormap-size >= 25 (c) DirectColor with bits-per-rgb >= 5 and colormap-size >= 25 Conceptually, it chooses a visual by making four tests: (1) If the default visual of the default screen is on the list and fits one of (a)-(c), use it. (2) If any visuals on the list fit one of (a)-(c), use one of them, breaking ties by favouring visuals on the default screen, then (a) over (b) or (c) and (b) over (c), then by preferring larger bits-per-rgb, then by preferring larger colormap-size, with any remaining ties broken arbitrarily. (3) If the default visual on the default screen is on the list, use it. (4) Choose the best visual from the list, with these preference rules, most important first: - Prefer visuals on the default screen. - Prefer StaticGray over StaticColor over TrueColor over GrayScale over PseudoColor over DirectColor. - Prefer larger colormap-size. - Prefer larger bits-per-rgb. - Any remaining ties are broken arbitrarily. If the visual was chosen by rule 3 or 4, the game runs in black-&-white mode. This means that, instead of different colours, the game uses only two colours for the game window, with different endpoints paired up by use of different textures rather than different colours. -force-bw is implemented by simply suppressing tests (1) and (2). B&W mode does not necessarily mean the program runs entirely in black and white. The actualy colours can be changed (they are the foreground and background colours, which are not necessarily black and white) and this does not affect the colours of things other than the gameplay board, such as the border colour. -borderwidth and -bordercolour control what the program requests. The window manager can and oftne will override the client's border. The window geometry requested also can be overridden by the window manager. The game computes the geometry to request by first checking to see if -mag was given. If so, that value is used below. If not, it divides the screen's dimensions and by the board's size in each dimension, rounding down, takes the smaller of the two values, and, if it's greater than 50, uses 50. In either case, if the value is less than 4, the game uses 4. The result is the mag factor, the size of each game board cell in pixels. This mag factor is then multiplied by the puzzle's size, and 1 is added in each dimension to acocunt for the inter-cell borders being present on all four sides of the board (the mag factor includes one pixel of inter-cell border). This is the window size the program wants to use. If a size was specified with -geometry, of course, it overrides this default (and if the puzzle doesn't fit even at mag 4, it will be only partially visible until the window is enlarged). If a position was specified via -geometry, it's used; if not, the window is placed in the middle of the screen. -trace and -dump exist for debugging purposes. The code is littered with can't-happen tests of various sorts. When one of them trips, indicating that there is still a bug remaining, by default the game just dumps core (see abort(3)). With -trace, various internal actions are printed to stdout as they occur. With -dump, when one of these checks trips, a good deal of internal state, including the last 64K of output -trace generated (or would have generated, if it's not actually specified), is written to the specified file before the coredump. The resulting data is of value mostly for debugging the code; if you find you can repetably provoke a crash, a dump file written by -dump will be extremely helpful when trying to track it down. /~\ The ASCII Mouse \ / Ribbon Campaign X Against HTML mouse@rodents-montreal.org / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B