home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-12-18 | 52.6 KB | 1,922 lines |
- Newsgroups: comp.sources.x
- From: andru@tonic.lcs.mit.edu (Andrew Myers)
- Subject: v21i058: xsokoban - Simple but complex move the stones game, Part01/04
- Message-ID: <csx-v21i058=xsokoban.170010@sparky.Sterling.COM>
- X-Md4-Signature: 698e17aef2785a90478d2d876ef2baad
- Sender: chris@sparky.sterling.com (Chris Olson)
- Organization: Sterling Software
- Date: Sat, 18 Dec 1993 23:00:32 GMT
- Approved: chris@sterling.com
-
- Submitted-by: andru@tonic.lcs.mit.edu (Andrew Myers)
- Posting-number: Volume 21, Issue 58
- Archive-name: xsokoban/part01
- Environment: X11, ansi
-
- This is the third release of XSokoban. XSokoban is a very tricky puzzle
- game with 90 difficult levels to solve. Once you've solved a level,
- there's still the puzzle of solving it optimally. The code has been
- tested on a variety of operating systems and compilers.
-
- Improvements over the second version are:
-
- - better mouse user interface
- - score ranking
- - infinite undo capability
-
- - Andrew Myers (andru@lcs.mit.edu)
-
-
- ----- cut here -----
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: xsokoban-3.0 xsokoban-3.0/bitmaps
- # xsokoban-3.0/bitmaps/defaults xsokoban-3.0/display.c
- # xsokoban-3.0/externs.h xsokoban-3.0/play.c xsokoban-3.0/saves
- # xsokoban-3.0/score.c xsokoban-3.0/scores xsokoban-3.0/screens
- # Wrapped by chris@sparky on Sat Dec 18 16:54:28 1993
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 1 (of 4)."'
- if test ! -d 'xsokoban-3.0' ; then
- echo shar: Creating directory \"'xsokoban-3.0'\"
- mkdir 'xsokoban-3.0'
- fi
- if test ! -d 'xsokoban-3.0/bitmaps' ; then
- echo shar: Creating directory \"'xsokoban-3.0/bitmaps'\"
- mkdir 'xsokoban-3.0/bitmaps'
- fi
- if test ! -d 'xsokoban-3.0/bitmaps/defaults' ; then
- echo shar: Creating directory \"'xsokoban-3.0/bitmaps/defaults'\"
- mkdir 'xsokoban-3.0/bitmaps/defaults'
- fi
- if test -f 'xsokoban-3.0/display.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xsokoban-3.0/display.c'\"
- else
- echo shar: Extracting \"'xsokoban-3.0/display.c'\" \(17310 characters\)
- sed "s/^X//" >'xsokoban-3.0/display.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X#include <string.h>
- X#include <assert.h>
- X
- X#include "externs.h"
- X#include "globals.h"
- X#include "defaults.h"
- X#include "help.h"
- X
- X/* mnemonic defines to help orient some of the text/line drawing, sizes */
- X#define HELPLINE ((bit_height * MAXROW) + 30)
- X#define STATUSLINE ((bit_height * MAXROW) + 5)
- X#define HELP_H (bit_height * MAXROW)
- X#define HELP_W (bit_width * MAXCOL)
- X
- X/* useful globals */
- XDisplay *dpy;
- XWindow win;
- Xint scr;
- XGC gc, rgc, drgc;
- XAtom wm_delete_window, wm_protocols;
- XXFontStruct *finfo;
- Xunsigned int width, height, depth, bit_height, bit_width;
- XBoolean display_alloc = False, font_alloc = False, gc_alloc = False,
- X pix_alloc = False;
- XBoolean optwalls;
- XColormap cmap;
- XCursor this_curs;
- Xstatic Pixmap help[HELP_PAGES], floor;
- Xstatic Pixmap blank, work, man, saveman, goal, object, treasure, walls[NUM_WALLS];
- Xint hlpscrn = -1;
- Xchar buf[500];
- X
- Xextern char *progname;
- Xextern char map[MAXROW+1][MAXCOL+1];
- Xextern short rows, cols, level, moves, pushes, packets, savepack;
- Xextern char *bitpath;
- X
- X/* names of the fancy wall bitmap files. If you define a set of fancy
- X * wall bitmaps, they must use these names
- X */
- Xchar *wallname[] = {
- X "lonewall.xbm", "southwall.xbm", "westwall.xbm", "llcornerwall.xbm",
- X "northwall.xbm", "vertiwall.xbm", "ulcornerwall.xbm", "west_twall.xbm",
- X "eastwall.xbm", "lrcornerwall.xbm", "horizwall.xbm", "south_twall.xbm",
- X "urcornerwall.xbm", "east_twall.xbm", "north_twall.xbm", "centerwall.xbm"
- X};
- X
- X/* Do all the nasty stuff like makeing the windows, setting all the defaults
- X * creating all the pixmaps, loading everything, and mapping the window.
- X * this does NOT do the XOpenDisplay() so that the -display switch can be
- X * handled cleanly.
- X */
- Xshort InitX(void)
- X{
- X int i;
- X Boolean reverse = _false_, tmpwalls = _false_;
- X char *rval;
- X unsigned long fore, back, bord, curs, gc_mask;
- X XSizeHints szh;
- X XWMHints wmh;
- X XSetWindowAttributes wattr;
- X XClassHint clh;
- X XGCValues val, reval;
- X XTextProperty wname, iname;
- X XColor cfg, cbg;
- X Atom protocols[1];
- X
- X /* these are always needed */
- X scr = DefaultScreen(dpy);
- X cmap = DefaultColormap(dpy, scr);
- X depth = DefaultDepth(dpy, scr);
- X
- X /* here is where we figure out the resources and set the defaults.
- X * resources can be either on the command line, or in your .Xdefaults/
- X * .Xresources files. They are read in and parsed in main.c, but used
- X * here.
- X */
- X rval = GetResource(FONT);
- X if(rval == (char *)0)
- X rval = DEF_FONT;
- X finfo = XLoadQueryFont(dpy, rval);
- X if(finfo == (XFontStruct *)0)
- X return E_NOFONT;
- X font_alloc = _true_;
- X
- X rval = GetResource(REVERSE);
- X if(rval != (char *)0) {
- X reverse = StringToBoolean(rval);
- X }
- X
- X if(!GetColorResource(FOREG, &fore))
- X fore = BlackPixel(dpy, scr);
- X
- X if(!GetColorResource(BACKG, &back))
- X back = WhitePixel(dpy, scr);
- X
- X if(reverse) {
- X unsigned long t;
- X t = fore;
- X fore = back;
- X back = t;
- X }
- X
- X if(!GetColorResource(BORDER, &bord))
- X bord = fore;
- X if(!GetColorResource(CURSOR, &curs))
- X curs = fore;
- X
- X bitpath = GetResource(BITDIR);
- X rval = GetResource(WALLS);
- X if(rval != (char *)0)
- X tmpwalls = StringToBoolean(rval);
- X
- X /* walls are funny. if a alternate bitpath has been defined, assume
- X * !fancywalls unless explicitly told fancy walls. If the default
- X * bitpath is being used, you can assume fancy walls.
- X */
- X if(bitpath && !tmpwalls)
- X optwalls = _false_;
- X else
- X optwalls = _true_;
- X
- X width = MAXCOL * DEF_BITW;
- X height = MAXROW * DEF_BITH + 50;
- X
- X wmh.initial_state = NormalState;
- X wmh.input = True;
- X wmh.flags = (StateHint | InputHint);
- X
- X clh.res_class = clh.res_name = progname;
- X
- X /* Make sure the window and icon names are set */
- X if(!XStringListToTextProperty(&progname, 1, &wname))
- X return E_NOMEM;
- X if(!XStringListToTextProperty(&progname, 1, &iname))
- X return E_NOMEM;
- X
- X /* load in a cursor, and recolor it so it looks pretty */
- X this_curs = XCreateFontCursor(dpy, DEF_CURSOR);
- X cfg.pixel = curs;
- X cbg.pixel = back;
- X XQueryColor(dpy, cmap, &cfg);
- X XQueryColor(dpy, cmap, &cbg);
- X XRecolorCursor(dpy, this_curs, &cfg, &cbg);
- X
- X /* set up the funky little window attributes */
- X wattr.background_pixel = back;
- X wattr.border_pixel = bord;
- X wattr.backing_store = Always;
- X wattr.event_mask = (KeyPressMask | ExposureMask | ButtonPressMask);
- X wattr.cursor = this_curs;
- X
- X /* whee, create the window, we create it with NO size so that we
- X * can load in the bitmaps, we later resize it correctly
- X */
- X win = XCreateWindow(dpy, RootWindow(dpy, scr), 0, 0, width, height, 4,
- X CopyFromParent, InputOutput, CopyFromParent,
- X (CWBackPixel | CWBorderPixel | CWBackingStore |
- X CWEventMask | CWCursor), &wattr);
- X
- X /* this will set the bit_width and bit_height as well as loading
- X * in the pretty little bitmaps
- X */
- X if(LoadBitmaps() == E_NOBITMAP)
- X return E_NOBITMAP;
- X blank = XCreatePixmap(dpy, win, bit_width, bit_height, 1);
- X pix_alloc = _true_;
- X
- X width = MAXCOL * bit_width;
- X height = MAXROW * bit_height + 50;
- X
- X /* whee, resize the window with the correct size now that we know it */
- X XResizeWindow(dpy, win, width, height);
- X
- X /* set up the size hints, we don't want manual resizing allowed. */
- X szh.min_width = szh.width = szh.max_width = width;
- X szh.min_height = szh.height = szh.max_height = height;
- X szh.x = szh.y = 0;
- X szh.flags = (PSize | PPosition | PMinSize | PMaxSize);
- X
- X /* now SET all those hints we create above */
- X XSetWMNormalHints(dpy, win, &szh);
- X XSetWMHints(dpy, win, &wmh);
- X XSetClassHint(dpy, win, &clh);
- X XSetWMName(dpy, win, &wname);
- X XSetWMIconName(dpy, win, &iname);
- X
- X /* Turn on WM_DELETE_WINDOW */
- X wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
- X wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", 0);
- X protocols[0] = wm_delete_window;
- X XSetWMProtocols(dpy, win, protocols, 1);
- X
- X work = XCreatePixmap(dpy, win, width, height, depth);
- X
- X /* set up all the relevant GC's */
- X val.foreground = reval.background = fore;
- X val.background = reval.foreground = back;
- X val.function = reval.function = GXcopy;
- X val.font = reval.font = finfo->fid;
- X gc_mask = (GCForeground | GCBackground | GCFunction | GCFont);
- X gc = XCreateGC(dpy, work, gc_mask, &val);
- X rgc = XCreateGC(dpy, blank, gc_mask, &reval);
- X drgc = XCreateGC(dpy, work, gc_mask, &reval);
- X
- X /* make the help windows and the working bitmaps */
- X /* we need to do this down here since it requires GCs to be allocated */
- X for(i = 0; i < HELP_PAGES; i++)
- X help[i] = XCreatePixmap(dpy, win, HELP_W, HELP_H, depth);
- X MakeHelpWindows();
- X XFillRectangle(dpy, blank, rgc, 0, 0, bit_width, bit_height);
- X
- X gc_alloc = _true_;
- X
- X /* display the friendly little clear screen */
- X ClearScreen();
- X XMapWindow(dpy, win);
- X RedisplayScreen();
- X
- X return 0;
- X}
- X
- X/* deallocate all the memory and structures used in creating stuff */
- Xvoid DestroyDisplay(void)
- X{
- X int i;
- X
- X /* kill the font */
- X if(font_alloc)
- X XFreeFont(dpy, finfo);
- X
- X /* destroy everything allocted right around the gcs. Help windows are
- X * freed here cause they are created about the same time. (Yes, I know
- X * this could cause problems, it hasn't yet.
- X */
- X if(gc_alloc) {
- X XFreeGC(dpy, gc);
- X XFreeGC(dpy, rgc);
- X XFreeGC(dpy, drgc);
- X XFreePixmap(dpy, work);
- X for (i = 0; i < HELP_PAGES; i++)
- X XFreePixmap(dpy, help[i]);
- X }
- X /* free up all the allocated pix */
- X if(pix_alloc) {
- X XFreePixmap(dpy, man);
- X XFreePixmap(dpy, saveman);
- X XFreePixmap(dpy, goal);
- X XFreePixmap(dpy, treasure);
- X XFreePixmap(dpy, object);
- X XFreePixmap(dpy, floor);
- X XFreePixmap(dpy, blank);
- X for(i = 0; i < NUM_WALLS; i++)
- X if(i == 0 || optwalls)
- X XFreePixmap(dpy, walls[i]);
- X }
- X /* okay.. NOW we can destroy the main window and the display */
- X if(display_alloc) {
- X XDestroyWindow(dpy, win);
- X XCloseDisplay(dpy);
- X }
- X}
- X
- X/* Load in a single bitmap. If this bitmap is the largest in the x or
- X * y direction, set bit_width or bit_height appropriately. If your pixmaps
- X * are of varying sizes, a bit_width by bit_height box is guaranteed to be
- X * able to surround all of them.
- X */
- XBoolean LoadOneBitmap(char *fname, char *altname, Pixmap *pix)
- X{
- X unsigned int dum1, dum2;
- X int dum3, dum4;
- X Boolean load_fail = _false_;
- X char buf[1024];
- X
- X if(bitpath && *bitpath) {
- X /* we have something to try other than the default, let's do it */
- X sprintf(buf, "%s/%s", bitpath, fname);
- X if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
- X BitmapSuccess) {
- X if(altname && *altname) {
- X fprintf(stderr, "%s: Cannot find '%s/%s', trying alternate.\n",
- X progname, bitpath, fname);
- X sprintf(buf, "%s/%s", bitpath, altname);
- X if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
- X BitmapSuccess) {
- X load_fail = _true_;
- X fprintf(stderr, "%s: Cannot find '%s/%s', trying defaults.\n",
- X progname, bitpath, altname);
- X } else {
- X if(dum1 > bit_width) bit_width = dum1;
- X if(dum2 > bit_height) bit_height = dum2;
- X return _true_;
- X }
- X } else {
- X load_fail = _true_;
- X fprintf(stderr, "%s: Cannot find '%s/%s', trying alternate.\n",
- X progname, bitpath, fname);
- X }
- X } else {
- X if(dum1 > bit_width) bit_width = dum1;
- X if(dum2 > bit_height) bit_height = dum2;
- X return _true_;
- X }
- X }
- X assert(!bitpath || !*bitpath || load_fail);
- X sprintf(buf, "%s/%s", BITPATH, fname);
- X if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
- X BitmapSuccess) {
- X if(altname && *altname) {
- X fprintf(stderr, "%s: Cannot find '%s', trying alternate.\n",
- X progname, fname);
- X sprintf(buf, "%s/%s", BITPATH, fname);
- X if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
- X BitmapSuccess) {
- X fprintf(stderr, "%s: Cannot find '%s'!\n", progname, altname);
- X return _false_;
- X } else {
- X if(dum1 > bit_width) bit_width = dum1;
- X if(dum2 > bit_height) bit_height = dum2;
- X return _true_;
- X }
- X } else {
- X fprintf(stderr, "%s: Cannot find '%s'!\n", progname, fname);
- X return _false_;
- X }
- X } else {
- X if(dum1 > bit_width) bit_width = dum1;
- X if(dum2 > bit_height) bit_height = dum2;
- X return _true_;
- X }
- X}
- X
- X/* loads all the bitmaps in.. if any fail, it returns E_NOBITMAP up a level
- X * so the program can report the error to the user. It tries to load in the
- X * alternates as well.
- X */
- Xshort LoadBitmaps(void)
- X{
- X register int i;
- X
- X if(!LoadOneBitmap("man.xbm", NULL, &man)) return E_NOBITMAP;
- X if(!LoadOneBitmap("saveman.xbm", "man.xbm", &saveman)) return E_NOBITMAP;
- X if(!LoadOneBitmap("object.xbm", NULL, &object)) return E_NOBITMAP;
- X if(!LoadOneBitmap("treasure.xbm", NULL, &treasure)) return E_NOBITMAP;
- X if(!LoadOneBitmap("goal.xbm", NULL, &goal)) return E_NOBITMAP;
- X if(!LoadOneBitmap("floor.xbm", NULL, &floor)) return E_NOBITMAP;
- X
- X if(optwalls) {
- X for(i = 0; i < NUM_WALLS; i++) {
- X if(!LoadOneBitmap(wallname[i], "wall.xbm", &walls[i])) return E_NOBITMAP;
- X }
- X } else {
- X if(!LoadOneBitmap("wall.xbm", NULL, &walls[0])) return E_NOBITMAP;
- X }
- X return 0;
- X}
- X
- X/* create and draw all the help windows in. This is not wholly fullproff with
- X * the variable size bitmap code yet, as the constants to place things on the
- X * screen, are just that, constants. This could most likley be reworked.
- X */
- Xvoid MakeHelpWindows(void)
- X{
- X register int i;
- X char *title =
- X " Sokoban -- X version by Joseph L. Traub -- Help page %d";
- X char *next =
- X " Press Enter to exit -- Any other key for next page.";
- X
- X for(i = 0; i < HELP_PAGES; i++) {
- X XFillRectangle(dpy, help[i], drgc, 0, 0, HELP_W, HELP_H);
- X sprintf(buf, title, (i+1));
- X XDrawImageString(dpy, help[i], gc, 0, 11, buf, strlen(buf));
- X XDrawLine(dpy, help[i], gc, 0, 17, HELP_W, 17);
- X XDrawLine(dpy, help[i], gc, 0, HELP_H-20, HELP_W, HELP_H-20);
- X XDrawImageString(dpy, help[i], gc, 2, HELP_H-7, next, strlen(next));
- X }
- X for(i = 0; help_pages[i].textline != NULL; i++) {
- X XDrawImageString(dpy,help[help_pages[i].page], gc,
- X help_pages[i].xpos * (finfo->max_bounds.width),
- X help_pages[i].ypos, help_pages[i].textline,
- X strlen(help_pages[i].textline));
- X }
- X XCopyPlane(dpy, man, help[0], gc, 0, 0, bit_width, bit_height, 180, 360, 1);
- X XCopyPlane(dpy, goal, help[0], gc, 0, 0, bit_width, bit_height, 270, 360, 1);
- X XCopyPlane(dpy, walls[0], help[0], gc, 0, 0, bit_width, bit_height,
- X 369, 360, 1);
- X XCopyPlane(dpy, object, help[0], gc, 0, 0, bit_width, bit_height,
- X 477, 360, 1);
- X XCopyPlane(dpy, treasure, help[0], gc, 0, 0, bit_width, bit_height,
- X 270, 400, 1);
- X XCopyPlane(dpy, saveman, help[0], gc, 0, 0, bit_width, bit_height,
- X 477, 400, 1);
- X}
- X
- X/* wipe out the entire contents of the screen */
- Xvoid ClearScreen(void)
- X{
- X register int i,j;
- X
- X XFillRectangle(dpy, work, drgc, 0, 0, width, height);
- X for(i = 0; i < MAXROW; i++)
- X for(j = 0; j < MAXCOL; j++)
- X XCopyPlane(dpy, floor, work, gc, 0, 0, bit_width, bit_height,
- X j*bit_width, i*bit_height, 1);
- X XDrawLine(dpy, work, gc, 0, bit_height*MAXROW, bit_width*MAXCOL,
- X bit_height*MAXROW);
- X}
- X
- X/* redisplay the current screen.. Has to handle the help screens if one
- X * is currently active.. Copys the correct bitmaps onto the window.
- X */
- Xvoid RedisplayScreen(void)
- X{
- X if(hlpscrn == -1)
- X XCopyArea(dpy, work, win, gc, 0, 0, width, height, 0, 0);
- X else
- X XCopyArea(dpy, help[hlpscrn], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
- X XFlush(dpy);
- X}
- X
- X/* Flush all X events to the screen and wait for them to get there. */
- Xvoid SyncScreen(void)
- X{
- X XSync(dpy, 0);
- X}
- X
- X/* Draws all the neat little pictures and text onto the working pixmap
- X * so that RedisplayScreen is happy.
- X */
- Xvoid ShowScreen(void)
- X{
- X register int i,j;
- X
- X for(i = 0; i < rows; i++)
- X for(j = 0; j < cols && map[i][j] != 0; j++)
- X MapChar(map[i][j], i, j, 0);
- X DisplayLevel();
- X DisplayPackets();
- X DisplaySave();
- X DisplayMoves();
- X DisplayPushes();
- X DisplayHelp();
- X RedisplayScreen();
- X}
- X
- X/* Draws a single pixmap, translating from the character map to the pixmap
- X * rendition. If "copy_area", also push the change through to the actual window.
- X */
- Xvoid MapChar(char c, int i, int j, Boolean copy_area)
- X{
- X Pixmap this;
- X
- X this = GetObjectPixmap(i, j, c); /* i, j are passed so walls can be done */
- X XCopyPlane(dpy, this, work, gc, 0, 0, bit_width, bit_height, cX(j), cY(i), 1);
- X if (copy_area) {
- X XCopyArea(dpy, work, win, gc, cX(j), cY(i), bit_width, bit_height,
- X cX(j), cY(i));
- X }
- X}
- X
- X/* figures out the appropriate pixmap from the internal game representation.
- X * Handles fancy walls.
- X */
- XPixmap GetObjectPixmap(int i, int j, char c)
- X{
- X switch(c) {
- X case player: return man;
- X case playerstore: return saveman;
- X case store: return goal;
- X case save: return treasure;
- X case packet: return object;
- X case wall:
- X if(optwalls) return walls[PickWall(i,j)];
- X else return walls[0];
- X case ground: return floor;
- X default: return blank;
- X }
- X}
- X
- X/* returns and index into the fancy walls array. works by assigning a value
- X * to each 'position'.. the type of fancy wall is computed based on how
- X * many neighboring walls there are.
- X */
- Xint PickWall(int i, int j)
- X{
- X int ret = 0;
- X
- X if(i > 0 && map[i-1][j] == wall) ret += 1;
- X if(j < cols && map[i][j+1] == wall) ret += 2;
- X if(i < rows && map[i+1][j] == wall) ret += 4;
- X if(j > 0 && map[i][j-1] == wall) ret += 8;
- X return ret;
- X}
- X
- X/* Draws a string onto the working pixmap */
- Xvoid DrawString(int x, int y, char *text)
- X{
- X int x_off, y_off;
- X
- X x_off = x * finfo->max_bounds.width;
- X y_off = y + finfo->ascent;
- X
- X XDrawImageString(dpy, work, gc, x_off, y_off, text, strlen(text));
- X}
- X
- X/* The following routines display various 'statusline' stuff (ie moves, pushes,
- X * etc) on the screen. they are called as they are needed to be changed to
- X * avoid unnecessary drawing */
- Xvoid DisplayLevel(void)
- X{
- X sprintf(buf, "Level: %3d", level);
- X DrawString(0, STATUSLINE, buf);
- X}
- X
- Xvoid DisplayPackets(void)
- X{
- X sprintf(buf, "Packets: %3d", packets);
- X DrawString(12, STATUSLINE, buf);
- X}
- X
- Xvoid DisplaySave(void)
- X{
- X sprintf(buf, "Saved: %3d", savepack);
- X DrawString(26, STATUSLINE, buf);
- X}
- X
- Xvoid DisplayMoves(void)
- X{
- X sprintf(buf, "Moves: %5d", moves);
- X DrawString(38, STATUSLINE, buf);
- X}
- X
- Xvoid DisplayPushes(void)
- X{
- X sprintf(buf, "Pushes: %3d", pushes);
- X DrawString(52, STATUSLINE, buf);
- X}
- X
- Xvoid DisplayHelp(void)
- X{
- X DrawString(0, HELPLINE, "Press ? for help.");
- X}
- X
- X/* Displays the first help page, and flips help pages (one per key press)
- X * until a return is pressed.
- X */
- Xvoid ShowHelp(void)
- X{
- X int i = 0;
- X Boolean done = _false_;
- X
- X hlpscrn = 0;
- X XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
- X XFlush(dpy);
- X while(!done) {
- X done = WaitForEnter();
- X if(done) {
- X hlpscrn = -1;
- X return;
- X } else {
- X i = (i+1)%HELP_PAGES;
- X hlpscrn = i;
- X XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
- X XFlush(dpy);
- X }
- X }
- X}
- X
- X/* since the 'press ? for help' is ALWAYS displayed, just beep when there is
- X * a problem.
- X */
- Xvoid HelpMessage(void)
- X{
- X XBell(dpy, 0);
- X RedisplayScreen();
- X}
- END_OF_FILE
- if test 17310 -ne `wc -c <'xsokoban-3.0/display.c'`; then
- echo shar: \"'xsokoban-3.0/display.c'\" unpacked with wrong size!
- fi
- # end of 'xsokoban-3.0/display.c'
- fi
- if test -f 'xsokoban-3.0/externs.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xsokoban-3.0/externs.h'\"
- else
- echo shar: Extracting \"'xsokoban-3.0/externs.h'\" \(2678 characters\)
- sed "s/^X//" >'xsokoban-3.0/externs.h' <<'END_OF_FILE'
- X#include <X11/X.h>
- X#include <X11/Xlib.h>
- X#include <X11/Xutil.h>
- X#include <X11/keysym.h>
- X#include <X11/Xresource.h>
- X#include <sys/stat.h>
- X#include <sys/types.h>
- X
- X#include "config_local.h"
- X
- X#if !defined(GETPASS_PROTO)
- Xextern char *getpass(char *);
- X#endif
- X
- X#if !defined(CREAT_PROTO)
- Xextern int creat(const char *, mode_t);
- X#endif
- X
- X#if !defined(FPRINTF_PROTO)
- Xextern int fprintf(FILE *, const char *, ...);
- X#endif
- X
- X#if !defined(FCLOSE_PROTO)
- Xextern int fclose(FILE *);
- X#endif
- X
- X/* The boolean typedef */
- Xtypedef enum { _false_ = 0, _true_ = 1 } Boolean;
- X
- X/* stuff from display.c */
- Xextern short LoadBitmaps(void);
- Xextern void MakeHelpWindows(void);
- Xextern void ClearScreen(void);
- Xextern void RedisplayScreen(void);
- Xextern void SyncScreen(void);
- Xextern void ShowScreen(void);
- Xextern void MapChar(char, int, int, Boolean);
- Xextern Pixmap GetObjectPixmap(int, int, char);
- Xextern int PickWall(int, int);
- Xextern void DrawString(int, int, char *);
- Xextern void ClearString(int, int, int);
- Xextern void DisplayLevel(void);
- Xextern void DisplayPackets(void);
- Xextern void DisplaySave(void);
- Xextern void DisplayMoves(void);
- Xextern void DisplayPushes(void);
- Xextern void DisplayHelp(void);
- Xextern void ShowHelp(void);
- Xextern void HelpMessage(void);
- Xextern void DestroyDisplay(void);
- Xextern short InitX(void);
- X
- X/* stuff from main.c */
- Xextern short CheckCommandLine(int *, char **);
- Xextern void main(int, char **);
- Xextern short GameLoop(void);
- Xextern short GetGamePassword(void);
- Xextern void Error(short);
- Xextern void Usage(void);
- X
- X/* stuff from resources.c */
- Xextern char *GetDatabaseResource(XrmDatabase, char *);
- Xextern char *GetResource(char *);
- Xextern Boolean StringToBoolean(char *);
- Xextern Boolean GetColorResource(char *, unsigned long *);
- X
- X/* stuff from play.c */
- Xextern short Play(void);
- Xextern void MakeMove(KeySym);
- Xextern short TestMove(KeySym);
- Xextern void DoMove(short);
- Xextern void UndoMove(void);
- Xextern void TempSave(void);
- Xextern void TempReset(void);
- Xextern Boolean WaitForEnter(void);
- Xextern void MoveMan(int, int);
- Xextern void FindTarget(int, int, int);
- Xextern Boolean RunTo(int, int);
- Xextern void PushMan(int, int);
- X
- X/* stuff from score.c */
- Xextern short OutputScore(void);
- Xextern short MakeNewScore(void);
- Xextern short GetUserLevel(short *);
- Xextern short Score(Boolean show);
- Xextern short ReadScore(void);
- Xextern short MakeScore(void);
- Xextern short FindUser(void);
- Xextern short FindPos(void);
- Xextern short WriteScore(void);
- Xextern void ShowScore(void);
- Xextern void CopyEntry(short, short);
- Xextern void FlushDeletedScores(Boolean[]);
- X
- X/* stuff from screen.c */
- Xextern short ReadScreen(void);
- X
- X/* stuff from save.c */
- Xextern short SaveGame(void);
- Xextern short RestoreGame(void);
- END_OF_FILE
- if test 2678 -ne `wc -c <'xsokoban-3.0/externs.h'`; then
- echo shar: \"'xsokoban-3.0/externs.h'\" unpacked with wrong size!
- fi
- # end of 'xsokoban-3.0/externs.h'
- fi
- if test -f 'xsokoban-3.0/play.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xsokoban-3.0/play.c'\"
- else
- echo shar: Extracting \"'xsokoban-3.0/play.c'\" \(17974 characters\)
- sed "s/^X//" >'xsokoban-3.0/play.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X#include <assert.h>
- X#include <string.h>
- X
- X#include "externs.h"
- X#include "globals.h"
- X
- Xextern int abs(int);
- X
- X/* defining the types of move */
- X#define MOVE 1
- X#define PUSH 2
- X#define SAVE 3
- X#define UNSAVE 4
- X#define STOREMOVE 5
- X#define STOREPUSH 6
- X
- X/* This value merely needs to be greater than the longest possible path length
- X * making it the number of squares in the array seems a good bet.
- X */
- X#define BADMOVE MAXROW*MAXCOL
- X
- X/* some simple checking macros to make sure certain moves are legal when
- X * trying to do mouse based movement.
- X */
- X#define ISPLAYER(x,y) ((map[x][y] == player) || (map[x][y] == playerstore))
- X#define ISCLEAR(x,y) ((map[x][y] == ground) || (map[x][y] == store))
- X#define ISPACKET(x,y) ((map[x][y] == packet) || (map[x][y] == save))
- X
- Xstatic XEvent xev;
- Xstatic POS tpos1, tpos2, lastppos, lasttpos1, lasttpos2;
- Xstatic char lppc, ltp1c, ltp2c;
- Xstatic short action, lastaction;
- X
- Xstatic Boolean shift = _false_;
- Xstatic Boolean cntrl = _false_;
- Xstatic KeySym oldmove;
- Xstatic int findmap[MAXROW+1][MAXCOL+1];
- X
- X#define MAXDELTAS 4
- X
- X/** For stack saves. **/
- Xstruct map_delta {
- X int x,y;
- X char oldchar, newchar;
- X};
- Xstruct move_r {
- X short px, py;
- X short moves, pushes, saved, ndeltas;
- X struct map_delta deltas[MAXDELTAS];
- X};
- Xstatic struct move_r move_stack[STACKDEPTH];
- Xstatic int move_stack_sp; /* points to last saved move. If no saved move, -1 */
- Xstatic Map prev_map;
- Xstatic void RecordChange(void);
- Xstatic void UndoChange(void);
- Xstatic void InitMoveStack(void);
- X
- X/* play a particular level.
- X * All we do here is wait for input, and dispatch to appropriate routines
- X * to deal with it nicely.
- X */
- Xshort Play(void)
- X{
- X short ret;
- X int bufs = 1;
- X char buf[1];
- X KeySym sym;
- X XComposeStatus compose;
- X
- X ClearScreen();
- X ShowScreen();
- X InitMoveStack();
- X ret = 0;
- X while (ret == 0) {
- X XNextEvent(dpy, &xev);
- X switch(xev.type) {
- X case Expose:
- X /* Redisplaying is pretty cheap, so I don't care too much about it */
- X RedisplayScreen();
- X break;
- X case ButtonPress:
- X switch(xev.xbutton.button) {
- X case Button1:
- X /* move the man to where the pointer is. */
- X MoveMan(xev.xbutton.x, xev.xbutton.y);
- X RecordChange();
- X break;
- X case Button2:
- X /* Push a ball */
- X PushMan(xev.xbutton.x, xev.xbutton.y);
- X RecordChange();
- X break;
- X case Button3:
- X /* undo last move */
- X UndoChange();
- X ShowScreen();
- X break;
- X default:
- X /* I'm sorry, you win the irritating beep for your efforts. */
- X HelpMessage();
- X break;
- X }
- X RedisplayScreen();
- X break;
- X case KeyPress:
- X buf[0] = '\0';
- X (void) XLookupString(&xev.xkey, buf, bufs, &sym, &compose);
- X cntrl = (xev.xkey.state & ControlMask) ? _true_ : _false_;
- X shift = (xev.xkey.state & ShiftMask) ? _true_ : _false_;
- X switch(sym) {
- X case XK_q:
- X /* q is for quit */
- X if(!cntrl)
- X ret = E_ENDGAME;
- X break;
- X case XK_s:
- X /* save */
- X if(!cntrl) {
- X ret = SaveGame();
- X if(ret == 0)
- X ret = E_SAVED;
- X }
- X break;
- X case XK_question:
- X /* help */
- X if(!cntrl) {
- X ShowHelp();
- X RedisplayScreen();
- X }
- X break;
- X case XK_r:
- X /* ^R refreshes the screen */
- X if(cntrl)
- X RedisplayScreen();
- X break;
- X case XK_U:
- X /* Do a full screen reset */
- X if(!cntrl) {
- X moves = pushes = 0;
- X ret = ReadScreen();
- X InitMoveStack();
- X if(ret == 0) {
- X ShowScreen();
- X }
- X }
- X break;
- X case XK_u:
- X if(!cntrl) {
- X UndoChange();
- X ShowScreen();
- X }
- X break;
- X case XK_k:
- X case XK_K:
- X case XK_Up:
- X case XK_j:
- X case XK_J:
- X case XK_Down:
- X case XK_l:
- X case XK_L:
- X case XK_Right:
- X case XK_h:
- X case XK_H:
- X case XK_Left:
- X /* A move, A move!! we have a MOVE!! */
- X MakeMove(sym);
- X RedisplayScreen();
- X RecordChange();
- X break;
- X default:
- X /* I ONLY want to beep if a key was pressed. Contrary to what
- X * X11 believes, SHIFT and CONTROL are NOT keys
- X */
- X if(buf[0]) {
- X HelpMessage();
- X }
- X break;
- X }
- X break;
- X case ClientMessage:
- X {
- X XClientMessageEvent *cm = (XClientMessageEvent *)&xev;
- X if (cm->message_type == wm_protocols &&
- X cm->data.l[0] == wm_delete_window)
- X ret = E_ENDGAME;
- X }
- X break;
- X default:
- X break;
- X }
- X /* if we solved a level, set it up so we get some score! */
- X if((ret == 0) && (packets == savepack)) {
- X scorelevel = level;
- X scoremoves = moves;
- X scorepushes = pushes;
- X break;
- X }
- X }
- X return ret;
- X}
- X
- X/* Perform a user move based on the key in "sym". */
- Xvoid MakeMove(KeySym sym)
- X{
- X do {
- X action = TestMove(sym);
- X if(action != 0) {
- X lastaction = action;
- X lastppos.x = ppos.x;
- X lastppos.y = ppos.y;
- X lppc = map[ppos.x][ppos.y];
- X lasttpos1.x = tpos1.x;
- X lasttpos1.y = tpos1.y;
- X ltp1c = map[tpos1.x][tpos1.y];
- X lasttpos2.x = tpos2.x;
- X lasttpos2.y = tpos2.y;
- X ltp2c = map[tpos2.x][tpos2.y];
- X DoMove(lastaction);
- X /* store the current keysym so we can do the repeat. */
- X oldmove = sym;
- X }
- X } while ((action != 0) && (packets != savepack) && (shift || cntrl));
- X}
- X
- X/* make sure a move is valid and if it is, return type of move */
- Xshort TestMove(KeySym action)
- X{
- X short ret;
- X char tc;
- X Boolean stop_at_object;
- X
- X stop_at_object = cntrl;
- X
- X if((action == XK_Up) || (action == XK_k) || (action == XK_K)) {
- X tpos1.x = ppos.x-1;
- X tpos2.x = ppos.x-2;
- X tpos1.y = tpos2.y = ppos.y;
- X }
- X if((action == XK_Down) || (action == XK_j) || (action == XK_J)) {
- X tpos1.x = ppos.x+1;
- X tpos2.x = ppos.x+2;
- X tpos1.y = tpos2.y = ppos.y;
- X }
- X if((action == XK_Left) || (action == XK_h) || (action == XK_H)) {
- X tpos1.y = ppos.y-1;
- X tpos2.y = ppos.y-2;
- X tpos1.x = tpos2.x = ppos.x;
- X }
- X if((action == XK_Right) || (action == XK_l) || (action == XK_L)) {
- X tpos1.y = ppos.y+1;
- X tpos2.y = ppos.y+2;
- X tpos1.x = tpos2.x = ppos.x;
- X }
- X tc = map[tpos1.x][tpos1.y];
- X if((tc == packet) || (tc == save)) {
- X if(!stop_at_object) {
- X if(map[tpos2.x][tpos2.y] == ground)
- X ret = (tc == save) ? UNSAVE : PUSH;
- X else if(map[tpos2.x][tpos2.y] == store)
- X ret = (tc == save) ? STOREPUSH : SAVE;
- X else
- X ret = 0;
- X } else
- X ret = 0;
- X } else if(tc == ground)
- X ret = MOVE;
- X else if(tc == store)
- X ret = STOREMOVE;
- X else
- X ret = 0;
- X return ret;
- X}
- X
- X/* actually update the internal map with the results of the move */
- Xvoid DoMove(short moveaction)
- X{
- X map[ppos.x][ppos.y] = (map[ppos.x][ppos.y] == player) ? ground : store;
- X switch( moveaction) {
- X case MOVE:
- X map[tpos1.x][tpos1.y] = player;
- X break;
- X case STOREMOVE:
- X map[tpos1.x][tpos1.y] = playerstore;
- X break;
- X case PUSH:
- X map[tpos2.x][tpos2.y] = map[tpos1.x][tpos1.y];
- X map[tpos1.x][tpos1.y] = player;
- X pushes++;
- X break;
- X case UNSAVE:
- X map[tpos2.x][tpos2.y] = packet;
- X map[tpos1.x][tpos1.y] = playerstore;
- X pushes++;
- X savepack--;
- X break;
- X case SAVE:
- X map[tpos2.x][tpos2.y] = save;
- X map[tpos1.x][tpos1.y] = player;
- X savepack++;
- X pushes++;
- X break;
- X case STOREPUSH:
- X map[tpos2.x][tpos2.y] = save;
- X map[tpos1.x][tpos1.y] = playerstore;
- X pushes++;
- X break;
- X }
- X moves++;
- X DisplayMoves();
- X DisplayPushes();
- X DisplaySave();
- X MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y, 1);
- X MapChar(map[tpos1.x][tpos1.y], tpos1.x, tpos1.y, 1);
- X MapChar(map[tpos2.x][tpos2.y], tpos2.x, tpos2.y, 1);
- X ppos.x = tpos1.x;
- X ppos.y = tpos1.y;
- X SyncScreen();
- X}
- X
- X/* undo the most recently done move */
- Xvoid UndoMove(void)
- X{
- X map[lastppos.x][lastppos.y] = lppc;
- X map[lasttpos1.x][lasttpos1.y] = ltp1c;
- X map[lasttpos2.x][lasttpos2.y] = ltp2c;
- X ppos.x = lastppos.x;
- X ppos.y = lastppos.y;
- X switch( lastaction) {
- X case MOVE:
- X moves--;
- X break;
- X case STOREMOVE:
- X moves--;
- X break;
- X case PUSH:
- X moves--;
- X pushes--;
- X break;
- X case UNSAVE:
- X moves--;
- X pushes--;
- X savepack++;
- X break;
- X case SAVE:
- X moves--;
- X pushes--;
- X savepack--;
- X break;
- X case STOREPUSH:
- X moves--;
- X pushes--;
- X break;
- X }
- X DisplayMoves();
- X DisplayPushes();
- X DisplaySave();
- X MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y, 1);
- X MapChar(map[lasttpos1.x][lasttpos1.y], lasttpos1.x, lasttpos1.y, 1);
- X MapChar(map[lasttpos2.x][lasttpos2.y], lasttpos2.x, lasttpos2.y, 1);
- X SyncScreen();
- X}
- X
- X/* Function used by the help pager. We ONLY want to flip pages if a key
- X * key is pressed.. We want to exit the help pager if ENTER is pressed.
- X * As above, <shift> and <control> and other such fun keys are NOT counted
- X * as a keypress.
- X */
- XBoolean WaitForEnter(void)
- X{
- X KeySym keyhit;
- X char buf[1];
- X int bufs = 1;
- X XComposeStatus compose;
- X
- X while (1) {
- X XNextEvent(dpy, &xev);
- X switch(xev.type) {
- X case Expose:
- X RedisplayScreen();
- X break;
- X case KeyPress:
- X buf[0] = '\0';
- X XLookupString(&xev.xkey, buf, bufs, &keyhit, &compose);
- X if(buf[0]) {
- X return (keyhit == XK_Return) ? _true_ : _false_;
- X }
- X break;
- X default:
- X break;
- X }
- X }
- X}
- X
- X/* find the shortest path to the target via a fill search algorithm */
- Xvoid FindTarget(int px, int py, int pathlen)
- X{
- X if(!(ISCLEAR(px, py) || ISPLAYER(px, py)))
- X return;
- X if(findmap[px][py] <= pathlen)
- X return;
- X
- X findmap[px][py] = pathlen++;
- X
- X if((px == ppos.x) && (py == ppos.y))
- X return;
- X
- X FindTarget(px - 1, py, pathlen);
- X FindTarget(px + 1, py, pathlen);
- X FindTarget(px, py - 1, pathlen);
- X FindTarget(px, py + 1, pathlen);
- X}
- X
- X/* Do all the fun movement stuff with the mouse */
- Xvoid MoveMan(int mx, int my)
- X{
- X int x, y;
- X
- X shift = cntrl = _false_;
- X
- X /* reverse the screen coords to get the internal coords (yes, I know this
- X * should be fixed) RSN */
- X y = wX(mx);
- X x = wY(my);
- X
- X /* make sure we are within the bounds of the array */
- X if((x < 0) || (x > MAXROW) || (y < 0) || (y > MAXCOL)) {
- X HelpMessage();
- X return;
- X }
- X
- X if(ISPACKET(x, y)) {
- X HelpMessage();
- X return;
- X }
- X /* if we clicked on the player or a wall (or an object but that was already
- X * handled) the we don't want to move.
- X */
- X if(!ISCLEAR(x, y)) {
- X HelpMessage();
- X return;
- X }
- X if (!RunTo(x, y)) HelpMessage();
- X}
- X
- X/* Return whether (x,y) is on the board */
- XBoolean ValidPosn(int x, int y)
- X{
- X return (x >= 0) && (x <= MAXROW) && (y >= 0) && (y <= MAXCOL);
- X}
- X
- X/*
- X Find the object at a position orthogonal to (x, y) that is
- X closest to (ppos.x, ppos.y), is separated from (x,y) only
- X by empty spaces, and has the player or an empty space on the far
- X side of (x,y), and is not directly opposite the destination space
- X from the player; place its coordinates in (*ox, *oy) and return _true_.
- X If no such object exists, return _false_.
- X*/
- XBoolean FindOrthogonalObject(int x, int y, int *ox, int *oy)
- X{
- X int dir;
- X int bestdist = BADMOVE;
- X Boolean foundOne = _false_;
- X for (dir = 0; dir < 4; dir++) {
- X int dx, dy, x1, y1, dist;
- X switch(dir) {
- X default: dx = 1; dy = 0; break; /* use "default" to please gcc */
- X case 1: dx = -1; dy = 0; break;
- X case 2: dx = 0; dy = 1; break;
- X case 3: dx = 0; dy = -1; break;
- X }
- X if ((ppos.x == x && ((ppos.y - y)*dy) < 0)
- X || (ppos.y == y && ((ppos.x - x)*dx) < 0)) continue;
- X /* Eliminate case where player would push in the opposite
- X direction. */
- X x1 = x; y1 = y;
- X while (ValidPosn(x1, y1)) {
- X x1 += dx; y1 += dy;
- X dist = abs(ppos.x - x1) + abs(ppos.y - y1);
- X if (dist <= bestdist && ISPACKET(x1, y1) &&
- X (ISPLAYER(x1 + dx, y1 + dy) || ISCLEAR(x1 + dx, y1 + dy))) {
- X if (dist < bestdist) {
- X bestdist = dist;
- X *ox = x1;
- X *oy = y1;
- X foundOne = _true_;
- X break;
- X } else {
- X foundOne = _false_; /* found one that was just as good */
- X }
- X }
- X if (!ISCLEAR(x1, y1)) break;
- X }
- X }
- X return foundOne ? _true_ : _false_ ;
- X}
- X
- X#define DEBUGPUSH 0
- X
- X/* Push a nearby stone to the position indicated by (mx, my). */
- Xvoid PushMan(int mx, int my)
- X{
- X int i, x, y, ox, oy, dist;
- X
- X shift = cntrl = _false_;
- X
- X /* reverse the screen coords to get the internal coords (yes, I know this
- X * should be fixed) RSN */
- X y = wX(mx);
- X x = wY(my);
- X
- X /* make sure we are within the bounds of the array */
- X if(!ValidPosn(x,y)) {
- X HelpMessage();
- X#if DEBUGPUSH
- X printf("Outside array\n");
- X#endif
- X return;
- X }
- X
- X /* if we are clicking on an object, and are right next to it, we want to
- X * move the object.
- X */
- X if(ISPACKET(x, y)) {
- X if(ISPLAYER(x - 1, y))
- X MakeMove(XK_Down);
- X else if(ISPLAYER(x + 1, y))
- X MakeMove(XK_Up);
- X else if(ISPLAYER(x, y - 1))
- X MakeMove(XK_Right);
- X else if(ISPLAYER(x, y + 1))
- X MakeMove(XK_Left);
- X else {
- X /* we weren't right next to the object */
- X HelpMessage();
- X return;
- X }
- X return;
- X }
- X
- X /* if we clicked on the player or a wall (or an object but that was already
- X * handled) the we don't want to move.
- X */
- X if(!ISCLEAR(x, y)) {
- X HelpMessage();
- X#if DEBUGPUSH
- X printf("Not a clear space\n"); */
- X#endif
- X return;
- X }
- X
- X#if 0
- X if (abs(x - ppos.x) < 2 && abs(ppos.y - y) < 2) {
- X#if DEBUGPUSH
- X printf("Too close to destination (%d, %d)\n",
- X abs(x - ppos.x) , abs(y - ppos.y));
- X#endif
- X HelpMessage();
- X /* Player must be sufficiently far from the destination that there
- X can be no ambiguity about which stone to push */
- X return;
- X }
- X#endif
- X
- X if (!FindOrthogonalObject(x, y, &ox, &oy)) {
- X HelpMessage();
- X#if DEBUGPUSH
- X printf("Can't find packet\n");
- X#endif
- X return;
- X }
- X
- X assert(x == ox || y == oy);
- X dist = abs(ox - x) + abs(oy - y);
- X
- X if (x > ox) ox--;
- X if (x < ox) ox++;
- X if (y > oy) oy--;
- X if (y < oy) oy++;
- X
- X /* (ox,oy) now denotes the place we need to run to to be able to push */
- X
- X if (ox != ppos.x || oy != ppos.y) {
- X if (!ISCLEAR(ox, oy)) {
- X#if DEBUGPUSH
- X printf("Can't move into an occupied space! (%d,%d)\n",
- X ox - ppos.x, oy - ppos.y);
- X#endif
- X HelpMessage();
- X return;
- X }
- X if (!RunTo(ox, oy)) {
- X HelpMessage();
- X#if DEBUGPUSH
- X printf("Can't get in position to push\n"); */
- X#endif
- X return;
- X }
- X }
- X assert(ppos.x == ox && ppos.y == oy);
- X
- X for (i = 0; i < dist; i++) {
- X if (ppos.x < x) MakeMove(XK_Down);
- X if (ppos.x > x) MakeMove(XK_Up);
- X if (ppos.y < y) MakeMove(XK_Right);
- X if (ppos.y > y) MakeMove(XK_Left);
- X }
- X}
- X
- X/* Move the player to the position (x,y), if possible. Return _true_
- X iff successful. The position (x,y) must be clear.
- X*/
- XBoolean RunTo(int x, int y)
- X{
- X int i,j,cx,cy;
- X /* Fill the trace map */
- X for(i = 0; i <= MAXROW; i++)
- X for (j = 0; j <= MAXCOL; j++)
- X findmap[i][j] = BADMOVE;
- X /* flood fill search to find a shortest path to the push point. */
- X FindTarget(x, y, 0);
- X
- X /* if we didn't make it back to the players position, there is no valid path
- X * to that place.
- X */
- X if(findmap[ppos.x][ppos.y] == BADMOVE) {
- X return _false_;
- X } else {
- X /* we made it back, so let's walk the path we just built up */
- X cx = ppos.x;
- X cy = ppos.y;
- X while(findmap[cx][cy]) {
- X if(findmap[cx - 1][cy] == (findmap[cx][cy] - 1)) {
- X MakeMove(XK_Up);
- X cx--;
- X } else if(findmap[cx + 1][cy] == (findmap[cx][cy] - 1)) {
- X MakeMove(XK_Down);
- X cx++;
- X } else if(findmap[cx][cy - 1] == (findmap[cx][cy] - 1)) {
- X MakeMove(XK_Left);
- X cy--;
- X } else if(findmap[cx][cy + 1] == (findmap[cx][cy] - 1)) {
- X MakeMove(XK_Right);
- X cy++;
- X } else {
- X /* if we get here, something is SERIOUSLY wrong, so we should abort */
- X abort();
- X }
- X }
- X }
- X return _true_;
- X}
- X
- X
- Xstatic void InitMoveStack()
- X{
- X move_stack_sp = -1;
- X move_stack[0].moves = moves;
- X move_stack[0].pushes = moves;
- X move_stack[0].saved = savepack;
- X memcpy(prev_map, map, sizeof(map));
- X}
- X
- X/* Add a record to the move stack that records the changes since the
- X last map state (which is stored in "prev_map"). Update "prev_map"
- X to contain the current map so the next call to "RecordChange()"
- X will perform correctly.
- X
- X If the stack runs out of space, dump the oldest half of the
- X saved moves and continue. Undoing past that point will jump
- X back to the beginning of the level. If the user is using the
- X mouse or any skill, should never happen.
- X*/
- Xstatic void RecordChange()
- X{
- X struct move_r *r = &move_stack[++move_stack_sp];
- X int x,y, ndeltas = 0;
- X assert(move_stack_sp < STACKDEPTH);
- X if (move_stack_sp == STACKDEPTH - 1) {
- X int shift = STACKDEPTH/2;
- X memcpy(&move_stack[0], &move_stack[shift],
- X sizeof(struct move_r) * (STACKDEPTH - shift));
- X move_stack_sp -= shift;
- X r -= shift;
- X }
- X r[1].moves = moves;
- X r[1].pushes = pushes;
- X r[1].saved = savepack;
- X r[1].px = ppos.x;
- X r[1].py = ppos.y;
- X for (x = 0; x <= MAXROW; x++) {
- X for (y = 0; y <= MAXROW; y++) {
- X if (map[x][y] != prev_map[x][y]) {
- X assert(ndeltas < MAXDELTAS);
- X r->deltas[ndeltas].x = x;
- X r->deltas[ndeltas].y = y;
- X r->deltas[ndeltas].newchar = map[x][y];
- X r->deltas[ndeltas].oldchar = prev_map[x][y];
- X ndeltas++;
- X#if 0
- X printf("Change (%d,%d) %c->%c\n", x, y, prev_map[x][y],
- X map[x][y]);
- X#endif
- X }
- X }
- X }
- X r->ndeltas = ndeltas;
- X if (ndeltas == 0) {
- X move_stack_sp--; /* Why push an identical entry? */
- X }
- X memcpy(prev_map, map, sizeof(map));
- X}
- X
- Xstatic void UndoChange()
- X{
- X if (move_stack_sp <= 0) {
- X int ret;
- X InitMoveStack();
- X ret = ReadScreen();
- X moves = pushes = 0;
- X if (ret) {
- X fprintf(stderr, "Can't read screen file\n");
- X exit(-1);
- X }
- X } else {
- X struct move_r *r = &move_stack[move_stack_sp];
- X int i;
- X moves = r->moves;
- X pushes = r->pushes;
- X savepack = r->saved;
- X ppos.x = r->px;
- X ppos.y = r->py;
- X for (i = 0; i<r->ndeltas; i++) {
- X#if 0
- X printf("Applying reverse change: (%d,%d) %c->%c\n",
- X r->deltas[i].x, r->deltas[i].y,
- X map[r->deltas[i].x][r->deltas[i].y], r->deltas[i].oldchar);
- X#endif
- X map[r->deltas[i].x][r->deltas[i].y] = r->deltas[i].oldchar;
- X }
- X move_stack_sp--;
- X memcpy(prev_map, map, sizeof(map));
- X }
- X}
- END_OF_FILE
- if test 17974 -ne `wc -c <'xsokoban-3.0/play.c'`; then
- echo shar: \"'xsokoban-3.0/play.c'\" unpacked with wrong size!
- fi
- # end of 'xsokoban-3.0/play.c'
- fi
- if test ! -d 'xsokoban-3.0/saves' ; then
- echo shar: Creating directory \"'xsokoban-3.0/saves'\"
- mkdir 'xsokoban-3.0/saves'
- fi
- if test -f 'xsokoban-3.0/score.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'xsokoban-3.0/score.c'\"
- else
- echo shar: Extracting \"'xsokoban-3.0/score.c'\" \(9121 characters\)
- sed "s/^X//" >'xsokoban-3.0/score.c' <<'END_OF_FILE'
- X#include "config_local.h"
- X
- X#include <stdio.h>
- X#include <signal.h>
- X#include <sys/types.h>
- X#include <assert.h>
- X#include <string.h>
- X#include <unistd.h>
- X#include <netinet/in.h>
- X#ifdef NEED_ENDIAN
- X#include <machine/endian.h> /* for ntohs */
- X#endif
- X
- X#include "externs.h"
- X#include "globals.h"
- X
- Xextern FILE *fopen();
- X
- Xextern char *username;
- X
- Xstatic short scoreentries;
- Xstatic struct st_entry {
- X char user[MAXUSERNAME];
- X unsigned short lv, pad1, mv, pad2, ps, pad3;
- X} scoretable[MAXSCOREENTRIES];
- X
- Xstatic FILE *scorefile;
- Xstatic long sfdbn;
- X
- Xshort LockScore(void)
- X{
- X int i, fd;
- X
- X for (i = 0; i < 10; i++) {
- X fd = creat(LOCKFILE, 0666);
- X if (fd < 0)
- X sleep(1);
- X else
- X break;
- X }
- X
- X if (fd < 0) {
- X /* assume that the last process to muck with the score file */
- X /* is dead */
- X /* XXX Should really be checking the datestamps on the score*/
- X /* file to make sure some other process hasn't mucked with */
- X /* in the last 10 seconds! */
- X unlink(LOCKFILE);
- X fd = creat(LOCKFILE, 0666);
- X }
- X
- X if (fd < 0)
- X return E_WRITESCORE;
- X else {
- X close(fd);
- X return 0;
- X }
- X}
- X
- Xvoid UnlockScore(void)
- X{
- X unlink(LOCKFILE);
- X}
- X
- X/* print out the score list */
- Xshort OutputScore(void)
- X{
- X short ret;
- X
- X if ((ret = LockScore()))
- X return ret;
- X
- X if ((ret = ReadScore()) == 0)
- X ShowScore();
- X UnlockScore();
- X return ((ret == 0) ? E_ENDGAME : ret);
- X}
- X
- X/* create a new score file */
- Xshort MakeNewScore(void)
- X{
- X short ret = 0;
- X
- X if ((ret = LockScore()))
- X return ret;
- X
- X scoreentries = 0;
- X
- X if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
- X ret = E_FOPENSCORE;
- X else {
- X sfdbn = fileno(scorefile);
- X if (write(sfdbn, &scoreentries, 2) != 2)
- X ret = E_WRITESCORE;
- X fclose(scorefile);
- X }
- X UnlockScore();
- X return ((ret == 0) ? E_ENDGAME : ret);
- X}
- X
- X/* get the players current level based on the level they last scored on */
- Xshort GetUserLevel(short *lv)
- X{
- X short ret = 0, pos;
- X
- X if ((ret = LockScore()))
- X return ret;
- X
- X if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
- X ret = E_FOPENSCORE;
- X else {
- X if ((ret = ReadScore()) == 0)
- X *lv = ((pos = FindUser()) > -1) ? scoretable[pos].lv + 1 : 1;
- X }
- X UnlockScore();
- X return (ret);
- X}
- X
- X/* Add a new score to the score file. Show the current scores if "show". */
- Xshort Score(Boolean show)
- X{
- X short ret;
- X
- X if ((ret = LockScore()))
- X return ret;
- X if ((ret = ReadScore()) == 0)
- X if ((ret = MakeScore()) == 0)
- X if ((ret = WriteScore()) == 0)
- X if (show) ShowScore();
- X UnlockScore();
- X return ((ret == 0) ? E_ENDGAME : ret);
- X}
- X
- Xvoid ntohs_entry(struct st_entry *entry)
- X{
- X entry->lv = ntohs(entry->lv);
- X entry->mv = ntohs(entry->mv);
- X entry->ps = ntohs(entry->ps);
- X}
- X
- X/* read in an existing score file. Uses the ntoh() and hton() functions
- X * so that the score files transfer across systems.
- X */
- Xshort ReadScore(void)
- X{
- X short ret = 0;
- X long tmp;
- X
- X if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
- X ret = E_FOPENSCORE;
- X else {
- X sfdbn = fileno(scorefile);
- X if (read(sfdbn, &scoreentries, 2) != 2)
- X ret = E_READSCORE;
- X else {
- X scoreentries = ntohs(scoreentries);
- X tmp = scoreentries * sizeof(scoretable[0]);
- X if (read(sfdbn, &(scoretable[0]), tmp) != tmp)
- X ret = E_READSCORE;
- X
- X /* swap up for little-endian machines */
- X for (tmp = 0; tmp < scoreentries; tmp++) ntohs_entry(&scoretable[tmp]);
- X }
- X fclose(scorefile);
- X }
- X return ret;
- X}
- X
- X/* Return the solution rank for table index "j". The solution rank for
- X an entry is one greater than the number of entries that are better
- X than it, unless there is a better or equal solution that is by the
- X same person, in which case the solution rank is at least "BADSOLN".
- X If two solutions are equal, the one that was arrived at first, and
- X thus has a lower table index, is considered to be better.
- X One solution is at least as good as another solution if it is at
- X least as good in numbers of moves and pushes. Note that
- X non-comparable solutions may exist.
- X
- X The array "ignore" indicates that some scoretable entries should
- X be ignored for the purpose of computing rank.
- X*/
- X#define BADSOLN 100
- Xint SolnRank(int j, Boolean *ignore)
- X{
- X int i, rank = 1;
- X unsigned short level = scoretable[j].lv;
- X for (i = 0; i < j; i++) {
- X if ((!ignore || !ignore[i]) && scoretable[i].lv == level) {
- X if (scoretable[i].mv <= scoretable[j].mv &&
- X scoretable[i].ps <= scoretable[j].ps)
- X {
- X if (0 == strcmp(scoretable[i].user,
- X scoretable[j].user))
- X rank = BADSOLN;
- X else
- X rank++;
- X }
- X }
- X }
- X return rank;
- X}
- X
- X/* Removes all score entries for a user who has multiple entries,
- X * that are for a level below the user's top level, and that are not "best
- X * solutions" as defined by "SolnRank". Also removes duplicate entries
- X * for a level that is equal to the user's top level, but which are not
- X * the user's best solution as defined by table position.
- X *
- X * The current implementation is O(n^2) in the number of actual score entries.
- X * A hash table would fix this.
- X */
- X
- Xvoid CleanupScoreTable()
- X{
- X int i;
- X Boolean deletable[MAXSCOREENTRIES];
- X for (i = 0; i < scoreentries; i++) {
- X deletable[i] = _false_;
- X if (SolnRank(i, deletable) > MAXSOLNRANK) {
- X char *user = scoretable[i].user;
- X int j;
- X for (j = 0; j < i; j++) {
- X if (0 == strcmp(scoretable[j].user, user))
- X deletable[i] = _true_;
- X }
- X }
- X }
- X FlushDeletedScores(deletable);
- X}
- X
- X/* Deletes entries from the score table for which the boolean array
- X contains true.
- X*/
- Xvoid FlushDeletedScores(Boolean delete[])
- X{
- X int i, k = 0;
- X for (i = 0; i < scoreentries; i++) {
- X if (i != k) CopyEntry(k, i);
- X if (!delete[i]) k++;
- X }
- X scoreentries = k;
- X}
- X
- X/* Adds a new user score to the score table, if appropriate. Users' top
- X * level scores, and the best scores for a particular level (in moves and
- X * pushes, separately considered), are always preserved.
- X */
- Xshort MakeScore(void)
- X{
- X short pos, i;
- X
- X pos = FindPos(); /* find the new score position */
- X if (pos > -1) { /* score table not empty */
- X for (i = scoreentries; i > pos; i--)
- X CopyEntry(i, i - 1);
- X } else {
- X pos = scoreentries;
- X }
- X
- X strcpy(scoretable[pos].user, username);
- X scoretable[pos].lv = scorelevel;
- X scoretable[pos].mv = scoremoves;
- X scoretable[pos].ps = scorepushes;
- X scoreentries++;
- X
- X CleanupScoreTable();
- X if (scoreentries == MAXSCOREENTRIES)
- X return E_TOMUCHSE;
- X else
- X return 0;
- X}
- X
- X
- X/* searches the score table to find a specific player. */
- Xshort FindUser(void)
- X{
- X short i;
- X Boolean found = _false_;
- X
- X for (i = 0; (i < scoreentries) && (!found); i++)
- X found = (strcmp(scoretable[i].user, username) == 0);
- X return ((found) ? i - 1 : -1);
- X}
- X
- X/* finds the position for a new score in the score table */
- Xshort FindPos(void)
- X{
- X short i;
- X Boolean found = _false_;
- X
- X for (i = 0; (i < scoreentries) && (!found); i++)
- X found = ((scorelevel > scoretable[i].lv) ||
- X ((scorelevel == scoretable[i].lv) &&
- X (scoremoves < scoretable[i].mv)) ||
- X ((scorelevel == scoretable[i].lv) &&
- X (scoremoves == scoretable[i].mv) &&
- X (scorepushes < scoretable[i].ps)));
- X return ((found) ? i - 1 : -1);
- X}
- X
- X/* writes out the score table. It uses ntoh() and hton() functions to make
- X * the scorefile transfer across systems.
- X */
- Xshort WriteScore(void)
- X{
- X short ret = 0;
- X long tmp;
- X
- X if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
- X ret = E_FOPENSCORE;
- X else {
- X sfdbn = fileno(scorefile);
- X scoreentries = htons(scoreentries);
- X if (write(sfdbn, &scoreentries, 2) != 2)
- X ret = E_WRITESCORE;
- X else {
- X scoreentries = ntohs(scoreentries);
- X
- X /* swap around for little-endian machines */
- X for (tmp = 0; tmp < scoreentries; tmp++) {
- X scoretable[tmp].lv = htons(scoretable[tmp].lv);
- X scoretable[tmp].mv = htons(scoretable[tmp].mv);
- X scoretable[tmp].ps = htons(scoretable[tmp].ps);
- X }
- X tmp = scoreentries * sizeof(scoretable[0]);
- X if (write(sfdbn, &(scoretable[0]), tmp) != tmp)
- X ret = E_WRITESCORE;
- X
- X /* and swap back for the rest of the run ... */
- X for (tmp = 0; tmp < scoreentries; tmp++) {
- X scoretable[tmp].lv = ntohs(scoretable[tmp].lv);
- X scoretable[tmp].mv = ntohs(scoretable[tmp].mv);
- X scoretable[tmp].ps = ntohs(scoretable[tmp].ps);
- X }
- X }
- X fclose(scorefile);
- X }
- X return ret;
- X}
- X
- X
- X/* displays the score table to the user */
- Xvoid ShowScore(void)
- X{
- X register i;
- X
- X fprintf(stdout, "Rank User Level Moves Pushes\n");
- X fprintf(stdout, "============================================\n");
- X for (i = 0; i < scoreentries; i++) {
- X int rank = SolnRank(i, 0);
- X if (rank <= MAXSOLNRANK) fprintf(stdout, "%4d", rank);
- X else fprintf(stdout, " ");
- X fprintf(stdout, "%10s %8d %8d %8d\n", scoretable[i].user,
- X scoretable[i].lv, scoretable[i].mv, scoretable[i].ps);
- X }
- X}
- X
- X/* duplicates a score entry */
- Xvoid CopyEntry(short i1, short i2)
- X{
- X strcpy(scoretable[i1].user, scoretable[i2].user);
- X scoretable[i1].lv = scoretable[i2].lv;
- X scoretable[i1].mv = scoretable[i2].mv;
- X scoretable[i1].ps = scoretable[i2].ps;
- X}
- END_OF_FILE
- if test 9121 -ne `wc -c <'xsokoban-3.0/score.c'`; then
- echo shar: \"'xsokoban-3.0/score.c'\" unpacked with wrong size!
- fi
- # end of 'xsokoban-3.0/score.c'
- fi
- if test ! -d 'xsokoban-3.0/scores' ; then
- echo shar: Creating directory \"'xsokoban-3.0/scores'\"
- mkdir 'xsokoban-3.0/scores'
- fi
- if test ! -d 'xsokoban-3.0/screens' ; then
- echo shar: Creating directory \"'xsokoban-3.0/screens'\"
- mkdir 'xsokoban-3.0/screens'
- fi
- echo shar: End of archive 1 \(of 4\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 2 3 4 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 4 archives.
- rm -f ark[1-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
- --
- // chris@Sterling.COM | Send comp.sources.x submissions to:
- \X/ Amiga: The only way to fly! | sources-x@sterling.com
- "It's intuitively obvious to the most casual observer..."
- GCS d++(--) -p+ c++ !l u++ e+ m+(-) s++/++ n h--- f+ g+++ w+ t++ r+ y+
-