home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / x / volume21 / xsokoban / part01 < prev    next >
Encoding:
Text File  |  1993-12-18  |  52.6 KB  |  1,922 lines

  1. Newsgroups: comp.sources.x
  2. From: andru@tonic.lcs.mit.edu (Andrew Myers)
  3. Subject: v21i058:  xsokoban - Simple but complex move the stones game, Part01/04
  4. Message-ID: <csx-v21i058=xsokoban.170010@sparky.Sterling.COM>
  5. X-Md4-Signature: 698e17aef2785a90478d2d876ef2baad
  6. Sender: chris@sparky.sterling.com (Chris Olson)
  7. Organization: Sterling Software
  8. Date: Sat, 18 Dec 1993 23:00:32 GMT
  9. Approved: chris@sterling.com
  10.  
  11. Submitted-by: andru@tonic.lcs.mit.edu (Andrew Myers)
  12. Posting-number: Volume 21, Issue 58
  13. Archive-name: xsokoban/part01
  14. Environment: X11, ansi
  15.  
  16. This is the third release of XSokoban. XSokoban is a very tricky puzzle
  17. game with 90 difficult levels to solve. Once you've solved a level,
  18. there's still the puzzle of solving it optimally.  The code has been
  19. tested on a variety of operating systems and compilers.
  20.  
  21. Improvements over the second version are:
  22.  
  23.    - better mouse user interface
  24.    - score ranking
  25.    - infinite undo capability
  26.  
  27. - Andrew Myers (andru@lcs.mit.edu)
  28.  
  29.  
  30. ----- cut here -----
  31. #! /bin/sh
  32. # This is a shell archive.  Remove anything before this line, then feed it
  33. # into a shell via "sh file" or similar.  To overwrite existing files,
  34. # type "sh file -c".
  35. # Contents:  xsokoban-3.0 xsokoban-3.0/bitmaps
  36. #   xsokoban-3.0/bitmaps/defaults xsokoban-3.0/display.c
  37. #   xsokoban-3.0/externs.h xsokoban-3.0/play.c xsokoban-3.0/saves
  38. #   xsokoban-3.0/score.c xsokoban-3.0/scores xsokoban-3.0/screens
  39. # Wrapped by chris@sparky on Sat Dec 18 16:54:28 1993
  40. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  41. echo If this archive is complete, you will see the following message:
  42. echo '          "shar: End of archive 1 (of 4)."'
  43. if test ! -d 'xsokoban-3.0' ; then
  44.     echo shar: Creating directory \"'xsokoban-3.0'\"
  45.     mkdir 'xsokoban-3.0'
  46. fi
  47. if test ! -d 'xsokoban-3.0/bitmaps' ; then
  48.     echo shar: Creating directory \"'xsokoban-3.0/bitmaps'\"
  49.     mkdir 'xsokoban-3.0/bitmaps'
  50. fi
  51. if test ! -d 'xsokoban-3.0/bitmaps/defaults' ; then
  52.     echo shar: Creating directory \"'xsokoban-3.0/bitmaps/defaults'\"
  53.     mkdir 'xsokoban-3.0/bitmaps/defaults'
  54. fi
  55. if test -f 'xsokoban-3.0/display.c' -a "${1}" != "-c" ; then 
  56.   echo shar: Will not clobber existing file \"'xsokoban-3.0/display.c'\"
  57. else
  58.   echo shar: Extracting \"'xsokoban-3.0/display.c'\" \(17310 characters\)
  59.   sed "s/^X//" >'xsokoban-3.0/display.c' <<'END_OF_FILE'
  60. X#include <stdio.h>
  61. X#include <string.h>
  62. X#include <assert.h>
  63. X
  64. X#include "externs.h"
  65. X#include "globals.h"
  66. X#include "defaults.h"
  67. X#include "help.h"
  68. X
  69. X/* mnemonic defines to help orient some of the text/line drawing, sizes */
  70. X#define HELPLINE ((bit_height * MAXROW) + 30)
  71. X#define STATUSLINE ((bit_height * MAXROW) + 5)
  72. X#define HELP_H (bit_height * MAXROW)
  73. X#define HELP_W (bit_width * MAXCOL)
  74. X
  75. X/* useful globals */
  76. XDisplay *dpy;
  77. XWindow win;
  78. Xint scr;
  79. XGC gc, rgc, drgc;
  80. XAtom wm_delete_window, wm_protocols;
  81. XXFontStruct *finfo;
  82. Xunsigned int width, height, depth, bit_height, bit_width;
  83. XBoolean display_alloc = False, font_alloc = False, gc_alloc = False,
  84. X        pix_alloc = False;
  85. XBoolean optwalls;
  86. XColormap cmap;
  87. XCursor this_curs;
  88. Xstatic Pixmap help[HELP_PAGES], floor;
  89. Xstatic Pixmap blank, work, man, saveman, goal, object, treasure, walls[NUM_WALLS];
  90. Xint hlpscrn = -1;
  91. Xchar buf[500];
  92. X
  93. Xextern char *progname;
  94. Xextern char map[MAXROW+1][MAXCOL+1];
  95. Xextern short rows, cols, level, moves, pushes, packets, savepack;
  96. Xextern char *bitpath;
  97. X
  98. X/* names of the fancy wall bitmap files.  If you define a set of fancy
  99. X * wall bitmaps, they must use these names
  100. X */
  101. Xchar *wallname[] = {
  102. X "lonewall.xbm", "southwall.xbm", "westwall.xbm", "llcornerwall.xbm",
  103. X "northwall.xbm", "vertiwall.xbm", "ulcornerwall.xbm", "west_twall.xbm",
  104. X "eastwall.xbm", "lrcornerwall.xbm", "horizwall.xbm", "south_twall.xbm",
  105. X "urcornerwall.xbm", "east_twall.xbm", "north_twall.xbm", "centerwall.xbm"
  106. X};
  107. X
  108. X/* Do all the nasty stuff like makeing the windows, setting all the defaults
  109. X * creating all the pixmaps, loading everything, and mapping the window.
  110. X * this does NOT do the XOpenDisplay() so that the -display switch can be
  111. X * handled cleanly.
  112. X */
  113. Xshort InitX(void)
  114. X{
  115. X  int i;
  116. X  Boolean reverse = _false_, tmpwalls = _false_;
  117. X  char *rval;
  118. X  unsigned long fore, back, bord, curs, gc_mask;
  119. X  XSizeHints szh;
  120. X  XWMHints wmh;
  121. X  XSetWindowAttributes wattr;
  122. X  XClassHint clh;
  123. X  XGCValues val, reval;
  124. X  XTextProperty wname, iname;
  125. X  XColor cfg, cbg;
  126. X  Atom protocols[1];
  127. X
  128. X  /* these are always needed */
  129. X  scr = DefaultScreen(dpy);
  130. X  cmap = DefaultColormap(dpy, scr);
  131. X  depth = DefaultDepth(dpy, scr);
  132. X
  133. X  /* here is where we figure out the resources and set the defaults.
  134. X   * resources can be either on the command line, or in your .Xdefaults/
  135. X   * .Xresources files.  They are read in and parsed in main.c, but used
  136. X   * here.
  137. X   */
  138. X  rval = GetResource(FONT);
  139. X  if(rval == (char *)0)
  140. X    rval = DEF_FONT;
  141. X  finfo = XLoadQueryFont(dpy, rval);
  142. X  if(finfo == (XFontStruct *)0)
  143. X    return E_NOFONT;
  144. X  font_alloc = _true_;
  145. X
  146. X  rval = GetResource(REVERSE);
  147. X  if(rval != (char *)0) {
  148. X    reverse = StringToBoolean(rval);
  149. X  }
  150. X
  151. X  if(!GetColorResource(FOREG, &fore))
  152. X    fore = BlackPixel(dpy, scr);
  153. X
  154. X  if(!GetColorResource(BACKG, &back))
  155. X    back = WhitePixel(dpy, scr);
  156. X
  157. X  if(reverse) {
  158. X    unsigned long t;
  159. X    t = fore;
  160. X    fore = back;
  161. X    back = t;
  162. X  }
  163. X
  164. X  if(!GetColorResource(BORDER, &bord))
  165. X    bord = fore;
  166. X  if(!GetColorResource(CURSOR, &curs))
  167. X    curs = fore;
  168. X
  169. X  bitpath = GetResource(BITDIR);
  170. X  rval = GetResource(WALLS);
  171. X  if(rval != (char *)0)
  172. X    tmpwalls = StringToBoolean(rval);
  173. X
  174. X  /* walls are funny.  if a alternate bitpath has been defined, assume
  175. X   * !fancywalls unless explicitly told fancy walls.  If the default 
  176. X   * bitpath is being used, you can assume fancy walls.
  177. X   */
  178. X  if(bitpath && !tmpwalls)
  179. X    optwalls = _false_;
  180. X  else
  181. X    optwalls = _true_;
  182. X
  183. X  width = MAXCOL * DEF_BITW;
  184. X  height = MAXROW * DEF_BITH + 50;
  185. X
  186. X  wmh.initial_state = NormalState;
  187. X  wmh.input = True;
  188. X  wmh.flags = (StateHint | InputHint);
  189. X
  190. X  clh.res_class = clh.res_name = progname;
  191. X
  192. X  /* Make sure the window and icon names are set */
  193. X  if(!XStringListToTextProperty(&progname, 1, &wname))
  194. X    return E_NOMEM;
  195. X  if(!XStringListToTextProperty(&progname, 1, &iname))
  196. X    return E_NOMEM;
  197. X
  198. X  /* load in a cursor, and recolor it so it looks pretty */
  199. X  this_curs = XCreateFontCursor(dpy, DEF_CURSOR);
  200. X  cfg.pixel = curs;
  201. X  cbg.pixel = back;
  202. X  XQueryColor(dpy, cmap, &cfg);
  203. X  XQueryColor(dpy, cmap, &cbg);
  204. X  XRecolorCursor(dpy, this_curs, &cfg, &cbg);
  205. X
  206. X  /* set up the funky little window attributes */
  207. X  wattr.background_pixel = back;
  208. X  wattr.border_pixel = bord;
  209. X  wattr.backing_store = Always;
  210. X  wattr.event_mask = (KeyPressMask | ExposureMask | ButtonPressMask);
  211. X  wattr.cursor = this_curs;
  212. X
  213. X  /* whee, create the window, we create it with NO size so that we
  214. X   * can load in the bitmaps, we later resize it correctly
  215. X   */
  216. X  win = XCreateWindow(dpy, RootWindow(dpy, scr), 0, 0, width, height, 4,
  217. X              CopyFromParent, InputOutput, CopyFromParent,
  218. X                      (CWBackPixel | CWBorderPixel | CWBackingStore |
  219. X                       CWEventMask | CWCursor), &wattr);
  220. X
  221. X  /* this will set the bit_width and bit_height as well as loading
  222. X   * in the pretty little bitmaps
  223. X   */
  224. X  if(LoadBitmaps() == E_NOBITMAP)
  225. X    return E_NOBITMAP;
  226. X  blank = XCreatePixmap(dpy, win, bit_width, bit_height, 1);
  227. X  pix_alloc = _true_;
  228. X
  229. X  width = MAXCOL * bit_width;
  230. X  height = MAXROW * bit_height + 50;
  231. X  
  232. X  /* whee, resize the window with the correct size now that we know it */
  233. X  XResizeWindow(dpy, win, width, height);
  234. X
  235. X  /* set up the size hints, we don't want manual resizing allowed. */
  236. X  szh.min_width = szh.width = szh.max_width = width;
  237. X  szh.min_height = szh.height = szh.max_height = height;
  238. X  szh.x = szh.y = 0;
  239. X  szh.flags = (PSize | PPosition | PMinSize | PMaxSize);
  240. X
  241. X  /* now SET all those hints we create above */
  242. X  XSetWMNormalHints(dpy, win, &szh);
  243. X  XSetWMHints(dpy, win, &wmh);
  244. X  XSetClassHint(dpy, win, &clh);
  245. X  XSetWMName(dpy, win, &wname);
  246. X  XSetWMIconName(dpy, win, &iname);
  247. X
  248. X  /* Turn on WM_DELETE_WINDOW */
  249. X  wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
  250. X  wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", 0);
  251. X  protocols[0] = wm_delete_window;
  252. X  XSetWMProtocols(dpy, win, protocols, 1);
  253. X
  254. X  work = XCreatePixmap(dpy, win, width, height, depth);
  255. X
  256. X  /* set up all the relevant GC's */
  257. X  val.foreground = reval.background = fore;
  258. X  val.background = reval.foreground = back;
  259. X  val.function = reval.function = GXcopy;
  260. X  val.font = reval.font = finfo->fid;
  261. X  gc_mask = (GCForeground | GCBackground | GCFunction | GCFont);
  262. X  gc = XCreateGC(dpy, work, gc_mask, &val);
  263. X  rgc = XCreateGC(dpy, blank, gc_mask, &reval);
  264. X  drgc = XCreateGC(dpy, work, gc_mask, &reval);
  265. X  
  266. X  /* make the help windows and the working bitmaps */
  267. X  /* we need to do this down here since it requires GCs to be allocated */
  268. X  for(i = 0; i < HELP_PAGES; i++)
  269. X    help[i] = XCreatePixmap(dpy, win, HELP_W, HELP_H, depth);
  270. X  MakeHelpWindows();
  271. X  XFillRectangle(dpy, blank, rgc, 0, 0, bit_width, bit_height);
  272. X
  273. X  gc_alloc = _true_;
  274. X
  275. X  /* display the friendly little clear screen */
  276. X  ClearScreen();
  277. X  XMapWindow(dpy, win);
  278. X  RedisplayScreen();
  279. X  
  280. X  return 0;
  281. X}
  282. X
  283. X/* deallocate all the memory and structures used in creating stuff */
  284. Xvoid DestroyDisplay(void)
  285. X{
  286. X  int i;
  287. X
  288. X  /* kill the font */
  289. X  if(font_alloc)
  290. X    XFreeFont(dpy, finfo);
  291. X
  292. X  /* destroy everything allocted right around the gcs.  Help windows are
  293. X   * freed here cause they are created about the same time.  (Yes, I know
  294. X   * this could cause problems, it hasn't yet.
  295. X   */
  296. X  if(gc_alloc) {
  297. X    XFreeGC(dpy, gc);
  298. X    XFreeGC(dpy, rgc);
  299. X    XFreeGC(dpy, drgc);
  300. X    XFreePixmap(dpy, work);
  301. X    for (i = 0; i < HELP_PAGES; i++)
  302. X      XFreePixmap(dpy, help[i]);
  303. X  }
  304. X  /* free up all the allocated pix */
  305. X  if(pix_alloc) {
  306. X    XFreePixmap(dpy, man);
  307. X    XFreePixmap(dpy, saveman);
  308. X    XFreePixmap(dpy, goal);
  309. X    XFreePixmap(dpy, treasure);
  310. X    XFreePixmap(dpy, object);
  311. X    XFreePixmap(dpy, floor);
  312. X    XFreePixmap(dpy, blank);
  313. X    for(i = 0; i < NUM_WALLS; i++)
  314. X      if(i == 0 || optwalls)
  315. X        XFreePixmap(dpy, walls[i]);
  316. X  }
  317. X  /* okay.. NOW we can destroy the main window and the display */
  318. X  if(display_alloc) {
  319. X    XDestroyWindow(dpy, win);
  320. X    XCloseDisplay(dpy);
  321. X  }
  322. X}
  323. X
  324. X/* Load in a single bitmap.  If this bitmap is the largest in the x or
  325. X * y direction, set bit_width or bit_height appropriately.  If your pixmaps
  326. X * are of varying sizes, a bit_width by bit_height box is guaranteed to be
  327. X * able to surround all of them.
  328. X */
  329. XBoolean LoadOneBitmap(char *fname, char *altname, Pixmap *pix)
  330. X{
  331. X  unsigned int dum1, dum2;
  332. X  int dum3, dum4;
  333. X  Boolean load_fail = _false_;
  334. X  char buf[1024];
  335. X
  336. X  if(bitpath && *bitpath) {
  337. X    /* we have something to try other than the default, let's do it */
  338. X    sprintf(buf, "%s/%s", bitpath, fname);
  339. X    if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
  340. X       BitmapSuccess) {
  341. X      if(altname && *altname) {
  342. X        fprintf(stderr, "%s: Cannot find '%s/%s', trying alternate.\n",
  343. X                progname, bitpath, fname);
  344. X        sprintf(buf, "%s/%s", bitpath, altname);
  345. X        if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
  346. X           BitmapSuccess) {
  347. X          load_fail = _true_;
  348. X          fprintf(stderr, "%s: Cannot find '%s/%s', trying defaults.\n",
  349. X                  progname, bitpath, altname);
  350. X        } else {
  351. X      if(dum1 > bit_width) bit_width = dum1;
  352. X      if(dum2 > bit_height) bit_height = dum2;
  353. X          return _true_;
  354. X        }
  355. X      } else {
  356. X        load_fail = _true_;
  357. X        fprintf(stderr, "%s: Cannot find '%s/%s', trying alternate.\n",
  358. X                progname, bitpath, fname);
  359. X      }
  360. X    } else {
  361. X      if(dum1 > bit_width) bit_width = dum1;
  362. X      if(dum2 > bit_height) bit_height = dum2;
  363. X      return _true_;
  364. X    }
  365. X  }
  366. X  assert(!bitpath || !*bitpath || load_fail);
  367. X  sprintf(buf, "%s/%s", BITPATH, fname);
  368. X  if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
  369. X     BitmapSuccess) {
  370. X      if(altname && *altname) {
  371. X      fprintf(stderr, "%s: Cannot find '%s', trying alternate.\n",
  372. X          progname, fname);
  373. X      sprintf(buf, "%s/%s", BITPATH, fname);
  374. X      if(XReadBitmapFile(dpy, win, buf, &dum1, &dum2, pix, &dum3, &dum4) !=
  375. X         BitmapSuccess) {
  376. X          fprintf(stderr, "%s: Cannot find '%s'!\n", progname, altname);
  377. X          return _false_;
  378. X      } else {
  379. X          if(dum1 > bit_width) bit_width = dum1;
  380. X          if(dum2 > bit_height) bit_height = dum2;
  381. X          return _true_;
  382. X      }
  383. X      } else {
  384. X      fprintf(stderr, "%s: Cannot find '%s'!\n", progname, fname);
  385. X      return _false_;
  386. X      }
  387. X  } else {
  388. X      if(dum1 > bit_width) bit_width = dum1;
  389. X      if(dum2 > bit_height) bit_height = dum2;
  390. X      return _true_;
  391. X  }
  392. X}
  393. X
  394. X/* loads all the bitmaps in.. if any fail, it returns E_NOBITMAP up a level
  395. X * so the program can report the error to the user.  It tries to load in the
  396. X * alternates as well.
  397. X */
  398. Xshort LoadBitmaps(void)
  399. X{
  400. X  register int i;
  401. X
  402. X  if(!LoadOneBitmap("man.xbm", NULL, &man)) return E_NOBITMAP;
  403. X  if(!LoadOneBitmap("saveman.xbm", "man.xbm", &saveman)) return E_NOBITMAP;
  404. X  if(!LoadOneBitmap("object.xbm", NULL, &object)) return E_NOBITMAP;
  405. X  if(!LoadOneBitmap("treasure.xbm", NULL, &treasure)) return E_NOBITMAP;
  406. X  if(!LoadOneBitmap("goal.xbm", NULL, &goal)) return E_NOBITMAP;
  407. X  if(!LoadOneBitmap("floor.xbm", NULL, &floor)) return E_NOBITMAP;
  408. X
  409. X  if(optwalls) {
  410. X    for(i = 0; i < NUM_WALLS; i++) {
  411. X      if(!LoadOneBitmap(wallname[i], "wall.xbm", &walls[i])) return E_NOBITMAP;
  412. X    }
  413. X  } else {
  414. X    if(!LoadOneBitmap("wall.xbm", NULL, &walls[0])) return E_NOBITMAP;
  415. X  }
  416. X  return 0;
  417. X}
  418. X
  419. X/* create and draw all the help windows in.  This is not wholly fullproff with
  420. X * the variable size bitmap code yet, as the constants to place things on the
  421. X * screen, are just that, constants.  This could most likley be reworked.
  422. X */
  423. Xvoid MakeHelpWindows(void)
  424. X{
  425. X  register int i;
  426. X  char *title =
  427. X    "    Sokoban  --  X version by Joseph L. Traub  --  Help page %d";
  428. X  char *next =
  429. X     "     Press Enter to exit  --   Any other key for next page.";
  430. X
  431. X  for(i = 0; i < HELP_PAGES; i++) {
  432. X    XFillRectangle(dpy, help[i], drgc, 0, 0, HELP_W, HELP_H);
  433. X    sprintf(buf, title, (i+1));
  434. X    XDrawImageString(dpy, help[i], gc, 0, 11, buf, strlen(buf));
  435. X    XDrawLine(dpy, help[i], gc, 0, 17, HELP_W, 17);
  436. X    XDrawLine(dpy, help[i], gc, 0, HELP_H-20, HELP_W, HELP_H-20);
  437. X    XDrawImageString(dpy, help[i], gc, 2, HELP_H-7, next, strlen(next));
  438. X  }
  439. X  for(i = 0; help_pages[i].textline != NULL; i++) {
  440. X    XDrawImageString(dpy,help[help_pages[i].page], gc,
  441. X                     help_pages[i].xpos * (finfo->max_bounds.width),
  442. X                     help_pages[i].ypos, help_pages[i].textline,
  443. X                     strlen(help_pages[i].textline));
  444. X  }
  445. X  XCopyPlane(dpy, man, help[0], gc, 0, 0, bit_width, bit_height, 180, 360, 1);
  446. X  XCopyPlane(dpy, goal, help[0], gc, 0, 0, bit_width, bit_height, 270, 360, 1);
  447. X  XCopyPlane(dpy, walls[0], help[0], gc, 0, 0, bit_width, bit_height,
  448. X         369, 360, 1);
  449. X  XCopyPlane(dpy, object, help[0], gc, 0, 0, bit_width, bit_height,
  450. X         477, 360, 1);
  451. X  XCopyPlane(dpy, treasure, help[0], gc, 0, 0, bit_width, bit_height,
  452. X         270, 400, 1);
  453. X  XCopyPlane(dpy, saveman, help[0], gc, 0, 0, bit_width, bit_height,
  454. X         477, 400, 1);
  455. X}
  456. X
  457. X/* wipe out the entire contents of the screen */
  458. Xvoid ClearScreen(void)
  459. X{
  460. X  register int i,j;
  461. X
  462. X  XFillRectangle(dpy, work, drgc, 0, 0, width, height);
  463. X  for(i = 0; i < MAXROW; i++)
  464. X    for(j = 0; j < MAXCOL; j++)
  465. X      XCopyPlane(dpy, floor, work, gc, 0, 0, bit_width, bit_height,
  466. X                 j*bit_width, i*bit_height, 1);
  467. X  XDrawLine(dpy, work, gc, 0, bit_height*MAXROW, bit_width*MAXCOL,
  468. X            bit_height*MAXROW);
  469. X}
  470. X
  471. X/* redisplay the current screen.. Has to handle the help screens if one
  472. X * is currently active..  Copys the correct bitmaps onto the window.
  473. X */
  474. Xvoid RedisplayScreen(void)
  475. X{
  476. X  if(hlpscrn == -1)
  477. X    XCopyArea(dpy, work, win, gc, 0, 0, width, height, 0, 0);
  478. X  else
  479. X    XCopyArea(dpy, help[hlpscrn], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
  480. X  XFlush(dpy);
  481. X}
  482. X
  483. X/* Flush all X events to the screen and wait for them to get there. */
  484. Xvoid SyncScreen(void)
  485. X{
  486. X  XSync(dpy, 0);
  487. X}
  488. X
  489. X/* Draws all the neat little pictures and text onto the working pixmap
  490. X * so that RedisplayScreen is happy.
  491. X */
  492. Xvoid ShowScreen(void)
  493. X{
  494. X  register int i,j;
  495. X
  496. X  for(i = 0; i < rows; i++)
  497. X    for(j = 0; j < cols && map[i][j] != 0; j++)
  498. X      MapChar(map[i][j], i, j, 0);
  499. X  DisplayLevel();
  500. X  DisplayPackets();
  501. X  DisplaySave();
  502. X  DisplayMoves();
  503. X  DisplayPushes();
  504. X  DisplayHelp();
  505. X  RedisplayScreen();
  506. X}
  507. X
  508. X/* Draws a single pixmap, translating from the character map to the pixmap
  509. X * rendition. If "copy_area", also push the change through to the actual window.
  510. X */
  511. Xvoid MapChar(char c, int i, int j, Boolean copy_area)
  512. X{
  513. X  Pixmap this;
  514. X
  515. X  this = GetObjectPixmap(i, j, c); /* i, j are passed so walls can be done */
  516. X  XCopyPlane(dpy, this, work, gc, 0, 0, bit_width, bit_height, cX(j), cY(i), 1);
  517. X  if (copy_area) {
  518. X    XCopyArea(dpy, work, win, gc, cX(j), cY(i), bit_width, bit_height,
  519. X          cX(j), cY(i));
  520. X  }
  521. X}
  522. X
  523. X/* figures out the appropriate pixmap from the internal game representation.
  524. X * Handles fancy walls.
  525. X */
  526. XPixmap GetObjectPixmap(int i, int j, char c)
  527. X{
  528. X  switch(c) {
  529. X    case player: return man;
  530. X    case playerstore: return saveman;
  531. X    case store: return goal;
  532. X    case save: return treasure;
  533. X    case packet: return object;
  534. X    case wall:
  535. X       if(optwalls) return walls[PickWall(i,j)];
  536. X       else return walls[0];
  537. X    case ground: return floor;
  538. X    default: return blank;
  539. X  }
  540. X}
  541. X
  542. X/* returns and index into the fancy walls array. works by assigning a value
  543. X * to each 'position'.. the type of fancy wall is computed based on how
  544. X * many neighboring walls there are.
  545. X */
  546. Xint PickWall(int i, int j)
  547. X{
  548. X  int ret = 0;
  549. X
  550. X  if(i > 0 && map[i-1][j] == wall) ret += 1;
  551. X  if(j < cols && map[i][j+1] == wall) ret += 2;
  552. X  if(i < rows && map[i+1][j] == wall) ret += 4;
  553. X  if(j > 0 && map[i][j-1] == wall) ret += 8;
  554. X  return ret;
  555. X}
  556. X
  557. X/* Draws a string onto the working pixmap */
  558. Xvoid DrawString(int x, int y, char *text)
  559. X{
  560. X  int x_off, y_off;
  561. X
  562. X  x_off = x * finfo->max_bounds.width;
  563. X  y_off = y + finfo->ascent;
  564. X
  565. X  XDrawImageString(dpy, work, gc, x_off, y_off, text, strlen(text));
  566. X}
  567. X
  568. X/* The following routines display various 'statusline' stuff (ie moves, pushes,
  569. X * etc) on the screen.  they are called as they are needed to be changed to
  570. X * avoid unnecessary drawing */
  571. Xvoid DisplayLevel(void)
  572. X{
  573. X   sprintf(buf, "Level: %3d", level);
  574. X   DrawString(0, STATUSLINE, buf);
  575. X}
  576. X
  577. Xvoid DisplayPackets(void)
  578. X{
  579. X   sprintf(buf, "Packets: %3d", packets);
  580. X   DrawString(12, STATUSLINE, buf);
  581. X}
  582. X
  583. Xvoid DisplaySave(void)
  584. X{
  585. X  sprintf(buf, "Saved: %3d", savepack);
  586. X  DrawString(26, STATUSLINE, buf);
  587. X}
  588. X
  589. Xvoid DisplayMoves(void)
  590. X{
  591. X  sprintf(buf, "Moves: %5d", moves);
  592. X  DrawString(38, STATUSLINE, buf);
  593. X}
  594. X
  595. Xvoid DisplayPushes(void)
  596. X{
  597. X  sprintf(buf, "Pushes: %3d", pushes);
  598. X  DrawString(52, STATUSLINE, buf);
  599. X}
  600. X
  601. Xvoid DisplayHelp(void)
  602. X{
  603. X  DrawString(0, HELPLINE, "Press ? for help.");
  604. X}
  605. X
  606. X/* Displays the first help page, and flips help pages (one per key press)
  607. X * until a return is pressed.
  608. X */
  609. Xvoid ShowHelp(void)
  610. X{
  611. X  int i = 0;
  612. X  Boolean done = _false_;
  613. X
  614. X  hlpscrn = 0;
  615. X  XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
  616. X  XFlush(dpy);
  617. X  while(!done) {
  618. X    done = WaitForEnter();
  619. X    if(done) {
  620. X      hlpscrn = -1;
  621. X      return;
  622. X    } else {
  623. X      i = (i+1)%HELP_PAGES;
  624. X      hlpscrn = i;
  625. X      XCopyArea(dpy, help[i], win, gc, 0, 0, HELP_W, HELP_H, 0, 0);
  626. X      XFlush(dpy);
  627. X    }
  628. X  }
  629. X}
  630. X
  631. X/* since the 'press ? for help' is ALWAYS displayed, just beep when there is
  632. X * a problem.
  633. X */
  634. Xvoid HelpMessage(void)
  635. X{
  636. X  XBell(dpy, 0);
  637. X  RedisplayScreen();
  638. X}
  639. END_OF_FILE
  640.   if test 17310 -ne `wc -c <'xsokoban-3.0/display.c'`; then
  641.     echo shar: \"'xsokoban-3.0/display.c'\" unpacked with wrong size!
  642.   fi
  643.   # end of 'xsokoban-3.0/display.c'
  644. fi
  645. if test -f 'xsokoban-3.0/externs.h' -a "${1}" != "-c" ; then 
  646.   echo shar: Will not clobber existing file \"'xsokoban-3.0/externs.h'\"
  647. else
  648.   echo shar: Extracting \"'xsokoban-3.0/externs.h'\" \(2678 characters\)
  649.   sed "s/^X//" >'xsokoban-3.0/externs.h' <<'END_OF_FILE'
  650. X#include <X11/X.h>
  651. X#include <X11/Xlib.h>
  652. X#include <X11/Xutil.h>
  653. X#include <X11/keysym.h>
  654. X#include <X11/Xresource.h>
  655. X#include <sys/stat.h>
  656. X#include <sys/types.h>
  657. X
  658. X#include "config_local.h"
  659. X
  660. X#if !defined(GETPASS_PROTO)
  661. Xextern char *getpass(char *);
  662. X#endif
  663. X
  664. X#if !defined(CREAT_PROTO)
  665. Xextern int creat(const char *, mode_t);
  666. X#endif
  667. X
  668. X#if !defined(FPRINTF_PROTO)
  669. Xextern int fprintf(FILE *, const char *, ...);
  670. X#endif
  671. X
  672. X#if !defined(FCLOSE_PROTO)
  673. Xextern int fclose(FILE *);
  674. X#endif
  675. X
  676. X/* The boolean typedef */
  677. Xtypedef enum { _false_ = 0, _true_ = 1 } Boolean;
  678. X
  679. X/* stuff from display.c */
  680. Xextern short LoadBitmaps(void);
  681. Xextern void MakeHelpWindows(void);
  682. Xextern void ClearScreen(void);
  683. Xextern void RedisplayScreen(void);
  684. Xextern void SyncScreen(void);
  685. Xextern void ShowScreen(void);
  686. Xextern void MapChar(char, int, int, Boolean);
  687. Xextern Pixmap GetObjectPixmap(int, int, char);
  688. Xextern int PickWall(int, int);
  689. Xextern void DrawString(int, int, char *);
  690. Xextern void ClearString(int, int, int);
  691. Xextern void DisplayLevel(void);
  692. Xextern void DisplayPackets(void);
  693. Xextern void DisplaySave(void);
  694. Xextern void DisplayMoves(void);
  695. Xextern void DisplayPushes(void);
  696. Xextern void DisplayHelp(void);
  697. Xextern void ShowHelp(void);
  698. Xextern void HelpMessage(void);
  699. Xextern void DestroyDisplay(void);
  700. Xextern short InitX(void);
  701. X
  702. X/* stuff from main.c */
  703. Xextern short CheckCommandLine(int *, char **);
  704. Xextern void main(int, char **);
  705. Xextern short GameLoop(void);
  706. Xextern short GetGamePassword(void);
  707. Xextern void Error(short);
  708. Xextern void Usage(void);
  709. X
  710. X/* stuff from resources.c */
  711. Xextern char *GetDatabaseResource(XrmDatabase, char *);
  712. Xextern char *GetResource(char *);
  713. Xextern Boolean StringToBoolean(char *);
  714. Xextern Boolean GetColorResource(char *, unsigned long *);
  715. X
  716. X/* stuff from play.c */
  717. Xextern short Play(void);
  718. Xextern void MakeMove(KeySym);
  719. Xextern short TestMove(KeySym);
  720. Xextern void DoMove(short);
  721. Xextern void UndoMove(void);
  722. Xextern void TempSave(void);
  723. Xextern void TempReset(void);
  724. Xextern Boolean WaitForEnter(void);
  725. Xextern void MoveMan(int, int);
  726. Xextern void FindTarget(int, int, int);
  727. Xextern Boolean RunTo(int, int);
  728. Xextern void PushMan(int, int);
  729. X
  730. X/* stuff from score.c */
  731. Xextern short OutputScore(void);
  732. Xextern short MakeNewScore(void);
  733. Xextern short GetUserLevel(short *);
  734. Xextern short Score(Boolean show);
  735. Xextern short ReadScore(void);
  736. Xextern short MakeScore(void);
  737. Xextern short FindUser(void);
  738. Xextern short FindPos(void);
  739. Xextern short WriteScore(void);
  740. Xextern void ShowScore(void);
  741. Xextern void CopyEntry(short, short);
  742. Xextern void FlushDeletedScores(Boolean[]);
  743. X
  744. X/* stuff from screen.c */
  745. Xextern short ReadScreen(void);
  746. X
  747. X/* stuff from save.c */
  748. Xextern short SaveGame(void);
  749. Xextern short RestoreGame(void);
  750. END_OF_FILE
  751.   if test 2678 -ne `wc -c <'xsokoban-3.0/externs.h'`; then
  752.     echo shar: \"'xsokoban-3.0/externs.h'\" unpacked with wrong size!
  753.   fi
  754.   # end of 'xsokoban-3.0/externs.h'
  755. fi
  756. if test -f 'xsokoban-3.0/play.c' -a "${1}" != "-c" ; then 
  757.   echo shar: Will not clobber existing file \"'xsokoban-3.0/play.c'\"
  758. else
  759.   echo shar: Extracting \"'xsokoban-3.0/play.c'\" \(17974 characters\)
  760.   sed "s/^X//" >'xsokoban-3.0/play.c' <<'END_OF_FILE'
  761. X#include <stdio.h>
  762. X#include <assert.h>
  763. X#include <string.h>
  764. X
  765. X#include "externs.h"
  766. X#include "globals.h"
  767. X
  768. Xextern int abs(int);
  769. X
  770. X/* defining the types of move */
  771. X#define MOVE            1
  772. X#define PUSH            2
  773. X#define SAVE            3
  774. X#define UNSAVE          4
  775. X#define STOREMOVE       5
  776. X#define STOREPUSH       6
  777. X
  778. X/* This value merely needs to be greater than the longest possible path length
  779. X * making it the number of squares in the array seems a good bet. 
  780. X */
  781. X#define BADMOVE MAXROW*MAXCOL
  782. X
  783. X/* some simple checking macros to make sure certain moves are legal when 
  784. X * trying to do mouse based movement.
  785. X */
  786. X#define ISPLAYER(x,y) ((map[x][y] == player) || (map[x][y] == playerstore))
  787. X#define ISCLEAR(x,y) ((map[x][y] == ground) || (map[x][y] == store))
  788. X#define ISPACKET(x,y) ((map[x][y] == packet) || (map[x][y] == save))
  789. X
  790. Xstatic XEvent xev;
  791. Xstatic POS tpos1, tpos2, lastppos, lasttpos1, lasttpos2;
  792. Xstatic char lppc, ltp1c, ltp2c;
  793. Xstatic short action, lastaction;
  794. X
  795. Xstatic Boolean shift = _false_;
  796. Xstatic Boolean cntrl = _false_;
  797. Xstatic KeySym oldmove;
  798. Xstatic int findmap[MAXROW+1][MAXCOL+1];
  799. X
  800. X#define MAXDELTAS 4
  801. X
  802. X/** For stack saves. **/
  803. Xstruct map_delta {
  804. X    int x,y;
  805. X    char oldchar, newchar;
  806. X};
  807. Xstruct move_r {
  808. X    short px, py;
  809. X    short moves, pushes, saved, ndeltas;
  810. X    struct map_delta deltas[MAXDELTAS];
  811. X};
  812. Xstatic struct move_r move_stack[STACKDEPTH];
  813. Xstatic int move_stack_sp; /* points to last saved move. If no saved move, -1 */
  814. Xstatic Map prev_map;
  815. Xstatic void RecordChange(void);
  816. Xstatic void UndoChange(void);
  817. Xstatic void InitMoveStack(void);
  818. X
  819. X/* play a particular level.
  820. X * All we do here is wait for input, and dispatch to appropriate routines
  821. X * to deal with it nicely.
  822. X */
  823. Xshort Play(void)
  824. X{
  825. X  short ret;
  826. X  int bufs = 1;
  827. X  char buf[1];
  828. X  KeySym sym;
  829. X  XComposeStatus compose;
  830. X  
  831. X  ClearScreen();
  832. X  ShowScreen();
  833. X  InitMoveStack();
  834. X  ret = 0;
  835. X  while (ret == 0) {
  836. X    XNextEvent(dpy, &xev);
  837. X    switch(xev.type) {
  838. X      case Expose:
  839. X    /* Redisplaying is pretty cheap, so I don't care too much about it */
  840. X    RedisplayScreen();
  841. X    break;
  842. X      case ButtonPress:
  843. X    switch(xev.xbutton.button) {
  844. X      case Button1:
  845. X        /* move the man to where the pointer is. */
  846. X        MoveMan(xev.xbutton.x, xev.xbutton.y);
  847. X        RecordChange();
  848. X        break;
  849. X      case Button2:
  850. X        /* Push a ball */
  851. X        PushMan(xev.xbutton.x, xev.xbutton.y);
  852. X        RecordChange();
  853. X        break;
  854. X      case Button3:
  855. X        /* undo last move */
  856. X        UndoChange();
  857. X        ShowScreen();
  858. X        break;
  859. X      default:
  860. X        /* I'm sorry, you win the irritating beep for your efforts. */
  861. X        HelpMessage();
  862. X        break;
  863. X    }
  864. X    RedisplayScreen();
  865. X    break;
  866. X      case KeyPress:
  867. X    buf[0] = '\0';
  868. X    (void) XLookupString(&xev.xkey, buf, bufs, &sym, &compose);
  869. X    cntrl = (xev.xkey.state & ControlMask) ? _true_ : _false_;
  870. X    shift = (xev.xkey.state & ShiftMask) ? _true_ : _false_;
  871. X    switch(sym) {
  872. X      case XK_q:
  873. X        /* q is for quit */
  874. X        if(!cntrl)
  875. X          ret = E_ENDGAME;
  876. X        break;
  877. X      case XK_s:
  878. X        /* save */
  879. X        if(!cntrl) {
  880. X          ret = SaveGame();
  881. X          if(ret == 0)
  882. X        ret = E_SAVED;
  883. X        }
  884. X        break;
  885. X      case XK_question:
  886. X        /* help */
  887. X        if(!cntrl) {
  888. X          ShowHelp();
  889. X          RedisplayScreen();
  890. X        }
  891. X        break;
  892. X      case XK_r:
  893. X        /* ^R refreshes the screen */
  894. X        if(cntrl)
  895. X          RedisplayScreen();
  896. X        break;
  897. X      case XK_U:
  898. X        /* Do a full screen reset */
  899. X        if(!cntrl) {
  900. X          moves = pushes = 0;
  901. X          ret = ReadScreen();
  902. X          InitMoveStack();
  903. X          if(ret == 0) {
  904. X        ShowScreen();
  905. X          }
  906. X        }
  907. X        break;
  908. X      case XK_u:
  909. X        if(!cntrl) {
  910. X        UndoChange();
  911. X        ShowScreen();
  912. X        }
  913. X        break;
  914. X      case XK_k:
  915. X      case XK_K:
  916. X      case XK_Up:
  917. X      case XK_j:
  918. X      case XK_J:
  919. X      case XK_Down:
  920. X      case XK_l:
  921. X      case XK_L:
  922. X      case XK_Right:
  923. X      case XK_h:
  924. X      case XK_H:
  925. X      case XK_Left:
  926. X        /* A move, A move!! we have a MOVE!! */
  927. X        MakeMove(sym);
  928. X        RedisplayScreen();
  929. X        RecordChange();
  930. X        break;
  931. X      default:
  932. X        /* I ONLY want to beep if a key was pressed.  Contrary to what
  933. X         * X11 believes, SHIFT and CONTROL are NOT keys
  934. X         */
  935. X            if(buf[0]) {
  936. X          HelpMessage();
  937. X        }
  938. X        break;
  939. X    }
  940. X    break;
  941. X      case ClientMessage:
  942. X    {
  943. X        XClientMessageEvent *cm = (XClientMessageEvent *)&xev;
  944. X        if (cm->message_type == wm_protocols &&
  945. X        cm->data.l[0] == wm_delete_window)
  946. X          ret = E_ENDGAME;
  947. X    }
  948. X    break;
  949. X      default:
  950. X    break;
  951. X    }
  952. X    /* if we solved a level, set it up so we get some score! */
  953. X    if((ret == 0) && (packets == savepack)) {
  954. X      scorelevel = level;
  955. X      scoremoves = moves;
  956. X      scorepushes = pushes;
  957. X      break;
  958. X    }
  959. X  }
  960. X  return ret;
  961. X}
  962. X
  963. X/* Perform a user move based on the key in "sym". */
  964. Xvoid MakeMove(KeySym sym)
  965. X{
  966. X  do {
  967. X    action = TestMove(sym);
  968. X    if(action != 0) {
  969. X      lastaction = action;
  970. X      lastppos.x = ppos.x;
  971. X      lastppos.y = ppos.y;
  972. X      lppc = map[ppos.x][ppos.y];
  973. X      lasttpos1.x = tpos1.x;
  974. X      lasttpos1.y = tpos1.y;
  975. X      ltp1c = map[tpos1.x][tpos1.y];
  976. X      lasttpos2.x = tpos2.x;
  977. X      lasttpos2.y = tpos2.y;
  978. X      ltp2c = map[tpos2.x][tpos2.y];
  979. X      DoMove(lastaction);
  980. X      /* store the current keysym so we can do the repeat. */
  981. X      oldmove = sym;
  982. X    }
  983. X  } while ((action != 0) && (packets != savepack) && (shift || cntrl));
  984. X}
  985. X
  986. X/* make sure a move is valid and if it is, return type of move */
  987. Xshort TestMove(KeySym action)
  988. X{
  989. X  short ret;
  990. X  char tc;
  991. X  Boolean stop_at_object;
  992. X
  993. X  stop_at_object = cntrl;
  994. X
  995. X  if((action == XK_Up) || (action == XK_k) || (action == XK_K)) {
  996. X    tpos1.x = ppos.x-1;
  997. X    tpos2.x = ppos.x-2;
  998. X    tpos1.y = tpos2.y = ppos.y;
  999. X  }
  1000. X  if((action == XK_Down) || (action == XK_j) || (action == XK_J)) {
  1001. X    tpos1.x = ppos.x+1;
  1002. X    tpos2.x = ppos.x+2;
  1003. X    tpos1.y = tpos2.y = ppos.y;
  1004. X  }
  1005. X  if((action == XK_Left) || (action == XK_h) || (action == XK_H)) {
  1006. X    tpos1.y = ppos.y-1;
  1007. X    tpos2.y = ppos.y-2;
  1008. X    tpos1.x = tpos2.x = ppos.x;
  1009. X  }
  1010. X  if((action == XK_Right) || (action == XK_l) || (action == XK_L)) {
  1011. X    tpos1.y = ppos.y+1;
  1012. X    tpos2.y = ppos.y+2;
  1013. X    tpos1.x = tpos2.x = ppos.x;
  1014. X  }
  1015. X  tc = map[tpos1.x][tpos1.y];
  1016. X  if((tc == packet) || (tc == save)) {
  1017. X    if(!stop_at_object) {
  1018. X      if(map[tpos2.x][tpos2.y] == ground)
  1019. X    ret = (tc == save) ? UNSAVE : PUSH;
  1020. X      else if(map[tpos2.x][tpos2.y] == store)
  1021. X    ret = (tc == save) ? STOREPUSH : SAVE;
  1022. X      else
  1023. X    ret = 0;
  1024. X    } else
  1025. X      ret = 0;
  1026. X  } else if(tc == ground)
  1027. X    ret = MOVE;
  1028. X  else if(tc == store)
  1029. X    ret = STOREMOVE;
  1030. X  else
  1031. X    ret = 0;
  1032. X  return ret;
  1033. X}
  1034. X
  1035. X/* actually update the internal map with the results of the move */
  1036. Xvoid DoMove(short moveaction)
  1037. X{
  1038. X  map[ppos.x][ppos.y] = (map[ppos.x][ppos.y] == player) ? ground : store;
  1039. X  switch( moveaction) {
  1040. X    case MOVE:
  1041. X      map[tpos1.x][tpos1.y] = player;
  1042. X      break;
  1043. X    case STOREMOVE:
  1044. X      map[tpos1.x][tpos1.y] = playerstore;
  1045. X      break;
  1046. X    case PUSH:
  1047. X      map[tpos2.x][tpos2.y] = map[tpos1.x][tpos1.y];
  1048. X      map[tpos1.x][tpos1.y] = player;
  1049. X      pushes++;
  1050. X      break;
  1051. X    case UNSAVE:
  1052. X      map[tpos2.x][tpos2.y] = packet;
  1053. X      map[tpos1.x][tpos1.y] = playerstore;
  1054. X      pushes++;
  1055. X      savepack--;
  1056. X      break;
  1057. X    case SAVE:
  1058. X      map[tpos2.x][tpos2.y] = save;
  1059. X      map[tpos1.x][tpos1.y] = player;
  1060. X      savepack++;
  1061. X      pushes++;
  1062. X      break;
  1063. X    case STOREPUSH:
  1064. X      map[tpos2.x][tpos2.y] = save;
  1065. X      map[tpos1.x][tpos1.y] = playerstore;
  1066. X      pushes++;
  1067. X      break;
  1068. X  }
  1069. X  moves++;
  1070. X  DisplayMoves();
  1071. X  DisplayPushes();
  1072. X  DisplaySave();
  1073. X  MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y, 1);
  1074. X  MapChar(map[tpos1.x][tpos1.y], tpos1.x, tpos1.y, 1);
  1075. X  MapChar(map[tpos2.x][tpos2.y], tpos2.x, tpos2.y, 1);
  1076. X  ppos.x = tpos1.x;
  1077. X  ppos.y = tpos1.y;
  1078. X  SyncScreen();
  1079. X}
  1080. X
  1081. X/* undo the most recently done move */
  1082. Xvoid UndoMove(void)
  1083. X{
  1084. X  map[lastppos.x][lastppos.y] = lppc;
  1085. X  map[lasttpos1.x][lasttpos1.y] = ltp1c;
  1086. X  map[lasttpos2.x][lasttpos2.y] = ltp2c;
  1087. X  ppos.x = lastppos.x;
  1088. X  ppos.y = lastppos.y;
  1089. X  switch( lastaction) {
  1090. X    case MOVE:
  1091. X      moves--;
  1092. X      break;
  1093. X    case STOREMOVE:
  1094. X      moves--;
  1095. X      break;
  1096. X    case PUSH:
  1097. X      moves--;
  1098. X      pushes--;
  1099. X      break;
  1100. X    case UNSAVE:
  1101. X      moves--;
  1102. X      pushes--;
  1103. X      savepack++;
  1104. X      break;
  1105. X    case SAVE:
  1106. X      moves--;
  1107. X      pushes--;
  1108. X      savepack--;
  1109. X      break;
  1110. X    case STOREPUSH:
  1111. X      moves--;
  1112. X      pushes--;
  1113. X      break;
  1114. X  }
  1115. X  DisplayMoves();
  1116. X  DisplayPushes();
  1117. X  DisplaySave();
  1118. X  MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y, 1);
  1119. X  MapChar(map[lasttpos1.x][lasttpos1.y], lasttpos1.x, lasttpos1.y, 1);
  1120. X  MapChar(map[lasttpos2.x][lasttpos2.y], lasttpos2.x, lasttpos2.y, 1);
  1121. X  SyncScreen();
  1122. X}
  1123. X
  1124. X/* Function used by the help pager.  We ONLY want to flip pages if a key
  1125. X * key is pressed.. We want to exit the help pager if ENTER is pressed.
  1126. X * As above, <shift> and <control> and other such fun keys are NOT counted
  1127. X * as a keypress.
  1128. X */
  1129. XBoolean WaitForEnter(void)
  1130. X{
  1131. X  KeySym keyhit;
  1132. X  char buf[1];
  1133. X  int bufs = 1;
  1134. X  XComposeStatus compose;
  1135. X
  1136. X  while (1) {
  1137. X    XNextEvent(dpy, &xev);
  1138. X    switch(xev.type) {
  1139. X      case Expose:
  1140. X    RedisplayScreen();
  1141. X    break;
  1142. X      case KeyPress:
  1143. X    buf[0] = '\0';
  1144. X    XLookupString(&xev.xkey, buf, bufs, &keyhit, &compose);
  1145. X    if(buf[0]) {
  1146. X      return (keyhit == XK_Return) ? _true_ : _false_;
  1147. X    }
  1148. X    break;
  1149. X      default:
  1150. X    break;
  1151. X    }
  1152. X  }
  1153. X}
  1154. X
  1155. X/* find the shortest path to the target via a fill search algorithm */
  1156. Xvoid FindTarget(int px, int py, int pathlen)
  1157. X{
  1158. X  if(!(ISCLEAR(px, py) || ISPLAYER(px, py)))
  1159. X    return;
  1160. X  if(findmap[px][py] <= pathlen)
  1161. X    return;
  1162. X
  1163. X  findmap[px][py] = pathlen++;
  1164. X
  1165. X  if((px == ppos.x) && (py == ppos.y))
  1166. X    return;
  1167. X
  1168. X  FindTarget(px - 1, py, pathlen);
  1169. X  FindTarget(px + 1, py, pathlen);
  1170. X  FindTarget(px, py - 1, pathlen);
  1171. X  FindTarget(px, py + 1, pathlen);
  1172. X}
  1173. X
  1174. X/* Do all the fun movement stuff with the mouse */
  1175. Xvoid MoveMan(int mx, int my)
  1176. X{
  1177. X  int x, y;
  1178. X
  1179. X  shift = cntrl = _false_;
  1180. X
  1181. X  /* reverse the screen coords to get the internal coords (yes, I know this 
  1182. X   * should be fixed) RSN */
  1183. X  y = wX(mx);
  1184. X  x = wY(my);
  1185. X
  1186. X  /* make sure we are within the bounds of the array */
  1187. X  if((x < 0) || (x > MAXROW) || (y < 0) || (y > MAXCOL)) {
  1188. X    HelpMessage();
  1189. X    return;
  1190. X  }
  1191. X
  1192. X  if(ISPACKET(x, y)) {
  1193. X    HelpMessage();
  1194. X    return;
  1195. X  }
  1196. X  /* if we clicked on the player or a wall (or an object but that was already
  1197. X   * handled) the we don't want to move.
  1198. X   */
  1199. X  if(!ISCLEAR(x, y)) {
  1200. X    HelpMessage();
  1201. X    return;
  1202. X  }
  1203. X  if (!RunTo(x, y)) HelpMessage();
  1204. X}
  1205. X
  1206. X/* Return whether (x,y) is on the board */
  1207. XBoolean ValidPosn(int x, int y)
  1208. X{
  1209. X    return (x >= 0) && (x <= MAXROW) && (y >= 0) && (y <= MAXCOL);
  1210. X}
  1211. X
  1212. X/* 
  1213. X   Find the object at a position orthogonal to (x, y) that is
  1214. X   closest to (ppos.x, ppos.y), is separated from (x,y) only
  1215. X   by empty spaces, and has the player or an empty space on the far
  1216. X   side of (x,y), and is not directly opposite the destination space
  1217. X   from the player; place its coordinates in (*ox, *oy) and return _true_.
  1218. X   If no such object exists, return _false_.
  1219. X*/
  1220. XBoolean FindOrthogonalObject(int x, int y, int *ox, int *oy)
  1221. X{
  1222. X    int dir;
  1223. X    int bestdist = BADMOVE;
  1224. X    Boolean foundOne = _false_;
  1225. X    for (dir = 0; dir < 4; dir++) {
  1226. X    int dx, dy, x1, y1, dist;
  1227. X    switch(dir) {
  1228. X        default: dx = 1; dy = 0; break; /* use "default" to please gcc */
  1229. X        case 1: dx = -1; dy = 0; break;
  1230. X        case 2: dx = 0; dy = 1; break;
  1231. X        case 3: dx = 0; dy = -1; break;
  1232. X    }
  1233. X    if ((ppos.x == x && ((ppos.y - y)*dy) < 0)
  1234. X        || (ppos.y == y && ((ppos.x - x)*dx) < 0)) continue;
  1235. X    /* Eliminate case where player would push in the opposite
  1236. X       direction. */
  1237. X    x1 = x; y1 = y;
  1238. X        while (ValidPosn(x1, y1)) {
  1239. X        x1 += dx; y1 += dy;
  1240. X        dist = abs(ppos.x - x1) + abs(ppos.y - y1);
  1241. X        if (dist <= bestdist && ISPACKET(x1, y1) &&
  1242. X        (ISPLAYER(x1 + dx, y1 + dy) || ISCLEAR(x1 + dx, y1 + dy))) {
  1243. X        if (dist < bestdist) {
  1244. X            bestdist = dist;
  1245. X            *ox = x1;
  1246. X            *oy = y1;
  1247. X            foundOne = _true_;
  1248. X            break;
  1249. X        } else {
  1250. X            foundOne = _false_; /* found one that was just as good */
  1251. X        }
  1252. X        }
  1253. X        if (!ISCLEAR(x1, y1)) break;
  1254. X    }
  1255. X    }
  1256. X    return foundOne ? _true_ : _false_ ;
  1257. X}
  1258. X
  1259. X#define DEBUGPUSH 0
  1260. X
  1261. X/* Push a nearby stone to the position indicated by (mx, my). */
  1262. Xvoid PushMan(int mx, int my)
  1263. X{
  1264. X  int i, x, y, ox, oy, dist;
  1265. X
  1266. X  shift = cntrl = _false_;
  1267. X
  1268. X  /* reverse the screen coords to get the internal coords (yes, I know this 
  1269. X   * should be fixed) RSN */
  1270. X  y = wX(mx);
  1271. X  x = wY(my);
  1272. X
  1273. X  /* make sure we are within the bounds of the array */
  1274. X  if(!ValidPosn(x,y)) {
  1275. X    HelpMessage();
  1276. X#if DEBUGPUSH
  1277. X    printf("Outside array\n");
  1278. X#endif
  1279. X    return;
  1280. X  }
  1281. X
  1282. X  /* if we are clicking on an object, and are right next to it, we want to
  1283. X   * move the object.
  1284. X   */
  1285. X  if(ISPACKET(x, y)) {
  1286. X    if(ISPLAYER(x - 1, y))
  1287. X      MakeMove(XK_Down);
  1288. X    else if(ISPLAYER(x + 1, y))
  1289. X      MakeMove(XK_Up);
  1290. X    else if(ISPLAYER(x, y - 1))
  1291. X      MakeMove(XK_Right);
  1292. X    else if(ISPLAYER(x, y + 1))
  1293. X      MakeMove(XK_Left);
  1294. X    else {
  1295. X      /* we weren't right next to the object */
  1296. X      HelpMessage();
  1297. X      return;
  1298. X    }
  1299. X    return;
  1300. X  }
  1301. X
  1302. X  /* if we clicked on the player or a wall (or an object but that was already
  1303. X   * handled) the we don't want to move.
  1304. X   */
  1305. X  if(!ISCLEAR(x, y)) {
  1306. X    HelpMessage();
  1307. X#if DEBUGPUSH
  1308. X    printf("Not a clear space\n"); */
  1309. X#endif
  1310. X    return;
  1311. X  }
  1312. X
  1313. X#if 0
  1314. X  if (abs(x - ppos.x) < 2 && abs(ppos.y - y) < 2) {
  1315. X#if DEBUGPUSH
  1316. X    printf("Too close to destination (%d, %d)\n",
  1317. X        abs(x - ppos.x) , abs(y - ppos.y));
  1318. X#endif
  1319. X    HelpMessage();
  1320. X    /* Player must be sufficiently far from the destination that there
  1321. X       can be no ambiguity about which stone to push */
  1322. X    return;
  1323. X  }
  1324. X#endif
  1325. X
  1326. X  if (!FindOrthogonalObject(x, y, &ox, &oy)) {
  1327. X    HelpMessage();
  1328. X#if DEBUGPUSH
  1329. X    printf("Can't find packet\n");
  1330. X#endif
  1331. X    return;
  1332. X  }
  1333. X
  1334. X  assert(x == ox || y == oy);
  1335. X  dist = abs(ox - x) + abs(oy - y);
  1336. X
  1337. X  if (x > ox) ox--;
  1338. X  if (x < ox) ox++;
  1339. X  if (y > oy) oy--;
  1340. X  if (y < oy) oy++;
  1341. X
  1342. X  /* (ox,oy) now denotes the place we need to run to to be able to push */
  1343. X
  1344. X  if (ox != ppos.x || oy != ppos.y) {
  1345. X      if (!ISCLEAR(ox, oy)) {
  1346. X#if DEBUGPUSH
  1347. X    printf("Can't move into an occupied space! (%d,%d)\n",
  1348. X        ox - ppos.x, oy - ppos.y);
  1349. X#endif
  1350. X    HelpMessage();
  1351. X    return;
  1352. X      }
  1353. X      if (!RunTo(ox, oy)) {
  1354. X    HelpMessage();
  1355. X#if DEBUGPUSH
  1356. X    printf("Can't get in position to push\n"); */
  1357. X#endif
  1358. X    return;
  1359. X      }
  1360. X  }
  1361. X  assert(ppos.x == ox && ppos.y == oy);
  1362. X
  1363. X  for (i = 0; i < dist; i++) {
  1364. X    if (ppos.x < x) MakeMove(XK_Down);
  1365. X    if (ppos.x > x) MakeMove(XK_Up);
  1366. X    if (ppos.y < y) MakeMove(XK_Right);
  1367. X    if (ppos.y > y) MakeMove(XK_Left);
  1368. X  }
  1369. X}
  1370. X
  1371. X/* Move the player to the position (x,y), if possible. Return _true_
  1372. X   iff successful. The position (x,y) must be clear.
  1373. X*/
  1374. XBoolean RunTo(int x, int y)
  1375. X{
  1376. X  int i,j,cx,cy;
  1377. X  /* Fill the trace map */
  1378. X  for(i = 0; i <= MAXROW; i++)
  1379. X    for (j = 0; j <= MAXCOL; j++)
  1380. X      findmap[i][j] = BADMOVE;
  1381. X  /* flood fill search to find a shortest path to the push point. */
  1382. X  FindTarget(x, y, 0);
  1383. X
  1384. X  /* if we didn't make it back to the players position, there is no valid path
  1385. X   * to that place.
  1386. X   */
  1387. X  if(findmap[ppos.x][ppos.y] == BADMOVE) {
  1388. X    return _false_;
  1389. X  } else {
  1390. X    /* we made it back, so let's walk the path we just built up */
  1391. X    cx = ppos.x;
  1392. X    cy = ppos.y;
  1393. X    while(findmap[cx][cy]) {
  1394. X      if(findmap[cx - 1][cy] == (findmap[cx][cy] - 1)) {
  1395. X    MakeMove(XK_Up);
  1396. X    cx--;
  1397. X      } else if(findmap[cx + 1][cy] == (findmap[cx][cy] - 1)) {
  1398. X    MakeMove(XK_Down);
  1399. X    cx++;
  1400. X      } else if(findmap[cx][cy - 1] == (findmap[cx][cy] - 1)) {
  1401. X    MakeMove(XK_Left);
  1402. X    cy--;
  1403. X      } else if(findmap[cx][cy + 1] == (findmap[cx][cy] - 1)) {
  1404. X    MakeMove(XK_Right);
  1405. X    cy++;
  1406. X      } else {
  1407. X    /* if we get here, something is SERIOUSLY wrong, so we should abort */
  1408. X    abort();
  1409. X      }
  1410. X    }
  1411. X  }
  1412. X  return _true_;
  1413. X}
  1414. X
  1415. X
  1416. Xstatic void InitMoveStack()
  1417. X{
  1418. X    move_stack_sp = -1;
  1419. X    move_stack[0].moves = moves;
  1420. X    move_stack[0].pushes = moves;
  1421. X    move_stack[0].saved = savepack;
  1422. X    memcpy(prev_map, map, sizeof(map));
  1423. X}
  1424. X
  1425. X/* Add a record to the move stack that records the changes since the
  1426. X   last map state (which is stored in "prev_map"). Update "prev_map"
  1427. X   to contain the current map so the next call to "RecordChange()"
  1428. X   will perform correctly.
  1429. X   
  1430. X   If the stack runs out of space, dump the oldest half of the
  1431. X   saved moves and continue. Undoing past that point will jump
  1432. X   back to the beginning of the level. If the user is using the
  1433. X   mouse or any skill, should never happen.
  1434. X*/
  1435. Xstatic void RecordChange()
  1436. X{
  1437. X    struct move_r *r = &move_stack[++move_stack_sp];
  1438. X    int x,y, ndeltas = 0;
  1439. X    assert(move_stack_sp < STACKDEPTH);
  1440. X    if (move_stack_sp == STACKDEPTH - 1) {
  1441. X    int shift = STACKDEPTH/2;
  1442. X    memcpy(&move_stack[0], &move_stack[shift],
  1443. X          sizeof(struct move_r) * (STACKDEPTH - shift));
  1444. X    move_stack_sp -= shift;
  1445. X    r -= shift;
  1446. X    }
  1447. X    r[1].moves = moves;
  1448. X    r[1].pushes = pushes;
  1449. X    r[1].saved = savepack;
  1450. X    r[1].px = ppos.x;
  1451. X    r[1].py = ppos.y;
  1452. X    for (x = 0; x <= MAXROW; x++) {
  1453. X    for (y = 0; y <= MAXROW; y++) {
  1454. X        if (map[x][y] != prev_map[x][y]) {
  1455. X        assert(ndeltas < MAXDELTAS);
  1456. X        r->deltas[ndeltas].x = x;
  1457. X        r->deltas[ndeltas].y = y;
  1458. X        r->deltas[ndeltas].newchar = map[x][y];
  1459. X        r->deltas[ndeltas].oldchar = prev_map[x][y];
  1460. X        ndeltas++;
  1461. X#if 0
  1462. X        printf("Change (%d,%d) %c->%c\n", x, y, prev_map[x][y],
  1463. X               map[x][y]);
  1464. X#endif
  1465. X        }
  1466. X    }
  1467. X    }
  1468. X    r->ndeltas = ndeltas;
  1469. X    if (ndeltas == 0) {
  1470. X    move_stack_sp--; /* Why push an identical entry? */
  1471. X    }
  1472. X    memcpy(prev_map, map, sizeof(map));
  1473. X}
  1474. X
  1475. Xstatic void UndoChange()
  1476. X{
  1477. X    if (move_stack_sp <= 0) {
  1478. X    int ret;
  1479. X    InitMoveStack();
  1480. X    ret = ReadScreen();
  1481. X    moves = pushes = 0;
  1482. X    if (ret) {
  1483. X        fprintf(stderr, "Can't read screen file\n");
  1484. X        exit(-1);
  1485. X    }
  1486. X    } else {
  1487. X    struct move_r *r = &move_stack[move_stack_sp];
  1488. X    int i;
  1489. X    moves = r->moves;
  1490. X    pushes = r->pushes;
  1491. X    savepack = r->saved;
  1492. X    ppos.x = r->px;
  1493. X    ppos.y = r->py;
  1494. X    for (i = 0; i<r->ndeltas; i++) {
  1495. X#if 0
  1496. X        printf("Applying reverse change: (%d,%d) %c->%c\n",
  1497. X        r->deltas[i].x, r->deltas[i].y,
  1498. X           map[r->deltas[i].x][r->deltas[i].y], r->deltas[i].oldchar);
  1499. X#endif
  1500. X        map[r->deltas[i].x][r->deltas[i].y] = r->deltas[i].oldchar;
  1501. X    }
  1502. X    move_stack_sp--;
  1503. X    memcpy(prev_map, map, sizeof(map));
  1504. X    }
  1505. X}
  1506. END_OF_FILE
  1507.   if test 17974 -ne `wc -c <'xsokoban-3.0/play.c'`; then
  1508.     echo shar: \"'xsokoban-3.0/play.c'\" unpacked with wrong size!
  1509.   fi
  1510.   # end of 'xsokoban-3.0/play.c'
  1511. fi
  1512. if test ! -d 'xsokoban-3.0/saves' ; then
  1513.     echo shar: Creating directory \"'xsokoban-3.0/saves'\"
  1514.     mkdir 'xsokoban-3.0/saves'
  1515. fi
  1516. if test -f 'xsokoban-3.0/score.c' -a "${1}" != "-c" ; then 
  1517.   echo shar: Will not clobber existing file \"'xsokoban-3.0/score.c'\"
  1518. else
  1519.   echo shar: Extracting \"'xsokoban-3.0/score.c'\" \(9121 characters\)
  1520.   sed "s/^X//" >'xsokoban-3.0/score.c' <<'END_OF_FILE'
  1521. X#include "config_local.h"
  1522. X
  1523. X#include <stdio.h>
  1524. X#include <signal.h>
  1525. X#include <sys/types.h>
  1526. X#include <assert.h>
  1527. X#include <string.h>
  1528. X#include <unistd.h>
  1529. X#include <netinet/in.h>
  1530. X#ifdef NEED_ENDIAN
  1531. X#include <machine/endian.h> /* for ntohs */
  1532. X#endif
  1533. X
  1534. X#include "externs.h"
  1535. X#include "globals.h"
  1536. X
  1537. Xextern FILE *fopen();
  1538. X
  1539. Xextern char *username;
  1540. X
  1541. Xstatic short scoreentries;
  1542. Xstatic struct st_entry {
  1543. X  char user[MAXUSERNAME];
  1544. X  unsigned short lv, pad1, mv, pad2, ps, pad3;
  1545. X} scoretable[MAXSCOREENTRIES];
  1546. X
  1547. Xstatic FILE *scorefile;
  1548. Xstatic long sfdbn;
  1549. X
  1550. Xshort LockScore(void)
  1551. X{
  1552. X     int i, fd;
  1553. X
  1554. X     for (i = 0; i < 10; i++) {
  1555. X      fd = creat(LOCKFILE, 0666);
  1556. X      if (fd < 0)
  1557. X           sleep(1);
  1558. X      else
  1559. X           break;
  1560. X     }
  1561. X
  1562. X     if (fd < 0) {
  1563. X      /* assume that the last process to muck with the score file */
  1564. X      /* is dead                              */
  1565. X      /* XXX Should really be checking the datestamps on the score*/
  1566. X      /* file to make sure some other process hasn't mucked with  */
  1567. X      /* in the last 10 seconds! */
  1568. X      unlink(LOCKFILE);
  1569. X      fd = creat(LOCKFILE, 0666);
  1570. X     }
  1571. X
  1572. X     if (fd < 0)
  1573. X      return E_WRITESCORE;
  1574. X     else {
  1575. X      close(fd);
  1576. X      return 0;
  1577. X     }
  1578. X}
  1579. X
  1580. Xvoid UnlockScore(void)
  1581. X{
  1582. X     unlink(LOCKFILE);
  1583. X}
  1584. X     
  1585. X/* print out the score list */
  1586. Xshort OutputScore(void)
  1587. X{
  1588. X  short ret;
  1589. X
  1590. X  if ((ret = LockScore()))
  1591. X       return ret;
  1592. X
  1593. X  if ((ret = ReadScore()) == 0)
  1594. X    ShowScore();
  1595. X  UnlockScore();
  1596. X  return ((ret == 0) ? E_ENDGAME : ret);
  1597. X}
  1598. X
  1599. X/* create a new score file */
  1600. Xshort MakeNewScore(void)
  1601. X{
  1602. X  short ret = 0;
  1603. X
  1604. X  if ((ret = LockScore()))
  1605. X       return ret;
  1606. X  
  1607. X  scoreentries = 0;
  1608. X
  1609. X  if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
  1610. X    ret = E_FOPENSCORE;
  1611. X  else {
  1612. X    sfdbn = fileno(scorefile);
  1613. X    if (write(sfdbn, &scoreentries, 2) != 2)
  1614. X      ret = E_WRITESCORE;
  1615. X    fclose(scorefile);
  1616. X  }
  1617. X  UnlockScore();
  1618. X  return ((ret == 0) ? E_ENDGAME : ret);
  1619. X}
  1620. X
  1621. X/* get the players current level based on the level they last scored on */
  1622. Xshort GetUserLevel(short *lv)
  1623. X{
  1624. X  short ret = 0, pos;
  1625. X
  1626. X  if ((ret = LockScore()))
  1627. X       return ret;
  1628. X
  1629. X  if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
  1630. X    ret = E_FOPENSCORE;
  1631. X  else {
  1632. X    if ((ret = ReadScore()) == 0)
  1633. X      *lv = ((pos = FindUser()) > -1) ? scoretable[pos].lv + 1 : 1;
  1634. X  }
  1635. X  UnlockScore();
  1636. X  return (ret);
  1637. X}
  1638. X
  1639. X/* Add a new score to the score file. Show the current scores if "show". */
  1640. Xshort Score(Boolean show)
  1641. X{
  1642. X  short ret;
  1643. X
  1644. X  if ((ret = LockScore()))
  1645. X       return ret;
  1646. X  if ((ret = ReadScore()) == 0)
  1647. X    if ((ret = MakeScore()) == 0)
  1648. X      if ((ret = WriteScore()) == 0)
  1649. X    if (show) ShowScore();
  1650. X  UnlockScore();
  1651. X  return ((ret == 0) ? E_ENDGAME : ret);
  1652. X}
  1653. X
  1654. Xvoid ntohs_entry(struct st_entry *entry)
  1655. X{
  1656. X    entry->lv = ntohs(entry->lv);
  1657. X    entry->mv = ntohs(entry->mv);
  1658. X    entry->ps = ntohs(entry->ps);
  1659. X}
  1660. X
  1661. X/* read in an existing score file.  Uses the ntoh() and hton() functions
  1662. X * so that the score files transfer across systems.
  1663. X */
  1664. Xshort ReadScore(void)
  1665. X{
  1666. X  short ret = 0;
  1667. X  long tmp;
  1668. X
  1669. X  if ((scorefile = fopen(SCOREFILE, "r")) == NULL)
  1670. X    ret = E_FOPENSCORE;
  1671. X  else {
  1672. X    sfdbn = fileno(scorefile);
  1673. X    if (read(sfdbn, &scoreentries, 2) != 2)
  1674. X      ret = E_READSCORE;
  1675. X    else {
  1676. X      scoreentries = ntohs(scoreentries);
  1677. X      tmp = scoreentries * sizeof(scoretable[0]);
  1678. X      if (read(sfdbn, &(scoretable[0]), tmp) != tmp)
  1679. X    ret = E_READSCORE;
  1680. X
  1681. X      /* swap up for little-endian machines */
  1682. X      for (tmp = 0; tmp < scoreentries; tmp++) ntohs_entry(&scoretable[tmp]);
  1683. X    }
  1684. X    fclose(scorefile);
  1685. X  }
  1686. X  return ret;
  1687. X}
  1688. X
  1689. X/* Return the solution rank for table index "j". The solution rank for
  1690. X   an entry is one greater than the number of entries that are better
  1691. X   than it, unless there is a better or equal solution that is by the
  1692. X   same person, in which case the solution rank is at least "BADSOLN".
  1693. X   If two solutions are equal, the one that was arrived at first, and
  1694. X   thus has a lower table index, is considered to be better.
  1695. X   One solution is at least as good as another solution if it is at
  1696. X   least as good in numbers of moves and pushes. Note that
  1697. X   non-comparable solutions may exist.
  1698. X
  1699. X   The array "ignore" indicates that some scoretable entries should
  1700. X   be ignored for the purpose of computing rank.
  1701. X*/
  1702. X#define BADSOLN 100
  1703. Xint SolnRank(int j, Boolean *ignore)
  1704. X{
  1705. X    int i, rank = 1;
  1706. X    unsigned short level = scoretable[j].lv;
  1707. X    for (i = 0; i < j; i++) {
  1708. X    if ((!ignore || !ignore[i]) && scoretable[i].lv == level) {
  1709. X        if (scoretable[i].mv <= scoretable[j].mv &&
  1710. X        scoretable[i].ps <= scoretable[j].ps)
  1711. X        {
  1712. X        if (0 == strcmp(scoretable[i].user,
  1713. X                scoretable[j].user))
  1714. X            rank = BADSOLN;
  1715. X        else
  1716. X            rank++;
  1717. X        }
  1718. X    }
  1719. X    }
  1720. X    return rank;
  1721. X}
  1722. X
  1723. X/* Removes all score entries for a user who has multiple entries,
  1724. X * that are for a level below the user's top level, and that are not "best
  1725. X * solutions" as defined by "SolnRank". Also removes duplicate entries
  1726. X * for a level that is equal to the user's top level, but which are not
  1727. X * the user's best solution as defined by table position.
  1728. X *
  1729. X * The current implementation is O(n^2) in the number of actual score entries.
  1730. X * A hash table would fix this.
  1731. X */
  1732. X
  1733. Xvoid CleanupScoreTable()
  1734. X{
  1735. X    int i;
  1736. X    Boolean deletable[MAXSCOREENTRIES];
  1737. X    for (i = 0; i < scoreentries; i++) {
  1738. X    deletable[i] = _false_;
  1739. X    if (SolnRank(i, deletable) > MAXSOLNRANK) {
  1740. X        char *user = scoretable[i].user;
  1741. X        int j;
  1742. X        for (j = 0; j < i; j++) {
  1743. X        if (0 == strcmp(scoretable[j].user, user))
  1744. X          deletable[i] = _true_;
  1745. X        }
  1746. X    }
  1747. X    }
  1748. X    FlushDeletedScores(deletable);
  1749. X}
  1750. X
  1751. X/* Deletes entries from the score table for which the boolean array
  1752. X   contains true.
  1753. X*/
  1754. Xvoid FlushDeletedScores(Boolean delete[])
  1755. X{
  1756. X    int i, k = 0;
  1757. X    for (i = 0; i < scoreentries; i++) {
  1758. X    if (i != k) CopyEntry(k, i);
  1759. X    if (!delete[i]) k++;
  1760. X    }
  1761. X    scoreentries = k;
  1762. X}
  1763. X
  1764. X/* Adds a new user score to the score table, if appropriate. Users' top
  1765. X * level scores, and the best scores for a particular level (in moves and
  1766. X * pushes, separately considered), are always preserved.
  1767. X */
  1768. Xshort MakeScore(void)
  1769. X{
  1770. X  short pos, i;
  1771. X
  1772. X  pos = FindPos();        /* find the new score position */
  1773. X  if (pos > -1) {        /* score table not empty */
  1774. X      for (i = scoreentries; i > pos; i--)
  1775. X    CopyEntry(i, i - 1);
  1776. X    } else {
  1777. X      pos = scoreentries;
  1778. X    }
  1779. X
  1780. X  strcpy(scoretable[pos].user, username);
  1781. X  scoretable[pos].lv = scorelevel;
  1782. X  scoretable[pos].mv = scoremoves;
  1783. X  scoretable[pos].ps = scorepushes;
  1784. X  scoreentries++;
  1785. X
  1786. X  CleanupScoreTable();
  1787. X  if (scoreentries == MAXSCOREENTRIES)
  1788. X    return E_TOMUCHSE;
  1789. X  else
  1790. X    return 0;
  1791. X}
  1792. X
  1793. X
  1794. X/* searches the score table to find a specific player. */
  1795. Xshort FindUser(void)
  1796. X{
  1797. X  short i;
  1798. X  Boolean found = _false_;
  1799. X
  1800. X  for (i = 0; (i < scoreentries) && (!found); i++)
  1801. X    found = (strcmp(scoretable[i].user, username) == 0);
  1802. X  return ((found) ? i - 1 : -1);
  1803. X}
  1804. X
  1805. X/* finds the position for a new score in the score table */ 
  1806. Xshort FindPos(void)
  1807. X{
  1808. X  short i;
  1809. X  Boolean found = _false_;
  1810. X
  1811. X  for (i = 0; (i < scoreentries) && (!found); i++)
  1812. X    found = ((scorelevel > scoretable[i].lv) ||
  1813. X         ((scorelevel == scoretable[i].lv) &&
  1814. X          (scoremoves < scoretable[i].mv)) ||
  1815. X         ((scorelevel == scoretable[i].lv) &&
  1816. X          (scoremoves == scoretable[i].mv) &&
  1817. X          (scorepushes < scoretable[i].ps)));
  1818. X  return ((found) ? i - 1 : -1);
  1819. X}
  1820. X
  1821. X/* writes out the score table.  It uses ntoh() and hton() functions to make
  1822. X * the scorefile transfer across systems.
  1823. X */
  1824. Xshort WriteScore(void)
  1825. X{
  1826. X  short ret = 0;
  1827. X  long tmp;
  1828. X
  1829. X  if ((scorefile = fopen(SCOREFILE, "w")) == NULL)
  1830. X    ret = E_FOPENSCORE;
  1831. X  else {
  1832. X    sfdbn = fileno(scorefile);
  1833. X    scoreentries = htons(scoreentries);
  1834. X    if (write(sfdbn, &scoreentries, 2) != 2)
  1835. X      ret = E_WRITESCORE;
  1836. X    else {
  1837. X      scoreentries = ntohs(scoreentries);
  1838. X
  1839. X      /* swap around for little-endian machines */
  1840. X      for (tmp = 0; tmp < scoreentries; tmp++) {
  1841. X    scoretable[tmp].lv = htons(scoretable[tmp].lv);
  1842. X    scoretable[tmp].mv = htons(scoretable[tmp].mv);
  1843. X    scoretable[tmp].ps = htons(scoretable[tmp].ps);
  1844. X      }
  1845. X      tmp = scoreentries * sizeof(scoretable[0]);
  1846. X      if (write(sfdbn, &(scoretable[0]), tmp) != tmp)
  1847. X    ret = E_WRITESCORE;
  1848. X
  1849. X      /* and swap back for the rest of the run ... */
  1850. X      for (tmp = 0; tmp < scoreentries; tmp++) {
  1851. X    scoretable[tmp].lv = ntohs(scoretable[tmp].lv);
  1852. X    scoretable[tmp].mv = ntohs(scoretable[tmp].mv);
  1853. X    scoretable[tmp].ps = ntohs(scoretable[tmp].ps);
  1854. X      }
  1855. X    }
  1856. X    fclose(scorefile);
  1857. X  }
  1858. X  return ret;
  1859. X}
  1860. X
  1861. X
  1862. X/* displays the score table to the user */
  1863. Xvoid ShowScore(void)
  1864. X{
  1865. X  register i;
  1866. X
  1867. X  fprintf(stdout, "Rank      User     Level     Moves    Pushes\n");
  1868. X  fprintf(stdout, "============================================\n");
  1869. X  for (i = 0; i < scoreentries; i++) {
  1870. X    int rank = SolnRank(i, 0);
  1871. X    if (rank <= MAXSOLNRANK) fprintf(stdout, "%4d", rank);
  1872. X    else fprintf(stdout, "    ");
  1873. X    fprintf(stdout, "%10s  %8d  %8d  %8d\n", scoretable[i].user,
  1874. X        scoretable[i].lv, scoretable[i].mv, scoretable[i].ps);
  1875. X  }
  1876. X}
  1877. X
  1878. X/* duplicates a score entry */
  1879. Xvoid CopyEntry(short i1, short i2)
  1880. X{
  1881. X  strcpy(scoretable[i1].user, scoretable[i2].user);
  1882. X  scoretable[i1].lv = scoretable[i2].lv;
  1883. X  scoretable[i1].mv = scoretable[i2].mv;
  1884. X  scoretable[i1].ps = scoretable[i2].ps;
  1885. X}
  1886. END_OF_FILE
  1887.   if test 9121 -ne `wc -c <'xsokoban-3.0/score.c'`; then
  1888.     echo shar: \"'xsokoban-3.0/score.c'\" unpacked with wrong size!
  1889.   fi
  1890.   # end of 'xsokoban-3.0/score.c'
  1891. fi
  1892. if test ! -d 'xsokoban-3.0/scores' ; then
  1893.     echo shar: Creating directory \"'xsokoban-3.0/scores'\"
  1894.     mkdir 'xsokoban-3.0/scores'
  1895. fi
  1896. if test ! -d 'xsokoban-3.0/screens' ; then
  1897.     echo shar: Creating directory \"'xsokoban-3.0/screens'\"
  1898.     mkdir 'xsokoban-3.0/screens'
  1899. fi
  1900. echo shar: End of archive 1 \(of 4\).
  1901. cp /dev/null ark1isdone
  1902. MISSING=""
  1903. for I in 1 2 3 4 ; do
  1904.     if test ! -f ark${I}isdone ; then
  1905.     MISSING="${MISSING} ${I}"
  1906.     fi
  1907. done
  1908. if test "${MISSING}" = "" ; then
  1909.     echo You have unpacked all 4 archives.
  1910.     rm -f ark[1-9]isdone
  1911. else
  1912.     echo You still must unpack the following archives:
  1913.     echo "        " ${MISSING}
  1914. fi
  1915. exit 0
  1916. exit 0 # Just in case...
  1917. -- 
  1918.   // chris@Sterling.COM           | Send comp.sources.x submissions to:
  1919. \X/  Amiga: The only way to fly!  |    sources-x@sterling.com
  1920.        "It's intuitively obvious to the most casual observer..."
  1921.  GCS d++(--) -p+ c++ !l u++ e+ m+(-) s++/++ n h--- f+ g+++ w+ t++ r+ y+
  1922.