pkg://xsok-1.02-8.src.rpm:118461/xsok-1.02-src.tar.gz
info downloads
xsok-1.02/ 40755 144 62 0 6123003304 10747 5 ustar mbi mathopt xsok-1.02/Makefile 100644 144 62 2175 6122776755 12543 0 ustar mbi mathopt # Note: Typing 'make' from this level will rebuild xsok from scratch.
# Afterwards, type 'make install' or 'make install.fsstnd'
# (as root) to install the game in default directories.
# A manual is in the doc subdirectory and can be TeXed by 'make manual'.
#
# You may change src/Imakefile for different configurations.
# But then, you're on your own...
all:
(cd src && xmkmf && $(MAKE) && strip xsok)
(cd lib && $(MAKE))
(cd src && $(MAKE) testname)
manual:
(cd doc && $(MAKE) xsok.dvi)
# different install targets: imake default, local, Linux FSSTND
install:
(cd src && $(MAKE) install)
install.local:
(cd src && $(MAKE) install.local)
install.fsstnd:
(cd src && $(MAKE) install.fsstnd)
clean:
(cd lib && $(MAKE) clean)
(cd src && xmkmf && $(MAKE) clean)
(cd doc && $(MAKE) clean)
(cd solver && $(MAKE) clean)
rm -f src/Makefile
find . -name "*~" -exec rm \{\} \;
distrib:
$(MAKE) clean
(cd ..; tar cvfz $(HOME)/xsok-1.02-src.tar.gz xsok-1.02)
bindistrib:
(cd /; tar cvfz $(HOME)/xsok-1.02-bin.tar.gz var/games/xsok/*.score \
usr/games/bin/xsok usr/man/man6/xsok.6x usr/games/lib/xsok \
usr/doc/xsok)
xsok-1.02/src/ 40755 144 62 0 6151124576 11556 5 ustar mbi mathopt xsok-1.02/src/xsok.h 100644 144 62 22162 6122601264 13023 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module xsok.h */
/* */
/* This file is included by all sources of xsok. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
/* defaults for user configurable directories */
#ifndef XSOKDIR /* directory where to get the game data files */
#define XSOKDIR "/usr/games/lib/xsok"
#endif
#ifndef XSOKSAVE /* directory where to save moves of solved levels */
#define XSOKSAVE "/var/games/xsok"
#endif
#define MAXSAVEFILELEN 200 /* pathname length including trailing zero */
#define MAXXSOKDIRLEN 99 /* pathname length of the xsok directory */
#define MAXFILENAMELEN 30 /* filename length of keyboard and message file */
/* maximum board dimensions, inclusive outer borders (tunable) */
#define MAXCOL 32 /* max x dim. */
#define MAXROW 22 /* max y dim. */
/* other maximum settings (tunable) */
#define MAXWALLS 32 /* max. different floor types */
#define MAXOBJECTS 32 /* max. different object types */
#define MAXINSTANCES 256 /* max. number of objects */
/* global string variables */
extern char username[256]; /* which user to blame for success */
extern const char *savedir; /* in which directory should saved games be stored (XSOKSAVE) */
extern const char *xsokdir; /* from which directory to read the level files (default: XSOKDIR) */
extern const char *langdir; /* a string (empty?) defining the language */
extern const char *rulepool[16];
extern int highscore[300];
extern int pushcost;
extern int movecost;
extern void (*lastcmd)(void);
extern int numeric_arg;
#ifndef EXIT_FAILURE /* poor old SUN's */
#define EXIT_FAILURE (-1)
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
typedef int boolean; /* just one bit of information */
struct key_action {
char *string;
void (*action)(void);
struct key_action *next;
};
#define True 1
#define False 0
#if 0
/* obsolete */
#define XSOK1MAGIC1 0xb5
#define XSOK1MAGIC2 0xa0
#define ARR_UP 0x100
#define ARR_LEFT 0x101
#define ARR_DOWN 0x102
#define ARR_RIGHT 0x103
#endif
#define Disable 0
#define Enable 1
#define EnableAndRedraw 2
struct walls {
int chr; /* character for representation */
int pic; /* picture number */
int enter;
int leave;
int mask;
int effect;
};
/* definitions for the effect field */
/* all effects plus 100: square will turn to normal floor if touched. */
#define E_ONCE 100 /* add this to make one-time effects */
#define E_NOTHING 0
#define E_TURN_CCW 1
#define E_TURN_180 2
#define E_TURN_CW 3
#define E_DEST 4 /* no effect, but required by finished() */
#define E_EXIT 5 /* dito */
#define E_ADDPOWER 6
#define E_SUBPOWER 7
#define E_TELEPORT 8
/* addstrength, teleporters */
struct objects {
int chr; /* character for representation */
int pic; /* picture number */
int movedir;
int pushdir;
int weight;
int power;
int mask;
int score;
};
struct game {
int numrows; /* game size */
int numcols;
int x; /* player pos */
int y;
int n_pushes; /* counter */
int n_moves;
int stored_moves;
int bookmark;
int finished;
int level;
int score;
const char *type;
int macroStart, macroEnd;
int macro_x, macro_y;
};
extern char levelcomment[100];
extern char levelauthor[100];
extern int gamegraphic;
extern int maxlevel; /* maximum level number for this type of game */
extern int nwalls, nobjects, ninstances;
extern struct objects objects[MAXOBJECTS];
extern struct walls walls[MAXWALLS]; /* wall types */
extern struct game game;
extern struct walls *map[MAXROW][MAXCOL];
extern struct objects *obj[MAXROW][MAXCOL];
extern struct objects instance[MAXINSTANCES];
extern char *movetab;
extern int numalloc;
/* function prototypes */
/* parse.c */
void ParseDefinitionFile(void); /* read definitions */
void ParseMapFile(void); /* read a level */
void OrgLevel(void); /* restart game */
/* tools.c */
void fatal(const char *, ...);
void *malloc_(size_t);
void *calloc_(size_t, size_t);
void *realloc_(void *, size_t);
void free_(void *);
char *strsav(const char *);
/* main.c */
void change_rules(const char *);
int compute_score(void);
int finished(void);
/* move.c */
#if 0
void savegame(const char *);
int loadgame(const char *);
#endif
void graphics_control(int);
void playermove(int);
#if 0
void restart(void);
int redo_move(void);
int undo_move(void);
#endif
/* username.c */
void buildusername(const char *);
/* messages.c */
void read_message_file(const char *);
void add_keybinding(struct key_action **, const char *, const char *);
void read_keyboard_file(const char *);
void key_pressed(char *);
/* loadsave.c */
void cmd_ReadHighscores(void);
void WriteHighscores(void);
void switch_uid(int);
void setlangdir(void);
void load_game(const char *);
void save_game(const char *);
void link_game(const char *, const char *);
/* commands.c */
void cmd_Up(void);
void cmd_Left(void);
void cmd_Down(void);
void cmd_Right(void);
void cmd_Repeat(void);
void cmd_LevelInfo(void);
void cmd_NextUnsolved(void);
void cmd_NextLevel(void);
void cmd_PrevLevel(void);
void rq_LeaveSok(void);
void rq_RestartGame(void);
void rq_PrevLevel(void);
void rq_NextLevel(void);
void rq_NextUnsolved(void);
void cmd_DropBookmark(void);
void jumpto_movenr(int);
void cmd_RestartGame(void);
void cmd_GotoBookmark(void);
void cmd_SaveGame(void);
void cmd_ShowVersion(void);
void cmd_ShowScore(void);
void cmd_ShowBestScore(void);
void cmd_ShowAuthor(void);
void cmd_ReplayGame(void);
void cmd_LoadGame(void);
void cmd_UndoMove(void);
void cmd_RedoMove(void);
extern const char *xsok_messages[];
#define TXT_QUIT_CONFIRM (xsok_messages[0])
#define TXT_NEW_CONFIRM (xsok_messages[1])
#define TXT_RESTART_CONFIRM (xsok_messages[2])
#define TXT_NEXT_CONFIRM (xsok_messages[3])
#define TXT_PREV_CONFIRM (xsok_messages[4])
#define TXT_MOVENOTPOSSIBLE (xsok_messages[5])
#define TXT_BOOKMARK_SET (xsok_messages[6])
#define TXT_YOU_WIN (xsok_messages[7])
#define TXT_OK (xsok_messages[8])
#define TXT_VERSION (xsok_messages[9])
#define TXT_SCORE (xsok_messages[10])
#define TXT_NOUNDO (xsok_messages[11])
#define TXT_UNDO (xsok_messages[12])
#define TXT_NOREDO (xsok_messages[13])
#define TXT_REDO (xsok_messages[14])
#define TXT_WELCOME (xsok_messages[15])
#define TXT_SAVE_ERR_BASIC (xsok_messages[16])
#define TXT_LOAD_ERR_BASIC (xsok_messages[17])
#define TXT_SAVE_ERR_OPEN (xsok_messages[18])
#define TXT_LOAD_ERR_OPEN (xsok_messages[19])
#define TXT_SAVE_ERR_HEADER (xsok_messages[20])
#define TXT_LOAD_ERR_HEADER (xsok_messages[21])
#define TXT_SAVE_ERR_MOVES (xsok_messages[22])
#define TXT_LOAD_ERR_MOVES (xsok_messages[23])
#define TXT_SAVE_OK (xsok_messages[24])
#define TXT_LOAD_OK (xsok_messages[25])
#define TXT_LOAD_ERR_BADMAGIC (xsok_messages[26])
#define TXT_NOAUTHOR (xsok_messages[27])
#define TXT_NEWHIGH (xsok_messages[28])
#define TXT_NOLOAD (xsok_messages[29])
#define TXT_HELP_KEYS (xsok_messages[30])
#define TXT_HELP_RULES (xsok_messages[31])
#define TXT_STARTMACRO (xsok_messages[32])
#define TXT_ENDMACRO (xsok_messages[33])
#define TXT_MACRO_BADPOS (xsok_messages[34])
#define TXT_UNSOLVED_CONFIRM (xsok_messages[35])
#define TXT_BEST (xsok_messages[36])
#define TXT_UNSOLVED (xsok_messages[37])
#define TXT_SAVE_ERR_LINK (xsok_messages[38])
#define TXT_NOBOX (xsok_messages[39])
#define TXT_ALREADYBOX (xsok_messages[40])
/* Xaw-help.c */
/*
void create_help(Widget);
void popup_help(void);
void popdown_help(Widget, XtPointer, XtPointer);
*/
/* Xaw-main.c */
void show_message(const char *str, ...);
void SetTitle(void);
void cmd_LeaveSok(void);
void cmd_Confirm(void);
void cmd_Cancel(void);
void request_confirm(void (*)(void), const char *);
#ifdef SOUND
int checksound(void);
#endif
int main(int argc, char *argv[]);
/* void Force_Resize(XSize_t, XSize_t); */
/* X-widget.c */
/* void AskWidgetForResize(XSize_t, XSize_t); */
/* X-events.c */
void refresh_screen(void);
/* void button_press(XButtonPressedEvent *);
void key_press(XKeyPressedEvent *); */
void cmd_Resize(void);
/* void resize_event(XSize_t, XSize_t); */
/* X-gfx.c */
void sync_and_wait(void);
void NewLevel(int);
void init_layout(void);
void init_gfx(const char *);
/* void dotPaint(int, int, int, int); */
void doPaint(int, int, int, int);
/* void redraw_table(XExposeEvent *); */
#if SOUND
/* X-sound_SUN.c */
int checksound(void);
void play_sound(const char *);
#else
#define play_sound(x)
#endif
/* dummy.c */
void cmd_debug(void);
/* xfopen.c */
FILE *zreadopen(const char *filename);
void zreadclose(FILE *fp);
/* mousemove.c */
void cmd_MouseUndo(void);
void cmd_MouseMove(void);
void cmd_MousePush(void);
void cmd_MouseDrag(void);
extern int mouse_x, mouse_y, mouse_x0, mouse_y0;
void cmd_StartMacro(void);
void cmd_EndMacro(void);
void cmd_PlayMacro(void);
xsok-1.02/src/xfopen.c 100644 144 62 4346 5665071060 13323 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module xfopen.c */
/* */
/* Possible emulation of the popen() / pclose() functions. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#ifdef HAVE_POPEN /* extra prototype (they are not POSIX.1) */
#include "xsok.h"
FILE *popen(const char *, const char *);
int pclose(FILE *);
FILE *zreadopen(const char *filename) {
char zcmd[MAXXSOKDIRLEN+20+100]; /* assume strlen(GUNZIP_PATH) <= 100 */
sprintf(zcmd, "%s < %s.gz", GUNZIP_PATH, filename);
return popen(zcmd, "r");
}
void zreadclose(FILE *fp) {
pclose(fp);
}
#else
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "xsok.h"
#ifndef GUNZIP_PATH
#define GUNZIP_PATH "gunzip"
#endif
FILE *zreadopen(const char *filename) {
int filedes[2];
char zname[MAXXSOKDIRLEN+17];
FILE *fp;
sprintf(zname, "%s.gz", filename);
if (access(zname, R_OK))
return NULL; /* simpler and more correct */
if (GUNZIP_PATH[0] == '/')
if (access(GUNZIP_PATH, X_OK))
return NULL; /* simpler and more correct */
/* following taken from D. Lewine "POSIX programmers guide, page 104 */
/* try fork and exec. This requires the path of the gzip binary */
/* but it is much faster */
if (pipe(filedes))
return NULL; /* cannot create pipe */
switch (fork()) {
case -1: /* cannot fork */
/* close the pipe and return NULL */
close(filedes[0]);
close(filedes[1]);
return NULL;
case 0: /* we are the child. exec the gunzip binary */
close(STDOUT_FILENO);
dup(filedes[1]);
close(filedes[0]);
close(filedes[1]);
execlp(GUNZIP_PATH, "gunzip", "-c", zname, NULL);
exit(1); /* if exec failed! */
default: /* we are the main process */
fp = fdopen(filedes[0], "r");
close(filedes[1]);
}
return fp;
}
void zreadclose(FILE *fp) {
int status;
fclose(fp);
wait(&status);
}
#endif
xsok-1.02/src/Tableau.h 100644 144 62 1550 5665071060 13400 0 ustar mbi mathopt #ifndef _Tableau_h
#define _Tableau_h
/* define any special resource names here that are not in <X11/StringDefs.h> */
#define XtNruleset "rules"
#define XtCRuleset "Rules"
#define XtNlevel "level"
#define XtCLevel "Level"
#define XtNusername "username"
#define XtCUsername "Username"
#define XtNxsokdir "xsokdir"
#define XtCXsokdir "Xsokdir"
#define XtNxpmdir "xpmdir"
#define XtCXpmdir "Xpmdir"
#define XtNsavedir "xsokdir"
#define XtCSavedir "Xsokdir"
#define XtNmessageFile "messageFile"
#define XtCMessageFile "MessageFile"
#define XtNkeyboardFile "keyboardFile"
#define XtCKeyboardFile "KeyboardFile"
/* declare specific TableauWidget class and instance datatypes */
typedef struct _TableauClassRec* TableauWidgetClass;
typedef struct _TableauRec* TableauWidget;
/* declare the class constant */
extern WidgetClass tableauWidgetClass;
#endif /* _Tableau_h */
xsok-1.02/src/TableauP.h 100644 144 62 1431 5665071060 13516 0 ustar mbi mathopt #ifndef _TableauP_h
#define _TableauP_h
#include "Tableau.h"
/* include superclass private header file */
#include <X11/CoreP.h>
/* define unique representation types not found in <X11/StringDefs.h> */
#define XtRTableauResource "TableauResource"
typedef struct {
int empty;
} TableauClassPart;
typedef struct _TableauClassRec {
CoreClassPart core_class;
TableauClassPart tableau_class;
} TableauClassRec;
extern TableauClassRec tableauClassRec;
typedef struct {
/* resources */
String rules;
int level;
String username;
String xsokdir;
String xpmdir;
String savedir;
String messageFile;
String keyboardFile;
} TableauPart;
typedef struct _TableauRec {
CorePart core;
TableauPart tableau;
} TableauRec;
#endif /* _TableauP_h */
xsok-1.02/src/X-events.c 100644 144 62 10453 6122570645 13553 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module X-events.c */
/* */
/* Event handler functions for the X window system. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "X-sok.h"
#include <X11/keysym.h> /* X11 key code definitions */
void refresh_screen(void) {
XClearArea(dpy, table, 0, 0, 0, 0, True);
}
/* event entry points are: key_press, button_press, button_release, redraw_table */
int mouse_x = 0, mouse_y = 0, mouse_x0 = 0, mouse_y0 = 0;
void button_press(XButtonPressedEvent *xev) {
mouse_x0 = xev->x / DX;
mouse_y0 = xev->y / DY;
}
void button_release(XButtonPressedEvent *xev) {
mouse_x = xev->x / DX;
mouse_y = xev->y / DY;
switch (xev->button) {
case Button1: /* quick move */
key_pressed("Mouse1");
break;
case Button2: /* quick move */
key_pressed("Mouse2");
break;
case Button3: /* quick move */
key_pressed("Mouse3");
break;
case Button4: /* quick move */
key_pressed("Mouse4");
break;
case Button5: /* quick move */
key_pressed("Mouse5");
break;
}
}
void key_press(XKeyPressedEvent *xev) {
char str[32];
int num;
#define get_name_field() get_selection()
num = XKeycodeToKeysym(dpy, xev->keycode, 0);
if (num & 0xff00) {
switch (num) {
case XK_Up:
key_pressed("Up");
return;
case XK_Left:
key_pressed("Left");
return;
case XK_Down:
key_pressed("Down");
return;
case XK_Right:
key_pressed("Right");
return;
case XK_Return:
case XK_Linefeed:
key_pressed("\n");
return;
case XK_BackSpace:
case XK_Delete:
key_pressed("\b");
return;
case XK_Escape:
key_pressed("\033");
return;
}
return;
}
num = XLookupString(xev, str, 31, NULL, NULL);
if (num == 0)
return;
str[num] = '\0'; /* NULL to terminate it */
key_pressed(str);
}
/*****************************************************************************/
/* */
/* Functions for resize events and resize requests */
/* */
/*****************************************************************************/
/* 1) hard resizes (i.e. forcing the outer window to change size) */
/* I think these are not liked in the Xaw community */
void cmd_Resize(void) {
XSize_t w, h;
w = graphic.width;
h = graphic.height;
Force_Resize(w, h);
}
/* event handler function. This function is called by the Widget in response
to a request from us. In Xaw, this is a resize of the logical area, i.e.
of the virtual size of the tableau. */
void resize_event(XSize_t w, XSize_t h) {
#ifdef LABER
printf("resize event to (%d,%d) called\n", w, h);
#endif
if (graphic.height == h && graphic.width == w)
return; /* no change of size */
/* in xlib, we must clear the new area by hand; there may be illegal data
left in the server. This applies to Xaw as well */
{ XExposeEvent xev;
xev.count = -1;
if (gamegraphic) {
if (graphic.height < h) {
/* window is greater now */
XClearArea(dpy, table, 0, graphic.height, graphic.width, h - graphic.height, True);
++xev.count;
}
if (graphic.width < w) {
/* window is greater now */
XClearArea(dpy, table, graphic.width, 0, w - graphic.width, h, True);
++xev.count;
}
if (xev.count >= 0) {
/* generate synthetic expose events for the new area */
/* this must be done before we possibly change the layout */
if (graphic.height < h) {
/* window is greater now */
xev.x = 0;
xev.y = graphic.height;
xev.width = graphic.width;
xev.height = h - graphic.height;
redraw_table(&xev);
--xev.count;
}
if (graphic.width < w) {
/* window is greater now */
xev.x = graphic.width;
xev.y = 0;
xev.width = w - graphic.width;
xev.height = h;
redraw_table(&xev);
}
}
}
}
graphic.height = h;
graphic.width = w;
if (!gamegraphic)
return;
}
xsok-1.02/src/xsok.man 100644 144 62 17237 6151124521 13354 0 ustar mbi mathopt .TH XSOK 6 "May 1996" "Handmade"
.SH NAME
xsok \- generic Sokoban game for X11, Version 1.02
.SH SYNOPSIS
.B xsok
[
.I options
]
.SH DESCRIPTION
.B xsok
is a single player strategic game, a superset of the well known Sokoban game.
This manpage describes only the user interface of \fBxsok\fP. If you want to
create own levels, you should consult the \fBxsok\fP manual for more information.
The target of \fBSokoban\fP
is to push all the objects into the score area of each level using the
mouse or the arrow keys. For the other level subsets, there are different
kinds of objects, and special effect squares.
\fBxsok\fP can be played using only the mouse, or only the keyboard. Keyboard
and mouse bindings are defined through a textfile. This manual page describes
the default bindings.
.SH OPTIONS
All standard X toolkit parameters may be given, such as
\fB\-display\fP \fIdisplay\fP etc.
Additional options are
.TP 4
.B \-rules \fIlevel subset\fP
This option specifies the initial level subset for \fBxsok\fP.
Valid built-in rule names are \fBSokoban\fP, \fBCyberbox\fP, and \fBXsok\fP,
but you may implement new level subsets without recompiling the game.
Level subsets share common characteristics of the board.
In \fBSokoban\fP, for example, all boxes have the same weight.
In \fBXsok\fP, the first level is a demo level, where you can experiment
with the new objects.
.TP 4
.B \-level \fIstartlevel\fP
Set the starting level.
.TP 4
.B \-username \fIusername\fP
In a save-game file, your name, as found in the \fB/etc/passwd\fP file, and the
hostname of your computer, will be stored in the file. The default format is
\fIrealname (username@hostname.domain)\fP, for example
\fBMichael Bischoff (mbi@flawless.ts.rz.tu-bs.de)\fP.
You can override this default string with the argument to the username option and provide a different e-mail address, for example
.br
\fBxsok -username "Michael Bischoff (m.bischoff@tu-bs.de)"\fP.
If you break the scores for one level, your solution will be saved automatically.
.TP 4
.B \-xsokdir \fIxsokdir\fP
This option sets the root of the \fBxsok\fP data file tree. The default is
\fB/usr/games/lib/xsok\fP.
.TP 4
.B \-xpmdir \fIxpmdir\fP
This gives the directory from where to load the graphic data.
.TP 4
.B \-savedir \fIsavedir\fP
This option sets the directory for save game files and the \fBxsok\fP highscore
files. The default is \fB/var/games/xsok\fP.
.TP 4
.B \-messageFile \fImessagefile\fP
This option sets the name of an alternative message file for \fBxsok\fP.
The pathname is relative to \fIxsokdir\fP. The default is \fBmessages\fP,
and does not exist, which means to use the internal messages.
.TP 4
.B \-keyboardFile \fIkeyboardfile\fP
This option sets the name of the file defining the keyboard bindings.
The pathname is relative to \fIxsokdir\fP. The default is \fBkeys\fP.
The bindings in the default file are described below.
All command line options may be abbreviated, or set by the X11 resource
manager. The resource name for option \fB\-xyz\fP is \fBTableau.xyz\fP and its
class name \fBTableau.Xyz\fP.
.SH KEYBOARD BINDINGS
The arrow keys will move the man. The default binding is similar to the
binding in \fBxsokoban\fP. Some commands accept a numerical prefix (i.e.
typing some digits before the command key), which usually is used as an
operation count.
.TP 8
.B a
Display the author of a level (if known).
.TP 8
.B b
Drops the bookmark.
.TP 8
.B g
Goto bookmark.
.TP 8
.B i
Displays the level comment (if any).
.TP 8
.B s
Saves the current position.
.TP 8
.B L
Reloads a saved game.
.TP 8
.B R
Restart this level. With numerical prefix \fIn\fP, jumps to move number
\fIn\fP.
.TP 8
.B N
Proceed to the next level. With numerical prefix \fIn\fP,
jumps to level \fIn\fP.
.TP 8
.B H
Reread the highscore table.
.TP 8
.B P
Return to the previous level.
.TP 8
.B U
Proceeds to the next unsolved level.
.TP 8
.B q
Quits the game.
.TP 8
.B v
Shows the version of \fBxsok\fP.
.TP 8
.B ?
Shows the current score.
.TP 8
.B b
Shows the best score for this level.
.TP 8
.B c
Drops the bookmark at the current position.
.TP 8
.B u
Undoes the last elementary move. Accepts numerical prefix.
.TP 8
.B r
Redoes last move (undoes an undo). Accepts numerical prefix.
.TP 8
.B (
Starts recording a macro (sequence of moves)
.TP 8
.B )
End a macro.
.TP 8
.B <ENTER>
Replays a macro.
.SH KEYBOARD BINDINGS
With the default button assignment, button 1 is bound to the
function \fBMouseMove\fP. If pressed on a clear square, the man will move to
that location via the optimal path if such a path exists. If pressed on an
object that is adjacent to the player, the object will be pushed.
Button 2 is bound to \fBMouseDrag\fP. This command requires that you press the
mouse button on a location where a box resides, drag the mouse, and release the
button on an empty square. The man will then move the box from the first square
onto the second with the minimal number of pushes, if it is possible at all.
Please note that the man will not move any other object and will only use
squares without effects.
Button 3 is bound to \fBMouseUndo\fP. This function undoes one of the previous
commands, which would possibly require a lot of calls to the atomic undo function.
.SH NATIONAL LANGUAGE SUPPORT
\fBxsok\fP has simple support for different languages. All messages which
appear in the X11 window may be overloaded by files, as well as the key
bindings. The typical support consists of an application-defaults file, a
message file, and a keyboard file. Possibly translated online-help files are
also there. To select a different language, call \fBxsok\fP after setting the
environment variable \fBLANG\fP to the desired value. Currently, no translated
version is available.
.SH FILES
(Directories may differ on your system.)
\fB/usr/games/bin/xsok\fP
\fB/var/games/xsok/\fP\fItype\fP\fB.score\fP
\fB/var/games/xsok/\fP\fItype\fP\fB.\fP\fInn\fP\fB.{sv,bs,mp,mm}\fP
\fB/usr/doc/xsok/COPYRIGHT.{GNU,xsok,xpm}\fP
\fB/usr/doc/xsok/xsok.dvi\fP
\fB/usr/doc/xsok/cyberbox.doc\fP
\fB/usr/games/lib/xsok/floor.xpm.gz\fP
\fB/usr/games/lib/xsok/objects.xpm.gz\fP
\fB/usr/games/lib/xsok/keys\fP
\fB/usr/games/lib/xsok/keys.help\fP
\fB/usr/games/lib/xsok/\fP\fItype\fP\fB.def.gz\fP
\fB/usr/games/lib/xsok/\fP\fItype\fP\fB.help\fP
Where \fItype\fP is one of \fBSokoban\fP, \fBXsok\fP, \fBCyberbox\fP, and
possibly others.
.SH CREDITS
Inspiration for \fBxsok\fP came from \fBxsokoban\fP, a previous implementation
of the \fBSokoban\fP game by Joseph L. Traub. From this game, the wall graphics
were taken, and the mouse button assignment. \fBxsokoban\fP's level files can
be used without change, but by default, all level files of a level subset are
combined into a single file. Of course, credits also go to the unknown author
of the curses based game.
The \fBCyberbox\fP levels (and a MSDOS game of the same name) are written by
Doug Beeferman.
.SH BUGS
The undo function is too slow. Highscore file handling uses no file locking.
\fBCyberbox\fP zappers are implemented as one-way passages, which causes worse
scores and easier levels.
Please mail bug reports to \fBmbi@mo.math.nat.tu-bs.de\fP. Fixes are
especially welcome.
.SH SEE ALSO
\fBxsokoban(6x)\fP, \fBsokoban(6)\fP
.SH AUTHOR
Michael Bischoff
.SH COPYRIGHT
Copyright (c) 1994 by Michael Bischoff (\fBmbi@mo.math.nat.tu-bs.de\fP)
.sp 1
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided that
the above copyright notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting documentation.
\fBxsok\fP was developed under Linux, the free UNIX for the IBM-PC and
compatibles. \fBxsok\fP is distributed by terms of the GNU General public
license (GNU Copyleft).
xsok-1.02/src/X-gfx.c 100644 144 62 11203 6046457411 13025 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module X-gfx.c */
/* */
/* Drawing routines for the X window system. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "X-sok.h"
/*#include <X11/X.h>*/
#include <xpm.h>
#ifndef DELAY
#define DELAY 50 /* time to sleep (in ms) between auto-moves & replay */
#endif
Display *dpy;
Window table = 0;
struct graphic graphic;
int gamegraphic = 1;
static Pixmap floor, objd, objclip;
static GC gc;
static int resize_pending = 0;
#ifndef HAVE_USLEEP
#include <sys/time.h>
/* usleep emulation code taken from xboing-2.2 by Justin C. Kibell */
static int usleep(unsigned long usec) {
#ifdef SYSV
#ifdef __clipper__
struct timeval tv;
tv.tv_sec=((usec)/1000);
tv.tv_usec=(((usec)%1000)*1000);
select(1,NULL,NULL,NULL,&tv);
#else
poll((struct poll *) 0, (size_t) 0, usec / 1000); /* ms resolution */
#endif
#else
struct timeval timeout;
timeout.tv_usec = usec % (unsigned long) 1000000;
timeout.tv_sec = usec / (unsigned long) 1000000;
select(0, (void *) 0, (void *) 0, (void *) 0, &timeout);
#endif
return 0;
}
#endif
void sync_and_wait(void) {
XSync(dpy, 0);
usleep(DELAY*1000);
}
void NewLevel(int levelnr) {
if (levelnr < 1)
levelnr = 1;
if (levelnr > maxlevel)
levelnr = maxlevel;
game.level = levelnr;
ParseMapFile();
SetTitle();
if (table) {
init_layout();
AskWidgetForResize(graphic.width, graphic.height);
cmd_Resize();
if (gamegraphic)
refresh_screen();
resize_pending = 1;
}
}
void init_layout(void) {
graphic.width = game.numcols * DX;
graphic.height = game.numrows * DY;
}
void init_gfx(const char *xpmdir) {
int screen, retcode;
char s[MAXXSOKDIRLEN+14];
screen = DefaultScreen(dpy);
gc = XDefaultGC(dpy, screen);
sprintf(s, "%s/floor.xpm", xpmdir);
if ((retcode = XpmReadFileToPixmap(dpy, RootWindow(dpy, screen),
s, &floor, 0, NULL)) == XpmSuccess) {
sprintf(s, "%s/objects.xpm", xpmdir);
retcode = XpmReadFileToPixmap(dpy, RootWindow(dpy, screen), s, &objd,
&objclip, NULL);
}
switch (retcode) {
case XpmSuccess:
return; /* no error */
case XpmColorFailed:
case XpmColorError:
fatal("Not enough colors for %s", s);
case XpmOpenFailed:
fatal("Cannot open %s", s);
case XpmFileInvalid:
fatal("Invalid File: %s", s);
case XpmNoMemory:
fatal("Out of memory reading %s", s);
default:
fatal("Unknown error (code %d) reading %s", s);
}
}
#define ODRAW(c) { \
XSetClipOrigin(dpy, gc, x*DX - ((c)&3)*DX, y*DY - ((c)>>2)*DY);\
XCopyArea(dpy, objd, table, gc, DX*((c)&3), DY*((c)>>2), DX, DY, (x)*DX, (y)*DY); }
static void do_redraw(int x, int y) {
if (x >= game.numcols || y >= game.numrows || map[y][x]->pic == 16)
return; /* x = y = 0; */
#ifndef SIMPLE_WALLS
if (!map[y][x]->pic) {
int b;
b = 0;
if (!map[y][x-1]->pic)
b += 1;
if (!map[y+1][x]->pic)
b += 2;
if (!map[y][x+1]->pic)
b += 4;
if (!map[y-1][x]->pic)
b += 8;
XCopyArea(dpy, floor, table, gc,
DX * (b & 7), DY * (b / 8), DX, DY, (x)*DX, (y)*DY);
} else
#endif
XCopyArea(dpy, floor, table, gc, DX * (map[y][x]->pic&7),
DY * (map[y][x]->pic >> 3), DX, DY, (x)*DX, (y)*DY);
if (obj[y][x]) {
int c;
XSetClipMask(dpy, gc, objclip);
c = obj[y][x]->pic;
ODRAW(c);
XSetClipMask(dpy, gc, None);
}
}
static void dotPaint(int minx, int miny, int maxx, int maxy) {
int x, y;
if (minx > maxx || miny > maxy) {
int h;
h = minx; minx = maxx; maxx = h;
h = miny; miny = maxy; maxy = h;
}
x = minx-1;
do {
++x;
for (y = miny; y <= maxy; ++y) {
do_redraw(x, y);
}
} while (x != maxx);
}
/* doPaint: just request it */
void doPaint(int minx, int miny, int maxx, int maxy) {
#if 0
XClearArea(dpy, table, minx*DX, miny*DY, (maxx+1)*DX-1, (maxy+1)*DY-1, True);
#else
dotPaint(minx, miny, maxx, maxy);
#endif
}
void redraw_table(XExposeEvent *xev) {
int minx, miny, maxx, maxy;
if (resize_pending) {
if (xev->count)
return;
/* after a resize, do a complete redraw */
XClearArea(dpy, table, 0, 0, 0, 0, False);
dotPaint(1, 1, game.numcols-2, game.numrows-2);
resize_pending = 0;
} else {
minx = xev->x / DX;
miny = xev->y / DY;
maxx = (xev->x + xev->width - 1) / DX;
maxy = (xev->y + xev->height - 1) / DY;
dotPaint(minx, miny, maxx, maxy);
}
}
xsok-1.02/src/score.c 100644 144 62 3065 5665071060 13134 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module score.c */
/* */
/* Score computation and checking for finished levels. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
void change_rules(const char *type) {
game.type = type;
game.level = 0;
ParseDefinitionFile();
}
int compute_score(void) {
int x, y;
int retval = 1;
game.score = 0;
for (x = 1; x < game.numcols; ++x)
for (y = 1; y < game.numrows; ++y) {
int c;
c = map[y][x]->effect;
if (c == E_EXIT) {
if (game.x != x || game.y != y)
retval = 0; /* EXIT field with no player on it */
else
game.score += obj[y][x]->score;
}
if (c == E_DEST)
if (!obj[y][x] || !(obj[y][x]->mask & ~1))
retval = 0; /* player doesn't score! */
else
game.score += obj[y][x]->score;
}
if (retval && !objects->score)
game.score += 10000; /* finished-score if no special EXIT square */
game.score -= movecost * game.n_moves + pushcost * game.n_pushes;
if (game.score < 0)
game.score = 0;
return retval;
}
int finished(void) {
if (!compute_score())
return game.finished = 0;
cmd_ShowScore();
return game.finished = 1;
}
xsok-1.02/src/tools.c 100644 144 62 2743 5665071060 13163 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module tools.c */
/* */
/* Miscellaneous utility functions. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
void fatal(const char *msg, ...) {
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
fprintf (stderr, "\n");
exit (1);
}
void *malloc_(size_t n) {
void *p;
if (!n)
return NULL; /* since malloc(0) may return NULL */
p = malloc(n);
if (!p)
fatal("out of memory");
return p;
}
void *calloc_(size_t n, size_t s) {
void *p;
if (!n)
return NULL; /* WATCOM C says "out of memory" in the case n = 0 */
if (!(p = calloc(n, s)))
fatal("out of memory");
return p;
}
void *realloc_(void *p, size_t n) {
if (p == NULL) /* no old block of size > 0 exists */
return malloc_(n);
if (!n) {
free_(p);
return NULL;
}
if (!(p = realloc(p, n)))
fatal("out of memory\n");
return p;
}
void free_(void *p) {
if (p)
free(p);
}
char *strsav(const char *txt) {
char *p = malloc_(1 + strlen(txt));
strcpy(p, txt);
return p;
}
xsok-1.02/src/commands.c 100644 144 62 14274 5665071060 13646 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module commands.c */
/* */
/* Most of the entries for commands assignable to keys */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <sys/types.h>
#include <unistd.h>
#include "xsok.h"
#include "version.h"
void cmd_Up(void) {
playermove(0);
}
void cmd_Left(void) {
playermove(1);
}
void cmd_Down(void) {
playermove(2);
}
void cmd_Right(void) {
playermove(3);
}
void cmd_Repeat(void) {
if (game.n_moves)
playermove(movetab[game.n_moves-1]);
}
void cmd_ShowAuthor(void) {
if (*levelauthor)
show_message("%s", levelauthor);
else
show_message("%s", TXT_NOAUTHOR);
}
void cmd_LevelInfo(void) {
if (*levelcomment) {
if (highscore[game.level])
show_message("%s (Best Score: %d)", levelcomment, highscore[game.level]);
else
show_message("%s (Unsolved level)", levelcomment);
} else
cmd_ShowBestScore();
}
void cmd_NextUnsolved(void) {
while (game.level < maxlevel) {
if (!highscore[++game.level]) {
NewLevel(game.level);
cmd_LevelInfo();
return;
}
}
}
void cmd_NextLevel(void) {
if (numeric_arg)
if (numeric_arg <= maxlevel)
game.level = numeric_arg - 1; /* & fall through */
if (game.level < maxlevel) {
if (game.stored_moves && !game.finished) {
play_sound("giveup");
}
NewLevel(game.level+1);
cmd_LevelInfo();
}
}
void cmd_PrevLevel(void) {
if (game.level > 1) {
if (game.stored_moves && !game.finished) {
play_sound("giveup");
}
NewLevel(game.level-1);
cmd_LevelInfo();
}
}
void rq_LeaveSok(void) {
request_confirm(cmd_LeaveSok, TXT_QUIT_CONFIRM);
}
/* unused, since this can be undone */
void rq_RestartGame(void) {
request_confirm(cmd_RestartGame, TXT_RESTART_CONFIRM);
}
void rq_PrevLevel(void) {
request_confirm(cmd_PrevLevel, TXT_PREV_CONFIRM);
}
void rq_NextLevel(void) {
request_confirm(cmd_NextLevel, TXT_NEXT_CONFIRM);
}
void rq_NextUnsolved(void) {
request_confirm(cmd_NextUnsolved, TXT_UNSOLVED_CONFIRM);
}
void cmd_DropBookmark(void) {
game.bookmark = game.n_moves; /* easy, isn't it? */
show_message(TXT_BOOKMARK_SET);
}
void jumpto_movenr(int move_ptr) {
int remgraphic = gamegraphic;
if (move_ptr == game.n_moves)
return;
/* assert(move_ptr <= game.stored_moves); */
if (remgraphic) /* graphic was on */
graphics_control(Disable);
if (move_ptr < game.n_moves) /* must reset first */
OrgLevel();
while (move_ptr > game.n_moves) {
/* printf("doing move %d of %d (%d)\n", game.n_moves, move_ptr, movetab[game.n_moves]); */
int xx;
xx = game.n_moves;
playermove(movetab[game.n_moves]);
if (xx == game.n_moves)
fatal("Shit, same old bug again!\n");
}
if (remgraphic)
graphics_control(EnableAndRedraw);
}
void cmd_RestartGame(void) {
if (numeric_arg <= game.stored_moves)
jumpto_movenr(numeric_arg);
else
jumpto_movenr(game.stored_moves);
cmd_LevelInfo();
}
void cmd_GotoBookmark(void) {
jumpto_movenr(game.bookmark);
cmd_ShowScore();
}
void cmd_SaveGame(void) {
game.finished = compute_score();
save_game("sv");
}
void cmd_ShowVersion(void) {
show_message(TXT_VERSION, VERSION);
}
void cmd_ShowScore(void) {
compute_score();
show_message(TXT_SCORE, game.n_moves, game.n_pushes, game.score,
obj[game.y][game.x]->power - obj[game.y][game.x]->weight);
}
void cmd_ShowBestScore(void) {
if (highscore[game.level])
show_message(TXT_BEST, highscore[game.level+100],
highscore[game.level+200], highscore[game.level]);
else
show_message(TXT_UNSOLVED);
}
void cmd_ReplayGame(void) {
if (game.n_moves) {
int rem = game.n_moves;
cmd_RestartGame();
sync_and_wait();
sync_and_wait();
do {
cmd_RedoMove();
sync_and_wait();
sync_and_wait();
} while (game.n_moves < rem);
cmd_ShowScore();
}
}
void cmd_LoadGame(void) {
char filename[MAXSAVEFILELEN];
static const char **p, *extensions[] = { "sv", "bs", "mp", "mm", "sav", "sol", NULL };
for (p = extensions; *p; ++p) {
sprintf(filename, "%s/%s.%02d.%s", savedir, game.type, game.level, *p);
if (!access(filename, R_OK)) {
load_game(filename);
return;
}
}
show_message(TXT_NOLOAD);
}
void cmd_StartMacro(void) {
game.macroStart = game.n_moves;
game.macroEnd = game.n_moves;
game.macro_x = game.x;
game.macro_y = game.y;
show_message(TXT_STARTMACRO);
}
void cmd_EndMacro(void) {
if (game.macroStart >= 0) {
game.macroEnd = game.n_moves;
show_message(TXT_ENDMACRO, game.n_moves-game.macroStart);
}
}
void cmd_PlayMacro(void) {
/* show_message("Stard %d, End %d, at (%d,%d)", game.macroStart, game.macroEnd,
game.macro_x, game.macro_y); */
if (game.macroStart >= 0) {
mouse_x = game.macro_x;
mouse_y = game.macro_y;
cmd_MouseMove(); /* sets the before_move variable */
if (game.x == game.macro_x && game.y == game.macro_y) {
int i, base;
base = game.n_moves;
for (i = game.macroStart; i < game.macroEnd;) {
playermove(movetab[i++]);
if (game.n_moves != base + i - game.macroStart)
break; /* invalid move */
}
} else
show_message(TXT_MACRO_BADPOS);
}
}
void cmd_UndoMove(void) {
if (game.n_moves) {
int num = numeric_arg;
if (!num)
num = 1;
if (game.n_moves >= num)
jumpto_movenr(game.n_moves-num);
else
jumpto_movenr(0);
show_message(TXT_UNDO);
if (game.n_moves < game.macroStart || game.n_moves < game.macroEnd)
game.macroStart = -1;
} else if (game.stored_moves) {
jumpto_movenr(game.stored_moves);
show_message(TXT_UNDO);
} else
show_message(TXT_NOUNDO);
}
void cmd_RedoMove(void) {
if (game.n_moves < game.stored_moves) {
int num = numeric_arg;
if (!num)
num = 1;
if (game.n_moves + num <= game.stored_moves)
jumpto_movenr(game.n_moves+num);
else
jumpto_movenr(game.stored_moves);
show_message(TXT_REDO);
} else
show_message(TXT_NOREDO);
}
xsok-1.02/src/X-sok.h 100644 144 62 5254 6122571522 13026 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module X-sok.h */
/* */
/* This file is included by all sources for the X interface. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "xsok.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Sme.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Toggle.h>
/* in X11R3, XSize_t was int, since R4 we seem to have a mixture of int */
/* and unsigned int! (complain!) */
typedef unsigned int XSize_t; /* type used by X for width and height */
/* this is not consistent used by X11R5 */
extern struct graphic {
boolean autolayout; /* automatic new layout at resize events */
XSize_t width; /* the width of the table window */
XSize_t height; /* the height of the table window */
} graphic;
extern Display *dpy;
extern Window table;
extern Widget toplevel;
#define DX 32 /* size of one square. we could even read this from the xpm file */
#define DY 32
/* prototypes. some of them may be in xsok.h already */
/* Xaw-help.c */
#ifdef ONLINE_HELP
void create_help(void);
void popup_help(void);
void popdown_help(Widget, XtPointer, XtPointer);
#endif
/* Xaw-main.c */
void show_message(const char *str, ...);
void SetTitle(void);
void cmd_LeaveSok(void);
void cmd_Confirm(void);
void cmd_Cancel(void);
void request_confirm(void (*)(void), const char *);
#ifdef SOUND
int checksound(void);
#endif
int main(int argc, char *argv[]);
void Force_Resize(XSize_t, XSize_t);
/* X-widget.c */
void AskWidgetForResize(XSize_t, XSize_t);
/* X-events.c */
void refresh_screen(void);
void button_press(XButtonPressedEvent *);
void button_release(XButtonPressedEvent *);
void key_press(XKeyPressedEvent *);
void cmd_Resize(void);
void resize_event(XSize_t, XSize_t);
/* X-gfx.c */
void NewLevel(int);
void init_layout(void);
void init_gfx(const char *);
/* void dotPaint(int, int, int, int); */
void doPaint(int, int, int, int);
void redraw_table(XExposeEvent *);
xsok-1.02/src/move.c 100644 144 62 27271 5665071060 13014 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module move.c */
/* */
/* Detection of valid moves, and effect handling functions. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
#define SAVE_STATE /* variant which should flicker less */
#define SELECTOR_HACK /* ugly hack to allow Cyberbox selectors */
static int dxtab[4] = { 0, -1, 0, 1 };
static int dytab[4] = { -1, 0, 1, 0 };
char *movetab = NULL;
int numalloc = 0;
/* declare the unmovable blocker which prevents race conditions */
static struct objects blocked = { 1, 0, 0, 0, 0, 0, 0, 0 };
static void storemove(int res, int dir) {
if (res > 1)
++game.n_pushes;
if (game.n_moves == numalloc)
movetab = realloc_(movetab, numalloc += 256);
if (game.stored_moves > game.n_moves && movetab[game.n_moves] == dir) {
/* This is a redo, but the user retyped it. Be nice. */
++game.n_moves;
} else {
/* this is a new move: reset number of stored moves (no redo beyond this one) */
if (game.bookmark > game.n_moves)
game.bookmark = game.n_moves;
movetab[game.n_moves++] = dir;
game.stored_moves = game.n_moves; /* reset the redo limit */
}
}
#define DOPAINT(x1,y1,x2,y2) if (gamegraphic) doPaint(x1,y1,x2,y2);
#ifdef SAVE_STATE
static struct walls *savemap[MAXROW][MAXCOL];
static struct objects *saveobj[MAXROW][MAXCOL];
static struct objects saveinstance[MAXINSTANCES];
static void save_state(void) {
int x, y;
gamegraphic = 0;
memcpy(savemap, map, sizeof(map)); /* dereferenced objects are readonly */
memcpy(saveinstance, instance, sizeof(instance));
memcpy(saveobj, obj, sizeof(obj));
for (y = 2; y < game.numrows-2; ++y)
for (x = 2; x < game.numcols-2; ++x)
if (obj[y][x]) {
int i;
i = obj[y][x] - instance;
saveobj[y][x] = saveinstance + i;
}
}
static void update_state(void) {
int x, y;
gamegraphic = 1;
for (y = 2; y < game.numrows-2; ++y)
for (x = 2; x < game.numcols-2; ++x) {
int pica, picb;
pica = obj[y][x] ? obj[y][x]->pic : -1;
picb = saveobj[y][x] ? saveobj[y][x]->pic : -1;
if (pica != picb || map[y][x] != savemap[y][x])
doPaint(x,y, x,y);
#if 0
if (pica != picb) {
printf("At (%2d,%2d): pics %d and %d differ\n", x, y, pica, picb);
}
#endif
}
}
#define SAVE save_state();
#define UPDATE update_state();
#else
#define SAVE gamegraphic = 0;
#define UPDATE { gamegraphic = 1; doPaint(2,2, game.numcols-3,game.numrows-3); }
#endif
void graphics_control(int on) {
switch (on) {
case Disable:
SAVE;
break;
case Enable:
gamegraphic = 1;
break;
case EnableAndRedraw:
UPDATE;
break;
}
}
/* Check if object on square (x,y) has enough power to move in direction dir */
/* If it has, do the move and return 1 or 2 (1 if object moved alone) */
static int do_move(int x, int y, int dir) {
struct objects *ip;
struct walls *wp;
int dx, dy, tx, ty;
int dirbits, power;
#ifdef SELECTOR_HACK
static struct objects *sel_ip = NULL;
struct objects *new_sel_ip;
int forbidden = 0;
int sel_enter = 0, sel_leave = 0;
if (obj[y][x]->pic == 7)
sel_leave = 1;
#endif
dx = dxtab[dir];
dy = dytab[dir];
dirbits = 1 << dir;
tx = x;
ty = y;
wp = map[ty][tx];
ip = obj[ty][tx]; /* what moves? */
power = 0;
do {
if (ip->chr) /* involves object already moved? */
return 0;
if (!(ip->movedir & dirbits)) /* object isn't allowed to go this way */
return 0;
if (ip->pushdir & dirbits) /* does this one help pushing? */
power += ip->power;
power -= ip->weight;
if (power < 0) /* does our power suffice? */
return 0; /* it's not enough to check this at the end of the loop */
/* may we leave from here? */
if (!(wp->leave & dirbits))
return 0; /* nope */
#ifdef SELECTOR_HACK
if (ip->mask & forbidden) { /* may not be pushed by THIS object */
switch (obj[ty-dy][tx-dx]->pic) {
case 0: /* man: */
case 7: /* man in selector */
if (ip->mask == 0x30010) { /* walkable selector */
sel_enter = 1;
new_sel_ip = ip;
goto move_ok;
}
}
return 0;
}
forbidden = (ip->mask & 0xffff) << 16; /* for next object */
#endif
tx += dx;
ty += dy;
wp = map[ty][tx];
if (!(wp->enter & dirbits))
return 0; /* may not enter from here */
if (!(wp->mask & ip->mask))
return 0; /* may not enter square anyway */
ip = obj[ty][tx]; /* next object, if any */
} while (ip);
move_ok:
;
/* yeah, move is OK, do it! */
/* do the move, count blocks moved */
{ int n;
n = 0;
do {
++n;
(obj[ty][tx] = obj[ty-dy][tx-dx])->chr = 1; /* mark object 'moved' */
tx -= dx;
ty -= dy;
} while (tx != x || ty != y);
#ifdef SELECTOR_HACK
if (sel_leave) {
obj[ty][tx] = sel_ip;
obj[ty+dy][tx+dx]->pic = 0; /* normal man again */
sel_ip->chr = 1;
sel_ip = NULL;
} else {
obj[ty][tx] = NULL;
}
if (sel_enter) {
sel_ip = new_sel_ip;
obj[ty+n*dy][tx+n*dx]->pic = 7; /* man in a box */
}
DOPAINT(x, y, x+n*dx, y+n*dy);
if (!sel_leave)
obj[ty][tx] = &blocked;
#else
obj[ty][tx] = NULL;
DOPAINT(x, y, x+n*dx, y+n*dy);
obj[ty][tx] = &blocked;
#endif
return n;
}
}
static int automoves(void) {
int x, y, flag = 0;
for (x = game.numcols-3; x>1; --x)
for (y = game.numrows-3; y>1; --y) {
struct objects *ip;
if ((ip = obj[y][x])) {
if ((ip->pushdir & 1) && do_move(x, y, 0))
flag = 1;
else if ((ip->pushdir & 2) && do_move(x, y, 1))
flag = 1;
}
}
for (x = 2; x < game.numcols-2; ++x)
for (y = 2; y < game.numrows-2; ++y) {
struct objects *ip;
if ((ip = obj[y][x])) {
if ((ip->pushdir & 4) && do_move(x, y, 2))
flag = 1;
else if ((ip->pushdir & 8) && do_move(x, y, 3))
flag = 1;
}
}
return flag; /* turn if at least one object was moved */
}
#define TURN(object, bits) ((((object) << (bits)) & 0x0f) | ((object) >> (4-(bits))))
static void check_effects(void) {
/* check moved objects, if they entered effect squares */
/* if so, apply effect */
/* mark objects unmoved and remove the blockers */
int x, y;
for (x = 2; x < game.numcols-2; ++x)
for (y = 2; y < game.numrows-2; ++y)
if (obj[y][x] && obj[y][x]->chr) {
struct objects *ip;
ip = obj[y][x];
if (ip == &blocked) {
obj[y][x] = NULL; /* square is free now */
if (map[y][x]->effect >= 2*E_ONCE)
/* square will change now to other type */
map[y][x] = walls + (map[y][x]->effect / E_ONCE) - 2;
} else {
int effect;
ip->chr = '\0';
if ((effect = map[y][x]->effect)) {
if (effect > E_ONCE && effect < 2 * E_ONCE)
map[y][x] = walls; /* one-time effect only */
effect %= E_ONCE;
switch (effect) {
case E_TURN_CCW:
case E_TURN_180:
case E_TURN_CW:
/* does a rotation affect the object? */
if ((ip->movedir != 0 && ip->movedir != 0x0f) ||
(ip->pushdir != 0 && ip->pushdir != 0x0f)) {
/* yes, it does. Are there 2 or 4 pictures for this tile? */
if (TURN(ip->movedir, 2) == ip->movedir &&
TURN(ip->pushdir, 2) == ip->pushdir)
/* only two pictures */
ip->pic ^= (effect & 1);
else
/* four pictures */
ip->pic = (ip->pic & ~3) | ((ip->pic + effect) & 3);
ip->movedir = TURN(ip->movedir, effect);
ip->pushdir = TURN(ip->pushdir, effect);
DOPAINT(x, y, x, y);
}
break;
case E_ADDPOWER:
++ip->power;
break;
case E_SUBPOWER:
if (ip->power)
--ip->power;
break;
case E_TELEPORT:
{ int xx, yy;
/* find a matching teleporter */
for (xx = 2; xx < game.numcols-2; ++xx)
for (yy = 2; yy < game.numrows-2; ++yy)
if ((xx != x || yy != y) &&
(!obj[yy][xx] || obj[yy][xx] == &blocked) &&
map[yy][xx]->effect % E_ONCE == E_TELEPORT) {
/* found a matching free teleporter */
obj[yy][xx] = ip;
if (map[yy][xx]->effect == E_TELEPORT+E_ONCE)
map[yy][xx] = walls;
obj[y][x] = NULL;
if (gamegraphic) {
doPaint(xx,yy,xx,yy);
doPaint(x,y,x,y);
}
goto done;
}
}
/* fall through if just a single teleporter, */
/* or no other teleporter is free: ignore! */
done:
break;
}
}
} /* moved object */
} /* any object at all */
}
void playermove(int dir) { /* dir is 0..3 */
int result;
struct objects *ip;
#if 0
{
int x, y;
for (x = 2; x < game.numcols-2; ++x)
for (y = 2; y < game.numrows-2; ++y)
if (obj[y][x] && obj[y][x]->chr) {
printf("unclean object at (%d,%d) of chr %x\n", x, y, obj[y][x]->chr);
obj[y][x]->chr = '\0';
}
}
#endif
(ip = obj[game.y][game.x])->pushdir = 1 << dir;
#ifdef SELECTOR_HACK
if (ip->pic == 7) /* no one may push from behind */
result = do_move(game.x, game.y, dir);
else
#endif
{
int n = 0;
/* check if there are blocks behind us that help pushing */
/* stupid compiler, result IS initialized! */
do
++n;
while (obj[game.y-n*dytab[dir]][game.x-n*dxtab[dir]]);
do
--n;
while (!(result = do_move(game.x-n*dxtab[dir], game.y-n*dytab[dir], dir)) && n);
if (result && result-1 < n)
fatal("internal error\n");
}
ip->pushdir = 0; /* this must be reset BEFORE check_effects! */
if (result) {
int x, y;
storemove(result, dir);
do {
if (gamegraphic)
sync_and_wait();
/* add sound here too! */
check_effects();
} while (automoves());
/* player may get pushed around: search him! */
game.x = game.y = 0;
for (x = 2; x < game.numcols-2; ++x)
for (y = 2; y < game.numrows-2; ++y)
if (obj[y][x] == ip) {
game.y = y;
game.x = x;
break;
}
/* fatal("Ooops, player has vanished!\n"); */
if (finished()) {
int update = 0;
cmd_ReadHighscores(); /* read the actual values! TODO: lock the highscore file */
if (highscore[game.level] < game.score) {
highscore[game.level] = game.score;
update = 1;
}
if (highscore[game.level+100] > game.n_moves) {
highscore[game.level+100] = game.n_moves;
update |= 2;
}
if (highscore[game.level+200] > game.n_pushes) {
highscore[game.level+200] = game.n_pushes;
update |= 4;
}
if (update) {
const char *firstfile = NULL;
WriteHighscores();
#if 0
if (highscore[game.level] == game.score)
save_game("sol"); /* optimum score */
else if (highscore[game.level+200] == game.n_pushes)
save_game("mp"); /* minimal pushes */
else if (highscore[game.level+100] == game.n_moves)
save_game("mm"); /* minimal moves */
/* TODO: should remove obsolete files and rename others. That's hard! */
#else
/* possibly save multiple files */
if (highscore[game.level] == game.score)
save_game(firstfile = "bs"); /* best score */
if (highscore[game.level+200] == game.n_pushes)
firstfile ? link_game(firstfile, "mp") :
save_game(firstfile = "mp"); /* minimal pushes */
if (highscore[game.level+100] == game.n_moves)
firstfile ? link_game(firstfile, "mm") :
save_game(firstfile = "mm"); /* minimal moves */
#endif
show_message(TXT_NEWHIGH, game.score);
return;
}
/* TODO: release the highscore file lock */
show_message(TXT_YOU_WIN, game.score, highscore[game.level]);
return;
} /* if (finished()) */
cmd_ShowScore();
} else
if (!game.finished) /* hack to avoid complains on excess moves at the end */
show_message(TXT_MOVENOTPOSSIBLE);
}
xsok-1.02/src/parse.c 100644 144 62 16647 5665071060 13165 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module parse.c */
/* */
/* Parsing of the level subset definition files and level files. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
#define MAXABBREVS 32
static char abbrevs[MAXABBREVS][4];
static int numabbrevs;
int nwalls, nobjects, ninstances;
int pushcost, movecost;
const char *xsokdir;
struct objects objects[MAXOBJECTS];
struct walls walls[MAXWALLS]; /* wall types */
struct game game;
struct walls *map[MAXROW][MAXCOL];
struct objects *obj[MAXROW][MAXCOL];
struct objects instance[MAXINSTANCES];
static struct game init = { 1, 1, -1, -1, 0, 0, 0 };
static struct walls the_void = { '\0', 16, 0,0,0,0 };
static struct walls *omap[MAXROW][MAXCOL];
static struct objects *oobj[MAXROW][MAXCOL];
static struct objects oinstance[MAXINSTANCES];
static struct game ogame;
/* restore the game state for initial tableau. No graphics update */
void OrgLevel(void) {
memcpy(map, omap, sizeof(map));
memcpy(obj, oobj, sizeof(obj));
memcpy(instance, oinstance, sizeof(instance));
ogame.macroStart = game.macroStart;
ogame.macroEnd = game.macroEnd;
ogame.macro_x = game.macro_x;
ogame.macro_y = game.macro_y;
ogame.bookmark = game.bookmark;
ogame.stored_moves = game.stored_moves;
game = ogame;
}
int maxlevel;
char levelcomment[100];
char levelauthor[100];
static int piped;
/* open the level database and read, starting at a given line */
static FILE *def_open(const char *firstline) {
FILE *fp;
char s[MAXXSOKDIRLEN+14];
char shorttype[8];
strcpy(shorttype, game.type);
shorttype[7] = '\0';
sprintf(s, "%s/%s.def", xsokdir, shorttype);
if (!(fp = fopen(s, "r"))) {
if (!(fp = zreadopen(s))) /* uncompress on-the-fly */
fatal("Cannot open definition file for %s\n", game.type);
piped = 1;
}
while (fgets(s, sizeof(s), fp))
if (!strcmp(s, firstline))
return fp;
fatal("No matching line found\n");
/* this end is not reached */
return fp; /* but keep the compiler happy (volatile functions are not ANSI) */
}
void ParseDefinitionFile(void) {
FILE *fp;
int mode = 0;
char s[MAXXSOKDIRLEN+22];
piped = 0;
maxlevel = 99;
pushcost = 10;
movecost = 1;
sprintf(s, "%s/%s/definitions", xsokdir, game.type);
if (!(fp = fopen(s, "r")))
fp = def_open(";WALLS\n");
numabbrevs = nwalls = nobjects = 0;
while (fgets(s, sizeof(s), fp)) {
int len;
len = strlen(s);
if (len && s[len-1] == '\n')
s[--len] = '\0';
if (!strcmp(s, ";WALLS")) {
mode = 0;
} else if (!strcmp(s, ";OBJECTS")) {
mode = 1;
} else if (!strncmp(s, ";MAXLEVEL", 9)) {
maxlevel = atoi(s+9);
if (maxlevel < 1 || maxlevel > 99)
maxlevel = 99;
} else if (!strncmp(s, ";PUSHCOST", 9)) {
pushcost = atoi(s+9);
} else if (!strncmp(s, ";MOVECOST", 9)) {
movecost = atoi(s+9);
} else if (!strncmp(s, ";ATOP ", 6)) {
if (numabbrevs == MAXABBREVS)
fatal("Too many abbrevs!\n");
strncpy(abbrevs[numabbrevs++], s+6, 3);
} else if (!strncmp(s, ";LEVEL", 6))
break;
if (!len || *s == ';')
continue; /* comment or empty line */
switch (mode) {
case 0:
{ struct walls *wp;
if (nwalls == MAXWALLS)
fatal("Too many wall types\n");
wp = walls + nwalls++;
wp->chr = *s;
if (sscanf(s+1, "%x %x %x %x %d", &wp->pic, &wp->enter,
&wp->leave, &wp->mask, &wp->effect) != 5)
fatal("Bad line for character '%c':\n%s\n",
wp->chr, s);
}
break;
case 1:
{ struct objects *wp;
if (nobjects == MAXOBJECTS)
fatal("Too many object types\n");
wp = objects + nobjects++;
wp->chr = *s;
if (sscanf(s+1, "%x %x %x %d %d %x %d", &wp->pic, &wp->movedir,
&wp->pushdir, &wp->weight, &wp->power, &wp->mask,
&wp->score) != 7)
fatal("Bad line for character '%c':\n%s\n",
wp->chr, s);
}
break;
default:
;
}
}
if (piped)
zreadclose(fp);
else
fclose(fp);
if (!nwalls || !nobjects)
fatal("Definition file is empty\n");
}
static void dfs(int x, int y) {
if (x < 0 || y < 0 || x >= game.numcols || y >= game.numrows)
return;
if (map[y][x] != walls)
return;
map[y][x] = &the_void;
dfs(x-1, y);
dfs(x, y-1);
dfs(x+1, y);
dfs(x, y+1);
}
/* read current level of current game.type */
void ParseMapFile(void) {
FILE *fp;
int x, y;
struct objects *ip;
char s[MAXXSOKDIRLEN+20];
piped = 0;
sprintf(s, "%s/%s/screen.%02d", xsokdir, game.type, game.level);
if (!(fp = fopen(s, "r"))) {
sprintf(s, ";LEVEL %d\n", game.level);
fp = def_open(s);
}
ninstances = 0;
for (x = 0; x < MAXCOL; ++x)
for (y = 0; y < MAXROW; ++y) {
map[y][x] = walls;
obj[y][x] = NULL;
}
init.level = game.level;
init.type = game.type;
*levelcomment = '\0';
*levelauthor = '\0';
game = init;
ip = instance;
for (y = 1; fgets(s, sizeof(s), fp); ++y) {
int c, cc;
/* parse s to map */
if (*s == ';') {
if (!strncmp(s+1, "LEVEL", 5))
break;
--y; /* line doesn't count */
if (!strncmp(s+1, "COMMENT ", 8)) {
strncpy(levelcomment, s+9, sizeof(levelcomment)-1);
levelcomment[sizeof(levelcomment)-1] = '\0';
if (strchr(levelcomment, '\n'))
*strchr(levelcomment, '\n') = '\0';
}
if (!strncmp(s+1, "AUTHOR ", 7)) {
strncpy(levelauthor, s+8, sizeof(levelauthor)-1);
levelauthor[sizeof(levelauthor)-1] = '\0';
if (strchr(levelauthor, '\n'))
*strchr(levelauthor, '\n') = '\0';
}
continue; /* skip this line (extra comment) */
}
if (y == MAXROW-1)
fatal("Level is too big\n");
for (x = 1; (c=s[x-1]) && c != '\n'; ++x) {
struct objects *op;
struct walls *wp;
int n;
if (x == MAXCOL-1)
fatal("Level is too wide\n");
if (x > game.numcols)
game.numcols = x;
/* check, if c is an abbrev */
cc = walls[0].chr; /* make standard floor below (should be space) */
for (n = 0; n < numabbrevs; ++n)
if (abbrevs[n][0] == c) {
c = abbrevs[n][1];
cc = abbrevs[n][2];
}
for (op = objects, n = nobjects; n; --n, ++op)
if (op->chr == c) { /* object found */
if (op == objects) { /* player himself */
if (game.x >= 0)
fatal("Multiple player positions\n");
game.x = x;
game.y = y;
}
if (ninstances++ == MAXINSTANCES)
fatal("Too many objects\n");
*ip = *op; /* copy object */
ip->chr = '\0'; /* mark unmoved object */
obj[y][x] = ip++;
c = cc; /* floor type */
break;
}
for (wp = walls, n = nwalls; n; --n, ++wp)
if (wp->chr == c) { /* wall found */
map[y][x] = wp;
break;
}
if (!n)
fatal("No wall type found for character '%c'\n", c);
}
}
if (piped)
zreadclose(fp);
else
fclose(fp);
game.numrows = y+1;
game.numcols += 2;
/* change floor to void */
dfs(0, 0);
/* save to orglevel for easy restart */
memcpy(omap, map, sizeof(map));
memcpy(oobj, obj, sizeof(obj));
memcpy(oinstance, instance, sizeof(instance));
game.macroStart = -1; /* no macro available */
ogame = game;
cmd_ReadHighscores();
lastcmd = NULL;
}
xsok-1.02/src/X-widget.c 100644 144 62 15337 6122571155 13535 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module X-widget.c */
/* */
/* The tableau widget for Xsok. */
/* written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "X-sok.h"
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "TableauP.h"
static XtResource resources[] = {
#define offset(field) XtOffsetOf(TableauRec, tableau.field)
/* {name, class, type, size, offset, default_type, default_addr}, */
{XtNruleset, XtCRuleset, XtRString, sizeof(String), offset(rules), XtRString, "Sokoban"},
{XtNlevel, XtCLevel, XtRInt, sizeof(int), offset(level), XtRImmediate, (XtPointer)1},
{XtNusername,XtCUsername,XtRString, sizeof(String), offset(username), XtRString, NULL},
{XtNxsokdir, XtCXsokdir, XtRString, sizeof(String), offset(xsokdir), XtRString, XSOKDIR},
{XtNxpmdir, XtCXpmdir, XtRString, sizeof(String), offset(xpmdir), XtRString, NULL},
{XtNsavedir, XtCSavedir, XtRString, sizeof(String), offset(savedir), XtRString, XSOKSAVE },
{XtNmessageFile,XtCMessageFile,XtRString,sizeof(String), offset(messageFile), XtRString, "messages"},
{XtNkeyboardFile,XtCKeyboardFile,XtRString,sizeof(String),offset(keyboardFile),XtRString, "keys"},
#undef offset
};
/* actions on the desktop area */
/*ARGSUSED*/
static void TableauKey(Widget w, XEvent *xev, String *s, Cardinal *num) {
key_press((XKeyPressedEvent *)xev);
}
/*ARGSUSED*/
static void TableauBtn_down(Widget w, XEvent *xev, String *s, Cardinal *num) {
button_press((XButtonPressedEvent *)xev);
}
/*ARGSUSED*/
static void TableauBtn_up(Widget w, XEvent *xev, String *s, Cardinal *num) {
button_release((XButtonPressedEvent *)xev);
}
/*ARGSUSED*/
static void Redisplay(Widget w, XEvent *xev, Region region) {
redraw_table((XExposeEvent *)xev);
}
static XtActionsRec actions[] = {
/* {name, procedure}, */
{ "tableau_k", TableauKey },
{ "tableau_d", TableauBtn_down },
{ "tableau_u", TableauBtn_up },
};
static char translations[] = "\
<Key>: tableau_k() \n\
<BtnDown>: tableau_d() \n\
<BtnUp>: tableau_u() \n\
";
static Boolean SetValues(Widget current, Widget request, Widget desired,
ArgList args, Cardinal *num_args) {
return FALSE;
}
static void Initialize(Widget request, Widget xnew, ArgList args, Cardinal *n);
static void Resize(Widget gw);
static void Realize(Widget w, XtValueMask *valuemask, XSetWindowAttributes *winattr) {
*valuemask |= CWEventMask | CWBackingStore | CWBitGravity;
winattr->backing_store = WhenMapped;
winattr->bit_gravity = NorthWestGravity;
winattr->event_mask = KeyPressMask | ExposureMask | ButtonPressMask |
ButtonReleaseMask | StructureNotifyMask | Button3MotionMask;
(*(tableauClassRec.core_class.superclass)->core_class.realize)(w, valuemask, winattr);
}
TableauClassRec tableauClassRec = {
{ /* core fields */
/* superclass */ (WidgetClass) &widgetClassRec,
/* class_name */ "Tableau",
/* widget_size */ sizeof(TableauRec),
/* class_initialize */ NULL,
/* class_part_initialize */ NULL,
/* class_inited */ FALSE,
/* initialize */ Initialize,
/* initialize_hook */ NULL,
/* realize */ Realize,
/* actions */ actions,
/* num_actions */ XtNumber(actions),
/* resources */ resources,
/* num_resources */ XtNumber(resources),
/* xrm_class */ NULLQUARK,
/* compress_motion */ TRUE,
/* compress_exposure */ TRUE,
/* compress_enterleave */ TRUE,
/* visible_interest */ FALSE,
/* destroy */ NULL,
/* resize */ Resize,
/* expose */ Redisplay,
/* set_values */ SetValues,
/* set_values_hook */ NULL,
/* set_values_almost */ XtInheritSetValuesAlmost,
/* get_values_hook */ NULL,
/* accept_focus */ NULL,
/* version */ XtVersion,
/* callback_private */ NULL,
/* tm_table */ translations,
/* query_geometry */ XtInheritQueryGeometry,
/* display_accelerator */ XtInheritDisplayAccelerator,
/* extension */ NULL
},
{ /* tableau fields */
/* empty */ 0
}
};
WidgetClass tableauWidgetClass = (WidgetClass)&tableauClassRec;
static Widget toplev = NULL;
static void Resize(Widget gw) {
TableauWidget w = (TableauWidget) gw;
/* printf("Parent wants me to have size %d,%d\n",
w->core.width, w->core.height); */
resize_event(w->core.width, w->core.height);
/* (*pileWidgetClass->core_class.superclass->core_class.resize)(gw); */
}
void AskWidgetForResize(XSize_t x, XSize_t y) {
XtWidgetGeometry Geo;
XtGeometryResult r;
Geo.width = x;
Geo.height = y;
do {
Geo.request_mode = CWWidth | CWHeight;
#ifdef LABER
printf("resize to %d %d yielded ", Geo.width, Geo.height);
#endif
r = XtMakeGeometryRequest(toplev, &Geo, &Geo);
#ifdef LABER
switch (r) {
case XtGeometryYes: printf("YES!\n");break;
case XtGeometryNo: printf("NO!\n"); break;
case XtGeometryAlmost:printf("Almost!\n"); break;
case XtGeometryDone: printf("Done!\n"); break;
}
#endif
} while (r == XtGeometryAlmost);
}
const char *keyfilename;
static void Initialize(Widget request, Widget xnew, ArgList args, Cardinal *n) {
static int is_inited = 0;
TableauWidget new = (TableauWidget)xnew;
TableauPart *p = &new->tableau;
toplev = xnew;
if (is_inited) {
fprintf(stderr, "Sorry, currently only one instance of Tableau is allowed\n");
exit(EXIT_FAILURE);
}
xsokdir = p->xsokdir;
savedir = p->savedir;
setlangdir();
if (!p->xpmdir)
p->xpmdir = p->xsokdir;
if (strlen(savedir) > MAXSAVEFILELEN-16 ||
strlen(xsokdir) > MAXXSOKDIRLEN ||
strlen(p->xpmdir) > MAXXSOKDIRLEN) {
fprintf(stderr, "directory too long\n");
exit(1);
}
if (strlen(p->keyboardFile) > MAXFILENAMELEN ||
strlen(p->messageFile) > MAXFILENAMELEN) {
fprintf(stderr, "filename too long\n");
exit(1);
}
keyfilename = p->keyboardFile;
read_message_file(p->messageFile);
read_keyboard_file(p->keyboardFile);
/* assign global data for old Xlib program */
dpy = XtDisplay(new);
buildusername(p->username);
/* gamegraphic = 1; */
change_rules(p->rules);
NewLevel(p->level);
init_gfx(p->xpmdir); /* make GCs, read xpm files */
init_layout();
#ifdef LABER
printf("Init widget: res %d grap %d\n", new->core.width, graphic.width);
#endif
if (new->core.width < graphic.width)
new->core.width = graphic.width;
if (new->core.height < graphic.height)
new->core.height = graphic.height;
}
xsok-1.02/src/Imakefile 100644 144 62 13604 6122776563 13517 0 ustar mbi mathopt # Imakefile for xsok
#
# ****************************************************************************
# customizing:
#
# 0.) extra warnings for gcc and development
#undef EXTRA_WARNINGS
# 1.) specify, if you want online help
# affected file(s): Xaw-main.c, Xaw-help.c
HELP_OPTION = -DONLINE_HELP
# 2.) specify, if you want sound (then, SOUNDOBJ should be one module of
# X-sound_*.o, else empty)
# Note: for X-sound_SUN.o, /dev/audio must have 666 permissions
# affected file(s): (several, you should recompile completely)
# note: sound isn't too great at the moment, we better leave it off.
SOUND_OPTION = # -DSOUND
SOUNDOBJ = # X-sound_SUN.o
# 3.) there is a function popen() in sys-V / X/OPEN which opens a pipe.
# Define -DHAVE_POPEN if you want to use this function for decompressing
# level files. Do not define it if you want to use my emulation, which
# does only require a standard POSIX.1 system and is faster, since it does
# not spawn a shell.
# Attention! In both cases, you need a binary of gunzip in your PATH.
# affected file(s): xfopen.c
PIPE_DEFINE = # -DHAVE_POPEN
# 4.) There is a function gethostbyname() in BSD systems, which will extract
# the domain of your host from the /etc/hosts database. The definition of the
# relevant structure is in the system include file netdb.h.
# This file is availaible on Linux, SUN-OS, HP-UX 9. (But it is not
# part of the POSIX standard.)
# Remove this define, if you do not have this function.
# affected file(s): username.c
NET_DEFINE = -DBSD_NETKIT
# 5.) For the replay option, we need the possibility to sleep for a short
# period of time. This can be done by the usleep() function.
# Remove this, if you need usleep() emulation code.
# affected file(s): X-gfx.c
SLEEP_DEFINE = -DHAVE_USLEEP
# BINDIR and LIBDIR should be predefined by the templates
# BINDIR = /usr/bin/X11
# LIBDIR = /usr/lib/X11
XSOKLIBDIR = /usr/games/lib/xsok
APPDEFSDIR = $(LIBDIR)
XSOKMANDIR = /usr/man/man6
XSOKDOCDIR = /usr/doc/xsok
# This is the name of the save directory, where solved games are stored:
# an alternative path would be $(XSOKLIBDIR)/save
# The directory XSOKSAVEDIR must have permissions rwx for world, else
# xsok must be installed suid, and XSOKDIR must have write permissions for
# the owner of xsok
XSOKSAVEDIR = /var/games/xsok
# paths for installation in user's home-directory.
LXSOKBINDIR = $(HOME)/bin
LXSOKMANDIR = $(HOME)/xsok
LAPPDEFSDIR = $(HOME)
# xsok needs to know where the xpm library resides
XPMLIB = -L$(USRLIBDIR) -lXpm
XPMINCLUDE = -I $(INCDIR)
# *****************************************************************************
# I hope you don't need to change anything below this point
# *****************************************************************************
#if defined(HPArchitecture) || defined(AIXArchitecture)
CC = c89
CCOPTIONS =
#else
CC = gcc
#ifdef EXTRA_WARNINGS
CCOPTIONS = -O2 -pipe -ansi -fno-common -Wall -Wshadow -Wpointer-arith \
-Wcast-qual -Wcast-align -Waggregate-return \
-Wstrict-prototypes -Wmissing-prototypes \
-Wnested-externs -Wwrite-strings
#else
CCOPTIONS = -O2 -pipe -ansi -Wall -fno-common
#endif
#endif
# Xaw interface (the only one currently available)
KIT_LIBS = XawClientLibs
KIT_OBJS = Xaw-main.o Xaw-help.o
DEPLIBS = XawClientDepLibs
LOCAL_LIBRARIES = $(XPMLIB) $(KIT_LIBS)
VER=1.02
XOBJS = X-events.o X-gfx.o X-widget.o $(SOUNDOBJ)
STDOBJS = messages.o commands.o score.o parse.o tools.o move.o \
loadsave.o username.o xfopen.o mousemove.o
OBJS = $(STDOBJS) $(XOBJS) $(KIT_OBJS)
MYPROG = xsok
ALLTARGETS = username combine showscore mergescores $(MYPROG)
DEFINES = $(HELP_OPTION) $(SOUND_OPTION) $(XPMINCLUDE) $(NET_DEFINE) \
$(PIPE_DEFINE) -DXSOKDIR=\"$(XSOKLIBDIR)\" -DXSOKSAVE=\"$(XSOKSAVEDIR)\" \
$(SLEEP_DEFINE)
# Dependencies:
# *.c require version.h xsok.h
# X*.c additionally require X-sok.h and Tableau.h
# X-widget.c additionally requires TableauP.h
LIBCONTS = *.gz *.help keys
all:: $(ALLTARGETS)
clean::
rm -f $(ALLTARGETS)
testname:
@echo
@echo "The name used for highscores you break is"
@./username
@echo "If you do not like this, please set the Tableau.username \
resource explicitly."
@echo
username: username.c
$(CC) $(NET_DEFINE) -DTESTING -s -o username username.c
combine: combine.c
$(CC) -s -o combine combine.c
# the install targets require that make has been run in the lib directory
install:: $(MYPROGS)
(umask 022 && mkdirhier $(XSOKLIBDIR))
# chmod 755 $(XSOKLIBDIR)
(umask 022 && mkdirhier $(XSOKSAVEDIR))
chmod 777 $(XSOKSAVEDIR)
(cd ../lib; tar cf - $(LIBCONTS) | (cd $(XSOKLIBDIR); tar xf -))
chown -R root $(XSOKLIBDIR) $(XSOKSAVEDIR)
chmod -R a+r $(XSOKLIBDIR)
(cd ../lib && ../src/mergescores $(XSOKSAVEDIR)/Xsok.score \
$(XSOKSAVEDIR)/Sokoban.score $(XSOKSAVEDIR)/Cyberbox.score)
chmod 666 $(XSOKSAVEDIR)/[A-z]*.score
(umask 022 && mkdirhier $(XSOKDOCDIR))
if [ -r ../doc/xsok.dvi ]; then cp ../doc/xsok.dvi $(XSOKDOCDIR); fi
cp ../doc/cyberbox.doc $(XSOKDOCDIR)
cp ../etc/COPYRIGHT* $(XSOKDOCDIR)
chmod -R a+r $(XSOKDOCDIR)
mkdirhier $(XSOKMANDIR)
cp xsok.man $(XSOKMANDIR)/xsok.6x
chmod 644 $(XSOKMANDIR)/xsok.6x
ComplexProgramTarget($(MYPROG))
InstallAppDefaults(XSok)
install.fsstnd:
$(MAKE) install
mkdirhier /usr/games/bin
chmod 755 /usr/games/bin
mv $(BINDIR)/xsok /usr/games/bin
install.local: $(MYPROGS)
mkdirhier $(LXSOKBINDIR) $(LXSOKMANDIR) $(XSOKLIBDIR) $(XSOKSAVEDIR) \
$(LAPPDEFSDIR)/app-defaults
chmod 755 $(XSOKLIBDIR) $(LXSOKMANDIR) $(LAPPDEFSDIR)/app-defaults
chmod 777 $(XSOKSAVEDIR)
cp XSok.ad $(LAPPDEFSDIR)/app-defaults/XSok
chmod 755 $(LAPPDEFSDIR)/app-defaults/XSok
(cd ../lib; tar cf - $(LIBCONTS) | (cd $(XSOKLIBDIR); tar xf -))
chmod -R a+r $(XSOKLIBDIR)
cp xsok $(LXSOKBINDIR)
chmod 755 $(LXSOKBINDIR)/xsok
cp xsok.man $(LXSOKMANDIR)/xsok.6x
chmod 644 $(LXSOKMANDIR)/xsok.6x
(cd ../lib && ../src/mergescores $(XSOKSAVEDIR)/Xsok.score \
$(XSOKSAVEDIR)/Sokoban.score $(XSOKSAVEDIR)/Cyberbox.score)
chmod 666 $(XSOKSAVEDIR)/[A-z]*.score
xsok-1.02/src/Xaw-main.c 100644 144 62 27717 6037770724 13543 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module Xaw-main.c */
/* */
/* main function for the Athena Widget interface. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#include "X-sok.h"
#include "Tableau.h"
#include "version.h"
Widget toplevel;
static XtAppContext app_con;
static void (*execfunc)(void) = NULL;
static Widget messagebox, container, desktop;
static Widget dialog, popup, paned;
static Window mainwindow;
void show_message(const char *str, ...) {
if (gamegraphic) {
static char last_message[256];
Arg Args;
va_list args;
va_start(args, str);
if (!str) {
memset(last_message, ' ', sizeof(last_message)-1);
last_message[sizeof(last_message)-1] = '\0';
} else
vsprintf(last_message, str, args);
XtSetArg(Args, XtNlabel, last_message);
XtSetValues(messagebox, &Args, 1);
}
}
void SetTitle(void) {
if (XtWindow(toplevel)) {
static char windowname[48];
sprintf(windowname, "%s - Level %d", game.type, game.level);
/* printf("SetTitle(): toplevel = %p, window = %x\n",
toplev, XtWindow(toplevel)); */
XStoreName(dpy, XtWindow(toplevel), windowname);
}
}
void cmd_LeaveSok(void) {
play_sound("goodbye");
XtDestroyApplicationContext(app_con);
exit(0);
}
static void perform_command(Widget widget, XtPointer client_data, XtPointer call_data) {
(*(void (*)(void))client_data)(); /* any questions? */
}
static void popup_confirm(const char *prompt) {
Arg args[2];
Position x, y;
Dimension xx, yy, wx, wy;
XtSetArg(args[0], XtNlabel, prompt);
XtSetValues(dialog, args, 1);
XtVaGetValues(dialog, XtNwidth, &xx, XtNheight, &yy, NULL);
XtVaGetValues(toplevel, XtNwidth, &wx, XtNheight, &wy, NULL);
x = (wx - xx)/2;
y = (wy - yy)/2;
XtTranslateCoords(toplevel, x, y, &x, &y);
XtSetArg(args[0], XtNx, x);
XtSetArg(args[1], XtNy, y);
XtSetValues(popup, args, 2);
XtPopup(popup, XtGrabNone);
}
void cmd_Confirm(void) {
if (execfunc) {
void (*execfunc2)(void) = execfunc; /* erase it first! */
execfunc = NULL;
XtPopdown(popup);
(*execfunc2)(); /* finally execute the desired function */
}
}
void cmd_Cancel(void) {
if (execfunc) {
execfunc = NULL;
XtPopdown(popup);
}
}
/* type converter functions: */
static void mXtAP_Cancel (Widget w, XEvent *xev, String *params, Cardinal *num) { cmd_Cancel(); }
static void mXtAP_Confirm(Widget w, XEvent *xev, String *params, Cardinal *num) { cmd_Confirm(); }
static void Cancel(Widget widget, XtPointer client_data, XtPointer call_data) { cmd_Cancel(); }
static void Ok(Widget widget, XtPointer client_data, XtPointer call_data) { cmd_Confirm(); }
void request_confirm(void (*dofunc)(void), const char *prompt) {
if (execfunc)
return; /* request pending => deny another one */
if (game.finished || !game.n_moves) {
(*dofunc)(); /* perform action without confirmation */
return;
}
execfunc = dofunc;
popup_confirm(prompt);
}
static String fallback_resources[] = {
"*beNiceToColormap: false",
"*shapeStyle: Rectangle",
"*topShadowContrast: 20",
"*bottomShadowContrast: 40",
"*Scrollbar*background: Grey70",
"*Background: grey85",
"*Foreground: black",
"*resizeToPreferred: True",
"*input: True",
"*showGrip: off",
"*shadowWidth: 2",
"*messages.justify: Left",
"*upperbox.orientation: XtorientHorizontal",
"*lowerbox.orientation: XtorientHorizontal",
"*Tableau.backingStore: WhenMapped",
"*Tableau.keyboardFile: keys",
"*Tableau.messageFile: messages",
"*Tableau.background: black",
"*Tableau.rules: Sokoban",
"*Viewport.allowHoriz: False", /* scrollbar disable! */
"*Viewport.allowVert: False", /* code is buggy! */
"*Viewport.forceBars: False",
"*Viewport.useBottom: True",
"*Viewport.useRight: True",
"*Label.shadowWidth: 0",
"*Label.BorderWidth: 2",
"*Dialog*Translations: #override \n<Key>y: Ok()\n<Key>n: Cancel()\n",
"XSok*title: XSok",
"XSok.prompt.allowShellResize: True",
"XSok.prompt.saveUnder: True",
"*Dialog*resizable: True",
"*Hint.Translations: #override\n<BtnDown>:set()\n<BtnUp>:HintNotify()unset()\n",
"*Sound.state: True",
"XSok.help.width: 403",
"XSok.help.height: 200",
"XSok.help.title: XSok Help Window",
"XSok.help.saveUnder: True",
"XSok*Close Help.fromHoriz: Topic",
"*helptext*string: Please choose a topic.",
"*helptext*displayCaret: False",
"*helptext*scrollHorizontal: whenNeeded",
"*helptext*scrollVertical: whenNeeded",
"*helptext*editType: read",
NULL,
};
static XrmOptionDescRec options[] = {
/* tableau resources */
{ "-rules", "*Tableau.rules", XrmoptionSepArg, NULL },
{ "-level", "*Tableau.level", XrmoptionSepArg, NULL },
{ "-username", "*Tableau.username", XrmoptionSepArg, NULL },
{ "-xsokdir", "*Tableau.xsokdir", XrmoptionSepArg, NULL },
{ "-xpmdir", "*Tableau.xpmdir", XrmoptionSepArg, NULL },
{ "-savedir", "*Tableau.savedir", XrmoptionSepArg, NULL },
{ "-messageFile", "*Tableau.messageFile", XrmoptionSepArg, NULL },
{ "-keyboardFile", "*Tableau.keyboardFile",XrmoptionSepArg, NULL },
/* non-tableau resources */
#ifdef SOUND
{ "-sound", "*Sound.state", XrmoptionNoArg, (XtPointer)"True" },
{ "-nosound", "*Sound.state", XrmoptionNoArg, (XtPointer)"False" },
#endif
};
static XtActionsRec moreActions[] = {
{ "Cancel", mXtAP_Cancel },
{ "Ok", mXtAP_Confirm }
};
static void process_extra_args(int argc, char *argv[]) {
/* check extra args */
if (argc >= 2) {
fprintf(stderr, "xsok: invalid argument: %s\n", argv[1]);
fprintf(stderr, "usage: xsok [options]\n"
"options are all standard X11 toolkit options and\n"
"-rules (ruleset) to initially use specified rules\n"
"-level (levelnr) to set the startlevel\n"
"-username (username) to set individual username for highscores\n"
"-xsokdir (dir) to set the game directory\n"
"-xpmdir (dir) to set the directory for xpm files\n"
"-savedir (dir) to set the directory for saved games\n"
"-messageFile (file) to set the message file name\n"
"-keyboardFile (file) define the keyboard assignment\n"
#ifdef SOUND
"-sound sound toggle on\n"
"-nosound sound toggle off\n"
#endif
);
exit(EXIT_FAILURE);
}
}
const char *rulepool[16] = { "Xsok", "Sokoban", "Cyberbox", NULL };
static void selectrules(Widget w, XtPointer number, XtPointer garbage) {
const char *s = XtName(w);
/* printf("widget %s has been selected\n", s); */
change_rules(s);
NewLevel(1);
cmd_LevelInfo();
}
#ifdef SOUND
static Widget sound;
int checksound(void) {
Boolean retval;
Arg args[1];
XtSetArg(args[0], XtNstate, &retval);
XtGetValues(sound, args, 1);
return retval & 0xff;
}
#endif
static void read_gametypes(void) {
char filename[256], s[80];
FILE *fp;
sprintf(filename, "%s/gametypes", xsokdir);
if ((fp = fopen(filename, "r"))) {
int i;
for (i = 3; i < 15; ++i) {
if (!fgets(s, sizeof(s), fp))
break;
if (*s == ';') {
--i;
continue;
}
if (strchr(s, '\n'))
*strchr(s, '\n') = '\0';
if (strlen(s) > 8)
s[8] = '\0';
rulepool[i] = strsav(s);
}
rulepool[i] = NULL;
fclose(fp);
} /* else keep default pool */
}
int main(int argc, char *argv[]) {
Widget buttonpanel;
Widget gamebutton, gamemenu, rulesbutton, rulesmenu;
int i;
struct button {
const char *name; void (*func)(void);
} *bp;
static struct button buttons[] = {
{ "Undo", cmd_UndoMove },
{ "Redo", cmd_RedoMove },
{ "Next Level", cmd_NextLevel },
{ "Score", cmd_ShowScore },
#ifdef ONLINE_HELP
{ "Help", popup_help },
#endif
{ "Save", cmd_SaveGame },
{ "Load", cmd_LoadGame }
}, mbuttons[] = {
{ "Drop Bookmark", cmd_DropBookmark },
{ "Goto Bookmark", cmd_GotoBookmark },
{ "Replay", cmd_ReplayGame },
{ "Restart", cmd_RestartGame },
{ "Next Level", cmd_NextLevel },
{ "Previous Level", cmd_PrevLevel },
{ "Quit", rq_LeaveSok }
};
/* use the command line arguments concerning the widgets */
switch_uid(1);
toplevel = XtAppInitialize(&app_con, "XSok", options, XtNumber(options),
&argc, argv, fallback_resources, NULL, 0);
switch_uid(0);
process_extra_args(argc, argv);
XtAppAddActions(app_con, moreActions, XtNumber(moreActions));
/* basic elements */
paned = XtCreateManagedWidget("paned", panedWidgetClass, toplevel, NULL, 0);
buttonpanel = XtCreateManagedWidget("buttonpanel", boxWidgetClass, paned, NULL, 0);
messagebox = XtCreateManagedWidget("messages", labelWidgetClass, paned, NULL, 0);
show_message(TXT_WELCOME, VERSION);
graphics_control(Disable);
container = XtCreateManagedWidget("container", viewportWidgetClass, paned, NULL, 0);
desktop = XtCreateManagedWidget("desktop", tableauWidgetClass, container, NULL, 0);
/* XtAddCallback(container, XtNreportCallback, reportfunc, NULL); */
/* create the button panel and its menus */
gamebutton = XtCreateManagedWidget("Game", menuButtonWidgetClass, buttonpanel, NULL, 0);
gamemenu = XtCreatePopupShell("gamemenu", simpleMenuWidgetClass, gamebutton, NULL, 0);
for (bp = mbuttons, i = 0; i < XtNumber(mbuttons); ++i) {
Widget w;
w = XtCreateManagedWidget(bp->name, smeBSBObjectClass, gamemenu, NULL, 0);
if (bp->func) XtAddCallback(w, XtNcallback, perform_command, bp->func);
++bp;
}
XtVaSetValues(gamebutton, XtNmenuName, "gamemenu", NULL);
/* create Rules button just right of the Game button */
rulesbutton = XtCreateManagedWidget("Level Subset", menuButtonWidgetClass, buttonpanel, NULL, 0);
rulesmenu = XtCreatePopupShell("rulesmenu", simpleMenuWidgetClass, rulesbutton, NULL, 0);
XtVaSetValues(rulesbutton, XtNmenuName, "rulesmenu", NULL);
/* rest of the buttons */
for (bp = buttons, i = 0; i < XtNumber(buttons); ++i) {
Widget w;
w = XtCreateManagedWidget(bp->name, commandWidgetClass, buttonpanel, NULL, 0);
if (bp->func) XtAddCallback(w, XtNcallback, perform_command, bp->func);
++bp;
}
#ifdef SOUND
sound = XtCreateManagedWidget("Sound", toggleWidgetClass, buttonpanel, NULL, 0);
#endif
/* OK. Now do the pop-up shells */
popup = XtCreatePopupShell("prompt", transientShellWidgetClass, toplevel, NULL, 0);
dialog = XtCreateManagedWidget("dialog", dialogWidgetClass, popup, NULL, 0);
XawDialogAddButton(dialog, "ok", Ok, (XtPointer)dialog);
XawDialogAddButton(dialog, "cancel", Cancel, (XtPointer)dialog);
#ifdef ONLINE_HELP
create_help();
#endif
graphic.width = graphic.height = 0;
graphic.autolayout = 1;
XtRealizeWidget(toplevel);
XSync(dpy, 0);
mainwindow = XtWindow(toplevel);
XSetIconName(dpy, mainwindow, "xsok");
SetTitle();
table = XtWindow(desktop);
read_gametypes();
{ const char **rp;
for (rp = rulepool; *rp; ++rp) {
Widget w;
w = XtCreateManagedWidget(*rp, smeBSBObjectClass, rulesmenu, NULL, 0);
XtAddCallback(w, XtNcallback, selectrules, NULL);
}
}
graphics_control(Enable);
XtRealizeWidget(popup);
XtAppMainLoop(app_con); /* does not return */
return 0; /* keep compiler happy */
}
void Force_Resize(XSize_t w, XSize_t h) {
Arg args[1];
int hh;
Dimension hhh = 0;
XtSetArg(args[0], XtNheight, &hhh);
XtGetValues(paned, args, 1);
hh = hhh; /* unsigned short => int */
XtGetValues(container, args, 1);
/* printf("rq %d, paned = %d, container = %d\n", h, hh, hhh); */
h += hh - hhh; /* difference between overall size and Viewport size */
XResizeWindow(dpy, mainwindow, w, h);
}
xsok-1.02/src/messages.c 100644 144 62 20020 6122601321 13622 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.01 -- module messages.c */
/* */
/* Internationalisation and keyboard translation/customisation. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
#include <ctype.h>
void (*lastcmd)(void);
int numeric_arg = 0;
const char *xsok_messages[] = {
"Quit game?",
"Another game?",
"Restart game?",
"Next level?",
"Previous level?",
"Not possible",
"Bookmark set",
"You won! (Score %d, Best score: %d)",
"OK",
"XSok version %s",
"Moves: %d, Pushes: %d, Score: %d. Current strength: %d.",
"Already at starting position.",
"Move undone.",
"Redo not possible.",
"Move redone.",
"Welcome to XSok version %s!",
"Saving of game FAILED.",
"Loading the game FAILED.",
"Could not open file.",
"Could not open file.",
"Could not write header.",
"Could not read header.",
"Could not write moves.",
"Could not read moves.",
"Saving the game succeeded.",
"Loading the game succeeded.",
"Magic match failed.",
"The author of this level is unknown.",
"New record for this level!",
"Cannot find a savegame file.",
"Help on keys",
"Help on the %s level subset",
"Starting macro recording.",
"Recorded macro has %d moves.",
"Wrong position for macro replay.",
"Proceed to the next unsolved level?",
"Least moves: %d, Least pushes: %d, Best score: %d.",
"This level is unsolved.",
"Cannot link savegame files.",
"There is no box.",
"There is already a box.",
};
void read_message_file(const char *filename) {
FILE *fp;
int i;
char line[256];
if (*filename != '/') {
sprintf(line, "%s/%s/%s", xsokdir, langdir, filename);
filename = line;
}
if (!(fp = fopen(filename, "r")))
return; /* Xt philosophy: ignore error */
for (i = 0; i < sizeof(xsok_messages) / sizeof(const char *); ++i) {
char *p;
again:
if (!fgets(line, sizeof(line), fp))
break; /* EOF */
if (*line == '#')
goto again;
if ((p = strrchr(line, '\n')))
*p = '\0';
if (*line) /* empty lines => keep old text */
xsok_messages[i] = strsav(line);
}
fclose(fp);
}
static void cmd_None(void) {}
static void replace_binding(struct key_action *p, const char *name) {
int i;
static struct translator {
const char *name;
void (*func)(void);
} translator[] = {
{ "None", cmd_None },
{ "rq_LeaveSok", rq_LeaveSok },
{ "rq_RestartGame", rq_RestartGame },
{ "rq_NextLevel", rq_NextLevel },
{ "rq_NextUnsolved", rq_NextUnsolved },
{ "rq_PrevLevel", rq_PrevLevel },
{ "Up", cmd_Up },
{ "Left", cmd_Left },
{ "Down", cmd_Down },
{ "Right", cmd_Right },
{ "NextUnsolved", cmd_NextUnsolved },
{ "NextLevel", cmd_NextLevel },
{ "PrevLevel", cmd_PrevLevel },
{ "UndoMove", cmd_UndoMove },
{ "RedoMove", cmd_RedoMove },
{ "LeaveSok", cmd_LeaveSok },
{ "ShowScore", cmd_ShowScore },
{ "ShowBestScore", cmd_ShowBestScore },
{ "ShowAuthor", cmd_ShowAuthor },
{ "RestartGame", cmd_RestartGame },
{ "ReplayGame", cmd_ReplayGame },
{ "SaveGame", cmd_SaveGame },
{ "LoadGame", cmd_LoadGame },
{ "ReadScores", cmd_ReadHighscores },
{ "ShowVersion", cmd_ShowVersion },
{ "ResizeWindow", cmd_Resize },
{ "RepeatMove", cmd_Repeat },
{ "MouseMove", cmd_MouseMove }, /* requires x, y */
{ "MousePush", cmd_MousePush }, /* requires x, y */
{ "MouseUndo", cmd_MouseUndo },
{ "MouseDrag", cmd_MouseDrag },
{ "LevelInfo", cmd_LevelInfo },
{ "DropBookmark", cmd_DropBookmark },
{ "GotoBookmark", cmd_GotoBookmark },
{ "StartMacro", cmd_StartMacro },
{ "EndMacro", cmd_EndMacro },
{ "PlayMacro", cmd_PlayMacro },
/* { "Debug", cmd_debug }, */
{ "Cancel", cmd_Cancel },
{ "Confirm", cmd_Confirm }
};
for (i = 0; i < sizeof(translator) / sizeof(translator[0]); ++i)
if (!strcmp(name, translator[i].name)) {
p->action = translator[i].func;
return;
}
fprintf(stderr, "WARNING: no function corresponds to \"%s\"\n", name);
p->action = cmd_None;
}
static struct key_action *global_bindings = NULL;
const char *langdir = "";
void add_keybinding(struct key_action **cp, const char *cmd, const char *function) {
/* a NULL pointer for cmd is a wildcard */
int done = 0;
struct key_action *p;
if (!cp)
return; /* bindings for unimplemented rulesets */
while (*cp) {
p = *cp;
if (!cmd || !strcmp(p->string, cmd)) {
/* replace this! */
replace_binding(p, function);
done = 1;
}
cp = &(p->next);
}
if (!done && cmd) {
/* didn't find previous command */
/* add a new entry */
p = *cp = malloc_(sizeof(struct key_action));
p->next = NULL;
p->string = strsav(cmd);
replace_binding(p, function);
}
}
void read_keyboard_file(const char *filename) {
FILE *fp;
char line[256];
char buff[32], cmd[2];
struct key_action **cp = &global_bindings;
cmd[1] = '\0'; /* 1-char commands currently */
if (*filename != '/') {
sprintf(line, "%s/%s/%s", xsokdir, langdir, filename);
filename = line;
}
/* printf("reading keyboard file \"%s\"\n", filename); */
if (!(fp = fopen(filename, "r"))) {
/* in xsok, the keyboard file is required, not optional */
/* therefore, we issue an error */
fprintf(stderr, "Cannot read keyboard definition file %s\n",
filename);
if (*langdir)
fprintf(stderr, "Hint: Perhaps unsetting LANG or making a symbolic"
" link\nfrom %s/%s to %s helps.\n", xsokdir,
langdir, xsokdir);
exit(1);
}
while (fgets(line, sizeof(line), fp)) {
char *p;
if ((p = strrchr(line, '\n')))
*p = '\0';
if (!*line)
continue;
if (!strncmp(line, "#c", 2)) /* comment */
continue;
if (!strncmp(line, "#include ", 9)) { /* include */
read_keyboard_file(line+9);
continue;
}
if (!strncmp(line, "#x", 2) && strlen(line) < 32) { /* hex number */
int c;
sscanf(line+2, "%x %s", &c, buff);
cmd[0] = c;
add_keybinding(cp, cmd, buff);
continue;
}
/* else assume string<space>commandname */
if (strlen(line) >= 3 && strlen(line) < 32) {
p = line+1;
while (*p && *p != '\t' && *p != ' ')
++p;
if (*p) {
*p = '\0';
while (*++p && (*p == '\t' || *p == ' '))
;
if (*p) {
add_keybinding(cp, line, p);
continue;
}
}
}
fprintf(stderr, "Warning: cannot parse line in keys file:%s\n", line);
}
fclose(fp);
}
void key_pressed(char *str) {
struct key_action *p;
/* search for global binding */
for (p = global_bindings; p; p = p->next)
if (!strcmp(p->string, str)) {
(*p->action)();
lastcmd = p->action;
numeric_arg = 0;
return;
}
/* not found. break up the string to smaller pieces */
if (strlen(str) > 1) {
char s[2];
while (*str) {
s[0] = *str++;
s[1] = '\0';
key_pressed(s);
}
return;
} /* use hardcoded entries. Note: These can be overridden by assigning them "None" before. */
/* process digits */
if (isdigit(str[0])) {
if (numeric_arg >= 3267)
numeric_arg = 0;
numeric_arg = 10 * numeric_arg + (str[0] - '0');
show_message("%d", numeric_arg);
return;
}
switch (str[0]) {
case '\b':
if (numeric_arg >= 10) {
show_message("%d", numeric_arg /= 10);
return;
}
/* else fall through */
case '\033': /* esc */
numeric_arg = 0;
show_message(" ");
return;
case '\014': /* ctrl-L */
case '\022': /* ctrl-R */
refresh_screen();
return;
}
if (str[0]) {
const char *rulechange = "XCS";
const char *rulename[] = { "Xsok", "Cyberbox", "Sokoban" };
const char *s;
if ((s = strchr(rulechange, str[0]))) {
/* printf("found %s\n", rulename[s-rulechange]); */
change_rules(rulename[s-rulechange]);
NewLevel(1);
cmd_LevelInfo();
return;
}
}
}
xsok-1.02/src/loadsave.c 100644 144 62 15453 6046460626 13647 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module loadsave.c */
/* */
/* Functions for highscores and loading/saving games. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <limits.h>
#include <unistd.h>
#include <time.h>
#include "xsok.h"
#include "version.h"
/* code to switch between real and effective user id */
/* we must use the real user id when saving files */
void switch_uid(int to_real) {
#ifdef _POSIX_SAVED_IDS
static int uid_state = -1; /* -1 = unknown, 1 = real, 0 = effective */
static uid_t real_uid, effective_uid;
if (uid_state < 0) {
real_uid = getuid();
effective_uid = geteuid();
uid_state = 0;
}
if (to_real != uid_state && real_uid != effective_uid) {
setuid(to_real ? real_uid : effective_uid);
uid_state = to_real;
}
#endif
}
void setlangdir(void) {
const char *s;
char p[100];
if ((s = getenv("LANG"))) {
sprintf(p, "%s/%s", xsokdir, s);
if (!access(p, F_OK)) { /* langdir does exist */
langdir = s;
return;
}
}
langdir = "";
}
const char *savedir = NULL;
#define NARGS 16 /* score, pushes, moves, time */
#define BUFSIZE 256 /* at least length of longest shortname + 1 */
#define MAGIC1 0x741b /* magic of xsok version 1 */
#define MAGICS 0x741c /* magic of xsok version 1 simple file */
#define MAGICH 0x741d /* magic of xsok version 1 highscore file */
static void read_err(const char *msg) {
if (gamegraphic) {
show_message("%s %s", TXT_LOAD_ERR_BASIC, msg);
cmd_LeaveSok(); /* make it better! */
}
fprintf(stderr, "%s %s\n", TXT_LOAD_ERR_BASIC, msg);
exit(EXIT_FAILURE);
}
static void portable_to_internal(long *args, unsigned char *p, int num) {
do {
int j;
*args = 0;
for (j = 0; j < 4; ++j)
*args += (long)p[3-j] << (j << 3);
++args;
p += 4;
} while (--num);
}
static void internal_to_portable(unsigned char *p, long *args, int num) {
do {
int j;
memset(p, (*args < 0 ? -1 : 0), 4);
for (j = 0; j < 4; ++j)
p[3-j] = (unsigned char)(*args >> (j << 3));
++args;
p += 4;
} while (--num);
}
int highscore[300];
void cmd_ReadHighscores(void) {
FILE *fp;
char filename[MAXSAVEFILELEN];
int i;
memset(highscore, 0, sizeof(highscore));
for (i = 100; i < 300; ++i)
highscore[i] = 0x7fffffff;
sprintf(filename, "%s/%s.score", savedir, game.type);
if ((fp = fopen(filename, "rb"))) {
long p[300];
unsigned char s[1200];
fread(s, 4, 300, fp);
portable_to_internal(p, s, 300);
for (i = 0; i < 300; ++i)
highscore[i] = p[i];
fclose(fp);
}
highscore[0] = MAGICH;
}
void WriteHighscores(void) {
FILE *fp;
char filename[MAXSAVEFILELEN];
sprintf(filename, "%s/%s.score", savedir, game.type);
if ((fp = fopen(filename, "wb"))) {
int i;
long p[300];
unsigned char s[1200];
for (i = 0; i < 300; ++i)
p[i] = highscore[i];
internal_to_portable(s, p, 300);
fwrite(s, 4, 300, fp);
fclose(fp);
}
}
void load_game(const char *filename) {
FILE *fp;
long args[NARGS];
int i, remgraphic = gamegraphic;
unsigned char p[NARGS * 4];
if (gamegraphic) /* interactive loading */
graphics_control(Disable);
OrgLevel();
if (!(fp = fopen(filename, "rb")))
read_err(TXT_LOAD_ERR_OPEN);
if (fread(p, 4, 1, fp) != 1)
read_err(TXT_LOAD_ERR_HEADER);
portable_to_internal(args, p, 1);
if (args[0] == MAGICS) {
fseek(fp, 0L, SEEK_END);
game.stored_moves = ftell(fp) - 4;
game.n_moves = game.bookmark = game.stored_moves;
game.macroStart = -1;
fseek(fp, 4L, SEEK_SET);
goto readmoves;
}
if (args[0] != MAGIC1)
read_err(TXT_LOAD_ERR_BADMAGIC);
if (fread(p, 4, NARGS-1, fp) != NARGS-1)
read_err(TXT_LOAD_ERR_HEADER);
portable_to_internal(args+1, p, NARGS-1);
game.n_moves = args[3];
if (args[7] == -1) { /* loading a game from the alpha version */
fseek(fp, 32, SEEK_SET);
game.stored_moves = args[3];
game.bookmark = game.n_moves;
game.macroStart = -1;
} else {
game.stored_moves = args[7];
game.macroStart = args[8];
game.macroEnd = args[9];
game.macro_x = args[10];
game.macro_y = args[11];
game.bookmark = args[12];
}
/* skip game type and level */
fseek(fp, 8L, SEEK_CUR);
/* skip user name */
i = getc(fp);
fseek(fp, (long)i, SEEK_CUR);
readmoves:
if (game.stored_moves > numalloc) {
numalloc = 256 + (game.stored_moves & ~255);
movetab = realloc_(movetab, numalloc);
}
if (fread(movetab, 1, game.stored_moves, fp) != game.stored_moves)
read_err(TXT_LOAD_ERR_MOVES);
fclose(fp);
{ int rem;
rem = game.n_moves;
game.n_moves = 0;
jumpto_movenr(rem);
}
if (remgraphic)
graphics_control(EnableAndRedraw);
cmd_ShowScore();
}
void save_game(const char *ext) {
FILE *fp;
char filename[MAXSAVEFILELEN];
long args[NARGS];
int i;
unsigned char p[4 * NARGS];
compute_score();
sprintf(filename, "%s/%s.%02d.%s", savedir, game.type, game.level, ext);
remove(filename); /* kill any old one first! */
if (!(fp = fopen(filename, "wb"))) {
show_message("%s %s", TXT_SAVE_ERR_BASIC, TXT_SAVE_ERR_OPEN);
goto werr2;
}
args[0] = MAGIC1;
args[1] = game.score;
args[2] = game.n_pushes;
args[3] = game.n_moves;
args[4] = time(NULL);
args[5] = game.finished;
args[6] = game.level;
args[7] = game.stored_moves;
args[8] = game.macroStart;
args[9] = game.macroEnd;
args[10] = game.macro_x;
args[11] = game.macro_y;
args[12] = game.bookmark;
args[13] = args[14] = args[15] = -1;
internal_to_portable(p, args, NARGS);
if (fwrite(p, 4, NARGS, fp) != NARGS) {
werr3:
show_message("%s %s", TXT_SAVE_ERR_BASIC, TXT_SAVE_ERR_HEADER);
goto werr;
}
strncpy(filename, game.type, 8);
if (fwrite(filename, 1, 8, fp) != 8)
goto werr3;
i = strlen(username);
putc(i, fp);
if (fwrite(username, 1, i, fp) != i)
goto werr3;
if (fwrite(movetab, 1, game.stored_moves, fp) != game.stored_moves)
goto werr3;
fclose(fp);
show_message(TXT_SAVE_OK);
play_sound("ok"); /* found no sound file for this. maybe later */
return;
werr:
fclose(fp);
werr2:
play_sound("cannotsave");
}
void link_game(const char *old, const char *ext) {
char file1[MAXSAVEFILELEN], file2[MAXSAVEFILELEN];
sprintf(file1, "%s/%s.%02d.%s", savedir, game.type, game.level, old);
sprintf(file2, "%s/%s.%02d.%s", savedir, game.type, game.level, ext);
remove(file2); /* kill any old one first! */
if (link(file1, file2))
show_message(TXT_SAVE_ERR_LINK);
}
xsok-1.02/src/username.c 100644 144 62 4207 6037671344 13644 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.01 -- module username.c */
/* */
/* Tries to compute a nice user- and hostname (e-mail address). */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* changed October 1995 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include "xsok.h"
#ifdef BSD_NETKIT
#include <netdb.h>
#endif
char username[256];
void buildusername(const char *name) {
if (name) {
if (strlen(name) > 255) {
strncpy(username, name, 255);
username[256] = '\0';
} else
strcpy(username, name);
} else {
struct passwd *pp;
const char *realname, *loginname;
char fqdn[256];
struct utsname buf;
#ifdef BSD_NETKIT
struct hostent *hp;
#endif
/* getlogin() fails when xsok is called from the fvwm window
manager menu (why?) and cuserid() has disappeared in POSIX
1990. We use getuid() now. */
if ((pp = getpwuid(getuid()))) {
if (pp->pw_gecos && strchr(pp->pw_gecos, ','))
*strchr(pp->pw_gecos, ',') = '\0';
realname = pp->pw_gecos;
loginname = pp->pw_name;
} else {
/* unable to obtain passwd entry */
realname = NULL;
loginname = "unknown";
}
if (uname(&buf))
strcpy(buf.nodename, "unknown");
#ifdef BSD_NETKIT
if ((hp = gethostbyname(buf.nodename)))
strcpy(fqdn, hp->h_name);
else
#endif
sprintf(fqdn, "%s.(unknown)", buf.nodename);
if (realname)
sprintf(username, "%s (%s@%s)", realname, loginname, fqdn);
else
sprintf(username, "%s@%s", loginname, fqdn);
}
}
#ifdef TESTING
int main(int argc, char *argv[]) {
if (argc == 3 && !strcmp(argv[1], "-u"))
buildusername(argv[2]);
else
buildusername(NULL);
printf( /* "Automatically generated username is\n" */
"\"%s\"\n", username);
return 0;
}
#endif
xsok-1.02/src/Xaw-help.c 100644 144 62 6055 5665071060 13510 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module Xaw-help.c */
/* */
/* Online help functions for the Athena Widget interface. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifdef ONLINE_HELP
#include "X-sok.h"
#include "Tableau.h"
#include <X11/Shell.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Sme.h>
#include <X11/Xaw/SmeBSB.h>
static int help_active = 0;
static Widget help, helppaned, helppanel, helptext, helpclose;
extern const char *keyfilename; /* from X-widget.c */
static void selecttopic(Widget w, XtPointer number, XtPointer garbage) {
char filename[200];
const char *s = XtName(w);
Arg Args[2];
int i = atoi(s+4);
sprintf(filename, "%s/%s/%s.help", xsokdir, langdir,
i ? rulepool[i-1] : keyfilename);
XtSetArg(Args[0], XtNstring, filename);
XtSetArg(Args[1], XtNtype, XawAsciiFile);
XtSetValues(helptext, Args, 2);
}
void create_help(void) {
Widget topicsmenu, topicsbutton, w;
Arg Args[1];
help = XtCreatePopupShell("help", transientShellWidgetClass, toplevel, NULL, 0);
helppaned = XtCreateManagedWidget("helppaned", panedWidgetClass, help, NULL, ZERO);
helppanel = XtCreateManagedWidget("helppanel", boxWidgetClass, helppaned, NULL, ZERO);
helptext = XtCreateManagedWidget("helptext", asciiTextWidgetClass, helppaned, NULL, ZERO);
XtSetArg(Args[0], XtNmenuName, "topicsmenu");
topicsbutton = XtCreateManagedWidget("Topic", menuButtonWidgetClass, helppanel, Args, 1);
topicsmenu = XtCreatePopupShell("topicsmenu", simpleMenuWidgetClass, topicsbutton, NULL, ZERO);
helpclose = XtCreateManagedWidget("Close Help", commandWidgetClass, helppanel, NULL, ZERO);
XtAddCallback(helpclose, XtNcallback, popdown_help, NULL);
XtSetArg(Args[0], XtNlabel, TXT_HELP_KEYS);
w = XtCreateManagedWidget("Help0", smeBSBObjectClass, topicsmenu, Args, 1);
XtAddCallback(w, XtNcallback, selecttopic, NULL);
{ const char **rp;
for (rp = rulepool; *rp; ++rp) {
char n[8], s[40];
sprintf(n, "Help%d", rp-rulepool+1);
XtSetArg(Args[0], XtNlabel, s);
sprintf(s, TXT_HELP_RULES, *rp);
w = XtCreateManagedWidget(n, smeBSBObjectClass, topicsmenu, Args, 1);
XtAddCallback(w, XtNcallback, selecttopic, NULL);
}
}
}
void popup_help(void) {
if (help_active)
return; /* request pending => deny another one */
help_active = 1;
XtPopup(help, XtGrabNone);
}
void popdown_help(Widget w, XtPointer a, XtPointer b) {
if (!help_active)
return; /* request pending => deny another one */
help_active = 0;
XtPopdown(help);
}
#endif
xsok-1.02/src/mousemove.c 100644 144 62 22442 6123001237 14045 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.02 -- module mousemove.c */
/* */
/* Computes paths to squares in the distance. */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November 1994 ... March 1996 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include "xsok.h"
#include <assert.h>
static int before_move;
void cmd_MouseUndo(void) {
if (lastcmd != cmd_MouseMove &&
lastcmd != cmd_MousePush &&
lastcmd != cmd_MouseDrag &&
lastcmd != cmd_PlayMacro) {
cmd_UndoMove(); /* normal undo */
return;
}
jumpto_movenr(before_move);
cmd_ShowScore();
}
#define RBSIZE 1024 /* only powers of 2 will work */
#ifndef sgn
#define sgn(x) ((x) < 0 ? -1 : 1)
#endif
static int noeffect(int eff) {
switch(eff) {
case E_NOTHING:
case E_EXIT:
case E_DEST:
return 1;
default:
return 0;
}
}
#define STEP(dx,dy,dir) if ( \
(map[y+(dy)][x+(dx)]->mask & 1) && \
noeffect(map[y+(dy)][x+(dx)]->effect) && \
(map[y+(dy)][x+(dx)]->enter & (1 << dir)) && \
(map[y+(dy)][x+(dx)]->leave & (1 << dir)) && \
!obj[y+(dy)][x+(dx)] && \
dist[y+(dy)][x+(dx)] > d) { \
dist[remy[wr] = y+(dy)][remx[wr]= x+(dx)] = d; \
if ((wr = (wr + 1) & (RBSIZE-1)) == rd) break; }
/* search from destination to source */
static int backsteps(unsigned dist[MAXROW][MAXCOL],
int x1, int y1, int x2, int y2) {
int rd = 0, wr = 1, remx[RBSIZE], remy[RBSIZE];
memset(dist, 0xff, sizeof(unsigned)*MAXROW*MAXCOL);
if (!(map[y1][x1]->mask & 1))
return -1;
remx[0] = x1;
remy[0] = y1;
dist[y1][x1] = 0;
while (rd != wr) {
int x, y, d;
if (dist[y2][x2] < 30000)
return dist[y2][x2];
x = remx[rd];
y = remy[rd];
rd = (rd+1) & (RBSIZE-1);
d = 1 + dist[y][x];
STEP(1,0,1); /* dir is reverse since we do backward search */
STEP(0,1,0);
STEP(-1,0,3);
STEP(0,-1,2);
}
/* no path is found */
return -1;
}
static void subcmd_pushto(int dx, int dy) {
int i, dir;
if (dx) {
i = abs(dx);
dir = 2 + sgn(dx);
} else {
i = abs(dy);
dir = 1 + sgn(dy);
}
while (i--)
playermove(dir);
}
void cmd_MousePush(void) {
int dx, dy;
before_move = game.n_moves;
dx = mouse_x - game.x;
dy = mouse_y - game.y;
if (dx && dy)
cmd_MouseMove();
else
subcmd_pushto(dx, dy);
}
static void subcmd_moveto(int xx, int yy) {
unsigned dist[MAXROW][MAXCOL];
struct objects *ip = obj[game.y][game.x];
/* case 1: a distance 1 click will move or push */
if (xx == game.x && yy == game.y)
return; /* shortcut! */
if (abs(xx - game.x) + abs(yy - game.y) == 1) {
if (xx - game.x)
if (xx > game.x)
playermove(3);
else
playermove(1);
else
if (yy > game.y)
playermove(2);
else
playermove(0);
return;
}
/* for greater distance, only use free space. */
obj[game.y][game.x] = NULL;
if (backsteps(dist, xx, yy, game.x, game.y) < 0) {
obj[game.y][game.x] = ip;
show_message(TXT_MOVENOTPOSSIBLE);
return;
}
obj[game.y][game.x] = ip;
while (game.y != yy || game.x != xx) {
unsigned length;
int omove;
length = dist[game.y][game.x] - 1;
omove = game.n_moves;
if (dist[game.y-1][game.x] == length) playermove(0);
else if (dist[game.y][game.x-1] == length) playermove(1);
else if (dist[game.y+1][game.x] == length) playermove(2);
else if (dist[game.y][game.x+1] == length) playermove(3);
if (dist[game.y][game.x] > length)
break; /* else may cause cycling (with teleporters) */
if (game.n_moves != omove + 1)
break; /* precomputed move not possible any more */
}
}
void cmd_MouseMove(void) {
before_move = game.n_moves;
subcmd_moveto(mouse_x, mouse_y);
}
static int dxtab[4] = { 0, -1, 0, 1 };
static int dytab[4] = { -1, 0, 1, 0 };
void cmd_MouseDrag(void) {
struct objects *ip = obj[game.y][game.x], *box;
unsigned dist[MAXROW][MAXCOL];
int xx, yy, number[MAXROW][MAXCOL], num = 0;
before_move = game.n_moves;
if (mouse_x == mouse_x0 && mouse_y == mouse_y0)
return; /* this is a no-op */
if (!(box = obj[mouse_y0][mouse_x0])) {
show_message(TXT_NOBOX);
return;
}
obj[game.y][game.x] = NULL; /* remove player */
if (obj[mouse_y][mouse_x]) {
show_message(TXT_ALREADYBOX);
obj[game.y][game.x] = ip; /* restore player */
return;
}
if (backsteps(dist, mouse_x0, mouse_y0, game.x, game.y) < 0) {
notposs:
obj[game.y][game.x] = ip;
show_message(TXT_MOVENOTPOSSIBLE);
return;
}
/* number the possibilities for the box */
num = 1;
for (xx = 0; xx < game.numcols; ++xx)
for (yy = 0; yy < game.numrows; ++yy)
if ((map[yy][xx]->mask & obj[mouse_y0][mouse_x0]->mask)
&& !obj[yy][xx])
number[yy][xx] = num++;
else
number[yy][xx] = -1;
number[mouse_y0][mouse_x0] = 0;
if (number[mouse_y][mouse_x] < 0)
goto notposs;
{ /* allocate array for the possible box states */
int d;
struct boxdist {
int pushes;
int moves;
int x, y; /* box position */
int inqueue;
int predecessor;
} *boxdist;
int *states, rd = 0, wr = 0;
boxdist = malloc(4 * num * sizeof(struct boxdist));
states = malloc(4 * num * sizeof(int));
for (xx = 0; xx < 4 * num; ++xx) {
boxdist[xx].pushes = boxdist[xx].moves = 30000;
boxdist[xx].inqueue = 0;
}
/* initial positions */
for (xx = 0; xx < 4; ++xx) {
d = backsteps(dist,
mouse_x0+dxtab[xx], mouse_y0+dytab[xx],
game.x, game.y);
if (d >= 0) {
states[wr++] = xx;
boxdist[xx].moves = d;
boxdist[xx].pushes = 0;
boxdist[xx].x = mouse_x0;
boxdist[xx].y = mouse_y0;
boxdist[xx].inqueue = 1;
boxdist[xx].predecessor = -1;
}
}
/* loop */
obj[mouse_y0][mouse_x0] = NULL; /* remove box */
while (rd < wr) {
/* get position */
struct boxdist *now;
now = boxdist + (yy = states[rd++]);
now->inqueue = 2;
#ifdef TESTING
printf("Scanning square %d: %d,%d, pushes=%d, moves=%d\n",
yy, now->x, now->y, now->pushes, now->moves);
#endif
obj[now->y][now->x] = box;
/* player is at x+dxtab[yy & 3], y+dytab[yy & 3] */
for (xx = 0; xx < 4; ++xx) {
int newx, newy, newpos;
newy = now->y-dytab[xx];
newx = now->x-dxtab[xx];
newpos = number[newy][newx];
if (newpos < 0)
continue;
newpos = 4 * newpos + xx; /* is index */
if (boxdist[newpos].pushes <= now->pushes)
continue; /* fewer pushes with the old path */
/* shift into dir 2^xx */
if (!(map[newy][newx]->mask & box->mask) ||
!(map[now->y][now->x]->mask & 1) ||
obj[newy][newx] || obj[now->y+dytab[xx]][now->x+dxtab[xx]])
continue;
d = backsteps(dist,
now->x+dxtab[xx], now->y+dytab[xx],
now->x+dxtab[yy&3], now->y+dytab[yy&3]);
if (d < 0)
continue;
d += now->moves;
if (boxdist[newpos].pushes > now->pushes+1 ||
(boxdist[newpos].pushes == now->pushes+1 &&
boxdist[newpos].moves > d)) {
boxdist[newpos].pushes = now->pushes+1;
boxdist[newpos].moves = d;
boxdist[newpos].x = newx;
boxdist[newpos].y = newy;
boxdist[newpos].predecessor = yy;
if (!boxdist[newpos].inqueue) {
boxdist[newpos].inqueue = 1;
states[wr++] = newpos;
/* printf(" adding state %d\n", newpos);
} else {
printf(" improving state %d\n", newpos); */
}
assert(boxdist[newpos].inqueue != 2);
}
}
obj[now->y][now->x] = NULL;
}
obj[mouse_y0][mouse_x0] = box; /* restore box */
free(states);
#ifdef TESTING
printf("box move from %d,%d to %d,%d requested, %d reachable\n",
mouse_x0, mouse_y0, mouse_x, mouse_y, num);
for (xx = 0; xx < 4; ++xx)
printf("xx = %d: pushes = %5d, moves = %5d\n",
xx,
boxdist[4*number[mouse_y][mouse_x]+xx].pushes,
boxdist[4*number[mouse_y][mouse_x]+xx].moves);
#endif
/* find best end place */
d = yy = 4*number[mouse_y][mouse_x];
for (xx = 1; xx < 4; ++xx) {
if (boxdist[yy+xx].pushes < boxdist[d].pushes ||
(boxdist[yy+xx].pushes == boxdist[d].pushes &&
boxdist[yy+xx].moves < boxdist[d].moves)) {
d = yy+xx;
}
}
if (boxdist[d].pushes == 30000) {
free(boxdist);
goto notposs; /* not possible */
}
obj[game.y][game.x] = ip; /* restore player */
/* no the move definitely is possible */
/* compute path by predecessor entries */
xx = -1; /* last */
yy = d; /* current */
do {
boxdist[yy].inqueue = xx; /* next */
xx = yy;
yy = boxdist[yy].predecessor;
} while (yy != -1);
/* have path. xx is first position to achieve */
/* first move is different (no push, just setup) */
xx = boxdist[xx].inqueue;
while (xx != -1) {
#ifdef TESTING
mouse_x = boxdist[xx].x + 2*dxtab[xx&3];
mouse_y = boxdist[xx].y + 2*dytab[xx&3];
printf("at %d,%d, xx=%d, require moveto %d,%d, pushto %d,%d\n",
game.x, game.y, xx, mouse_x, mouse_y,
boxdist[xx].x, boxdist[xx].y);
#endif
subcmd_moveto(boxdist[xx].x + 2*dxtab[xx&3],
boxdist[xx].y + 2*dytab[xx&3]);
#if 0
mouse_x = boxdist[xx].x + dxtab[xx&3];
mouse_y = boxdist[xx].y + dytab[xx&3];
cmd_MousePush();
#else
subcmd_pushto(-dxtab[xx&3],-dytab[xx&3]);
#endif
xx = boxdist[xx].inqueue; /* next move */
}
free(boxdist);
}
}
xsok-1.02/src/X-sound_SUN.c 100644 144 62 2164 5665071060 14102 0 ustar mbi mathopt /*****************************************************************************/
/* */
/* */
/* Xsok version 1.00 -- module X-sound_SUN.c */
/* */
/* SUN audio functions play_sound(). */
/* Written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de) */
/* November-1994 */
/* see COPYRIGHT.xsok for Copyright details */
/* */
/* */
/*****************************************************************************/
#ifdef SOUND
#include "X-sok.h"
#ifndef AUDIO_DEVICE
#define AUDIO_DEVICE "/dev/audio"
#endif
void play_sound(const char *filename) {
static int audio = 1;
if (audio && checksound()) {
char fullname[200];
FILE *fp, *fsnd;
int c;
if (!(fsnd = fopen(AUDIO_DEVICE, "wb"))) {
audio = 0;
return; /* cannot open /dev/audio */
}
XSync(dpy, 0); /* text first! */
sprintf(fullname, "%s/audio/%s.au", xsokdir, filename);
if (!(fp = fopen(fullname, "rb"))) {
fclose(fsnd);
return;
}
/* yeah, copy data */
while ((c = getc(fp)) != EOF)
fputc(c, fsnd);
fclose(fsnd);
fclose(fp);
}
}
#endif
xsok-1.02/src/showscore.c 100644 144 62 4764 6055646517 14055 0 ustar mbi mathopt #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#define MAGICH 0x741d /* magic of xsok version 1 highscore file */
static void portable_to_internal(long *args, unsigned char *p, int num) {
do {
int j;
*args = 0;
for (j = 0; j < 4; ++j)
*args += (long)p[3-j] << (j << 3);
++args;
p += 4;
} while (--num);
}
int highscore[300];
static void ReadHighscores(const char *levelfile) {
FILE *fp;
char filename[256], base_name[32], *s;
int i;
memset(highscore, 0, sizeof(highscore));
for (i = 100; i < 300; ++i)
highscore[i] = 0x7fffffff;
strcpy(base_name, levelfile);
if (!(s = strchr(base_name, '.')))
return;
strcpy(s+1, "score");
sprintf(filename, "%s/%s", XSOKSAVE, base_name);
if ((fp = fopen(filename, "rb"))) {
long p[300];
unsigned char ss[1200];
fread(ss, 4, 300, fp);
portable_to_internal(p, ss, 300);
for (i = 0; i < 300; ++i)
highscore[i] = p[i];
fclose(fp);
}
highscore[0] = MAGICH;
}
static int brief = 1;
#define NARGS 16
static void checkfile(const char *filename) {
FILE *fp;
long p[NARGS];
unsigned char s[256];
char type[10], *z;
int namelen, level;
if (!(fp = fopen(filename, "rb")))
return;
fread(s, 4, NARGS, fp);
portable_to_internal(p, s, NARGS);
if (p[0] != 0x741b)
return;
fread(type, 1, 8, fp);
level = p[6];
type[8] = '\0';
namelen = getc(fp);
fread(s, 1, namelen, fp);
s[namelen] = '\0';
if (strrchr(filename, '/'))
filename = strrchr(filename, '/') + 1;
ReadHighscores(filename);
z = ctime(&p[4]);
if (strchr(z, '\n'))
*strchr(z, '\n') = '\0';
if (!brief) {
printf("%-14s %-7s Level %2d: Score: %5ld, Pushes: %4ld, Moves: %4ld\n",
filename, type, level, p[1], p[2], p[3]);
#if 0
if (!p[5]) /* unsolved */
printf("\n");
else
#endif
printf("saved %s by %s\n", z, s);
} else {
int c1, c2, c3;
c1 = p[5] && highscore[level] == p[1] ? '*' : ' ';
c2 = p[5] && highscore[level+200] == p[2] ? '*' : ' ';
c3 = p[5] && highscore[level+100] == p[3] ? '*' : ' ';
printf("%2d %5ld%c %4ld%c %4ld%c %s\n", level, p[1], c1, p[2], c2, p[3], c3, s);
}
}
int main(int argc, char *argv[]) {
int i;
if (argc == 1) {
fprintf(stderr, "usage: showscore [-b] (savefiles)\n");
exit(1);
}
i = 1;
if (!strcmp(argv[i], "-b")) {
brief = 0;
++i;
} else
printf("Lv Score Pushes Moves Player\n");
for (; i < argc; ++i)
checkfile(argv[i]);
return 0;
}
xsok-1.02/src/combine.c 100644 144 62 2026 5665071060 13431 0 ustar mbi mathopt #define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int stripcomments = 0;
static int copy(FILE *fout, char *s, int i) {
FILE *fp;
if (!(fp = fopen(s, "r")))
return 0;
if (i)
fprintf(fout, ";LEVEL %d\n", i);
while (fgets(s, 100, fp)) {
if (stripcomments) {
if (!strncmp(s, "; ", 2))
continue;
if (!strcmp(s, ";\n"))
continue;
}
fprintf(fout, "%s", s);
}
fclose(fp);
return 1;
}
int main(int argc, char *argv[]) {
int c, i;
for (c = 1; c < argc; ++c) {
FILE *fout;
char s[100];
if (!strcmp(argv[c], "-s")) {
stripcomments = 1;
continue;
}
sprintf(s, "%s.def", argv[c]);
if (!(fout = fopen(s, "w"))) {
fprintf(stderr, "Cannot open output file %s\n", s);
exit(1);
}
sprintf(s, "%s/definitions", argv[c]);
if (!copy(fout, s, 0)) {
fprintf(stderr, "No file %s\n", s);
exit(1);
}
for (i = 1; i < 100; ++i) {
sprintf(s, "%s/screen.%02d", argv[c], i);
if (!copy(fout, s, i))
break;
}
}
return 0;
}
xsok-1.02/src/version.h 100644 144 62 30 6122776727 13453 0 ustar mbi mathopt #define VERSION "1.02"
xsok-1.02/src/mergescores.c 100644 144 62 4030 6055646577 14347 0 ustar mbi mathopt #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
int highscore[300];
int newscore[300];
#define MAGICH 0x741d /* magic of xsok version 1 highscore file */
static void portable_to_internal(long *args, unsigned char *p, int num) {
do {
int j;
*args = 0;
for (j = 0; j < 4; ++j)
*args += (long)p[3-j] << (j << 3);
++args;
p += 4;
} while (--num);
}
static void internal_to_portable(unsigned char *p, long *args, int num) {
do {
int j;
memset(p, (*args < 0 ? -1 : 0), 4);
for (j = 0; j < 4; ++j)
p[3-j] = (unsigned char)(*args >> (j << 3));
++args;
p += 4;
} while (--num);
}
static void ReadHighscores(const char *filename) {
FILE *fp;
int i;
memset(highscore, 0, sizeof(highscore));
for (i = 100; i < 300; ++i)
highscore[i] = 0x7fffffff;
if ((fp = fopen(filename, "rb"))) {
long p[300];
int num;
unsigned char s[1200];
num = fread(s, 4, 300, fp);
portable_to_internal(p, s, num);
for (i = 0; i < num; ++i)
highscore[i] = p[i];
fclose(fp);
}
highscore[0] = MAGICH;
}
static void WriteHighscores(const char *filename) {
FILE *fp;
if ((fp = fopen(filename, "wb"))) {
int i;
long p[300];
unsigned char s[1200];
for (i = 0; i < 300; ++i)
p[i] = highscore[i];
internal_to_portable(s, p, 300);
fwrite(s, 1, 1200, fp);
fclose(fp);
}
}
static void mergescores(const char *filename) {
const char *base_name;
int i;
base_name = strrchr(filename, '/');
if (!base_name) {
fprintf(stderr, "need directory components for %s\n", filename);
exit(1);
}
++base_name;
ReadHighscores(filename);
memcpy(newscore, highscore, sizeof(highscore));
ReadHighscores(base_name);
for (i = 1; i < 100; ++i)
if (newscore[i] > highscore[i])
highscore[i] = newscore[i];
for (i = 100; i < 300; ++i)
if (newscore[i] < highscore[i])
highscore[i] = newscore[i];
WriteHighscores(filename);
}
int main(int argc, char *argv[]) {
int i;
for (i = 1; i < argc; ++i)
mergescores(argv[i]);
return 0;
}
xsok-1.02/src/XSok.ad 100644 144 62 2521 5665071060 13043 0 ustar mbi mathopt XSok*beNiceToColormap: false
XSok*shapeStyle: Rectangle
XSok*topShadowContrast: 20
XSok*bottomShadowContrast: 40
XSok*Scrollbar*background: Gray70
XSok*Background: grey85
XSok*Foreground: black
XSok*resizeToPreferred: True
XSok*input: True
XSok*showGrip: off
XSok*shadowWidth: 2
XSok*messages.justify: Left
XSok*upperbox.orientation: XtorientHorizontal
XSok*lowerbox.orientation: XtorientHorizontal
XSok*Tableau.keyboardFile: keys
XSok*Tableau.messageFile: messages
XSok*Tableau.background: black
XSok*Tableau.rules: Sokoban
XSok*Viewport.allowHoriz: False
XSok*Viewport.allowVert: False
XSok*Viewport.useBottom: True
XSok*Viewport.useRight: True
XSok*Label.shadowWidth: 0
XSok*Label.BorderWidth: 2
XSok*Dialog*Translations: #override \n\
<Key>y: Ok()\n\
<Key>n: Cancel()\n
XSok*title: XSok
XSok.prompt.allowShellResize: True
XSok.prompt.saveUnder: True
XSok*Dialog*resizable: True
XSok*Hint.Translations: #override\n\
<BtnDown>:set()\n\
<BtnUp>:HintNotify()unset()\n
XSok*Sound.state: True
XSok.help.width: 403
XSok.help.height: 200
XSok.help.title: XSok Help Window
XSok.help.saveUnder: True
XSok*Close Help.fromHoriz: Topic
XSok*helptext*string: Please choose a topic.
XSok*helptext*displayCaret: False
XSok*helptext*scrollHorizontal: whenNeeded
XSok*helptext*scrollVertical: whenNeeded
XSok*helptext*editType: read
xsok-1.02/src/convertscore.c 100644 144 62 1171 5665071060 14531 0 ustar mbi mathopt #include <stdio.h>
#include <stdlib.h>
/* convert a savegame file from the alpha version to the beta version */
int main(void) {
long len;
unsigned char p[64];
unsigned char s[10000];
memset(p, 0xff, sizeof(p));
fread(p, 32, 1, stdin);
len = fread(s, 1, 10000, stdin);
memcpy(p+28, p+12, 4); /* stored move */
memset(p+20, 0, 8);
p[23] = 1; /* assume it's finished */
p[27] = s[7]; /* level */
memcpy(p+48, p+12, 4); /* bookmark where started */
if (s[0] == 'C')
s[7] = 'x';
else
s[7] = '\0';
fwrite(p, 1, 64, stdout);
fwrite(s, 1, len, stdout);
return 0;
}
xsok-1.02/README 100644 144 62 3372 5665071060 11747 0 ustar mbi mathopt Hi,
Here is a short checklist of what you need to compile and run xsok-1.00.
- an ANSI C compiler
- a POSIX.1 conforming system (OK, with some patches, you may do without)
- GNU gzip / gunzip binaries somewhere in your PATH (same comment)
- X11 R5 or R6 (older ones possibly work, but are untested)
- the XPM library (libXpm.*), preferably version 3.2g or newer. I used the
dll version of Mitch DSouza (mitchum.dsouza@mrc-apu.cam.ac.uk) found in
sunsite.unc.edu:/pub/Linux/libs/X/libXpm-3.4c.tar.gz (28-aug-1994)
- LaTeX if you want to print the manual.
For a standard installation, type 'make', optionally 'make manual', then
(as root) 'make install'. Type 'install.fsstnd' if you want to install the
binary in /usr/games/bin instead of X11's bin directory (I prefer this).
If you want to install xsok locally, i.e. in your home directory, change
XSOKDIR and XSOKSAVE to $(HOME)/xsok in src/Imakefile, then type 'make'
and 'make install.local'. There is a patch for this in the diffs subdirectory.
Apart from Linux, this software has been tested on a HP 9000/720 (HP-UX 9.01),
which required commenting out the HAVE_USLEEP and BSD_NETKIT options in
src/Imakefile. Diffs for this can be found in the diffs subdirectory.
Compiling on a SUN (SUNOS 4.1.3) produced LOTS of warnings due to missing
prototypes in the non-ANSI include files, but the binary worked nevertheless.
If the installation succeeds, but you get the error message
'Cannot read floor.xpm', you perhaps have an older version of libXpm.
Simply gunzip $(XSOKDIR)/*.xpm.gz and be happy again.
(This was the case with both the SUN and the HP machine.)
Please report bugs to mbi@mo.math.nat.tu-bs.de.
Should you create new levels, I would be glad if you could send them to me.
Have fun
Michael
xsok-1.02/diffs/ 40755 144 62 0 5665071060 12060 5 ustar mbi mathopt xsok-1.02/diffs/diff.local 100644 144 62 2256 5665071060 14106 0 ustar mbi mathopt *** src/Imakefile Thu Nov 24 15:19:07 1994
--- src/Imakefile.local Thu Nov 24 15:50:41 1994
***************
*** 46,52 ****
# BINDIR and LIBDIR should be predefined by the templates
# BINDIR = /usr/bin/X11
# LIBDIR = /usr/lib/X11
! XSOKLIBDIR = /usr/games/lib/xsok
APPDEFSDIR = $(LIBDIR)
XSOKMANDIR = /usr/man/man6
XSOKDOCDIR = /usr/doc
--- 46,52 ----
# BINDIR and LIBDIR should be predefined by the templates
# BINDIR = /usr/bin/X11
# LIBDIR = /usr/lib/X11
! XSOKLIBDIR = $(HOME)/xsok
APPDEFSDIR = $(LIBDIR)
XSOKMANDIR = /usr/man/man6
XSOKDOCDIR = /usr/doc
***************
*** 56,62 ****
# The directory XSOKSAVEDIR must have permissions rwx for world, else
# xsok must be installed suid, and XSOKDIR must have write permissions for
# the owner of xsok
! XSOKSAVEDIR = /var/games/xsok
# paths for installation in user's home-directory.
LXSOKBINDIR = $(HOME)/bin
--- 56,62 ----
# The directory XSOKSAVEDIR must have permissions rwx for world, else
# xsok must be installed suid, and XSOKDIR must have write permissions for
# the owner of xsok
! XSOKSAVEDIR = $(HOME)/xsok
# paths for installation in user's home-directory.
LXSOKBINDIR = $(HOME)/bin
xsok-1.02/diffs/diff.hpux 100644 144 62 2157 5665071060 14000 0 ustar mbi mathopt *** src/Imakefile Thu Nov 24 15:19:07 1994
--- src/Imakefile.hpux Thu Nov 24 15:50:51 1994
***************
*** 35,47 ****
# part of the POSIX standard.)
# Remove this define, if you do not have this function.
# affected file(s): username.c
! NET_DEFINE = -DBSD_NETKIT
# 5.) For the replay option, we need the possibility to sleep for a short
# period of time. This can be done by the usleep() function.
# Remove this, if you need usleep() emulation code.
# affected file(s): X-gfx.c
! SLEEP_DEFINE = -DHAVE_USLEEP
# BINDIR and LIBDIR should be predefined by the templates
# BINDIR = /usr/bin/X11
--- 35,47 ----
# part of the POSIX standard.)
# Remove this define, if you do not have this function.
# affected file(s): username.c
! NET_DEFINE = #-DBSD_NETKIT
# 5.) For the replay option, we need the possibility to sleep for a short
# period of time. This can be done by the usleep() function.
# Remove this, if you need usleep() emulation code.
# affected file(s): X-gfx.c
! SLEEP_DEFINE = #-DHAVE_USLEEP
# BINDIR and LIBDIR should be predefined by the templates
# BINDIR = /usr/bin/X11
xsok-1.02/etc/ 40755 144 62 0 6151124576 11542 5 ustar mbi mathopt xsok-1.02/etc/magic 100644 144 62 1057 5665071060 12643 0 ustar mbi mathopt # /etc/magic values for xpat and xsok as of 13-nov-1994
# 1) for low-endian machines (i386)
0 long 0x19740000 xpat save game file
0 long 0x1a740000 xpat2 save game file
0 long 0x1b740000 xsok save game file
0 long 0x1c740000 xsok simple save game file (autosolver)
0 long 0x1d740000 xsok highscore file
# 2) for high-endian machines (SUN, HP-RISC)
0 long 0x00007419 xpat save game file
0 long 0x0000741a xpat2 save game file
0 long 0x0000741b xsok save game file
0 long 0x0000741c xsok simple save game file (autosolver)
0 long 0x0000741d xsok highscore file
xsok-1.02/etc/COPYRIGHT.GNU 100644 144 62 35433 5665071060 13610 0 ustar mbi mathopt GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
xsok-1.02/etc/COPYRIGHT.xsok 100644 144 62 1345 5665071060 14116 0 ustar mbi mathopt /*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. (The file COPYRIGHT.GNU)
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
xsok-1.02/etc/xsok.lsm 100644 144 62 1055 6151124070 13326 0 ustar mbi mathopt Begin3
Title: xsok
Version: 1.02
Entered-date: 23MAY96
Description: Strategy game for X11, implementation of Sokoban, Cyberbox.
Keywords: Sokoban, Cyberbox, puzzle
Author: mbi@mo.math.nat.tu-bs.de (Michael Bischoff)
Maintained-by: mbi@mo.math.nat.tu-bs.de (Michael Bischoff)
Primary-site: sunsite.unc.edu: /pub/Linux/games/x11/strategy
117kB xsok-1.02-src.tar.gz
58kB xsok-1.02-dllbin.tar.gz (X11R5)
64kB xsok-1.02-elfbin.tar.gz (X11R6)
Platform: X11, Athena Widgets, Xpm library
Copying-policy: GPL2
End
xsok-1.02/etc/COPYRIGHT.xpm 100644 144 62 3253 5665071060 13736 0 ustar mbi mathopt /*
* Copyright (C) 1989-94 GROUPE BULL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of GROUPE BULL shall not be
* used in advertising or otherwise to promote the sale, use or other dealings
* in this Software without prior written authorization from GROUPE BULL.
*/
Arnaud LE HORS BULL Research FRANCE -- Koala Project
(XPM - X PixMap format version 2 & 3)
Internet: lehors@sophia.inria.fr
Surface Mail: Arnaud LE HORS, INRIA - Sophia Antipolis,
2004, route des Lucioles, 06565 Valbonne Cedex -- FRANCE
Voice phone: (33) 93.65.77.71, Fax: (33) 93 65 77 66, Telex: 97 00 50 F
xsok-1.02/etc/Changelog 100644 144 62 333 6151123774 13427 0 ustar mbi mathopt Changes from version 1.01 to version 1.02:
- the middle mouse button now acts in a more smart fashion:
With this button, you may move the box on a non-linear
path with a single click! See the man-page for details.
xsok-1.02/doc/ 40755 144 62 0 6123003304 11514 5 ustar mbi mathopt xsok-1.02/doc/Makefile 100644 144 62 246 5665071060 13251 0 ustar mbi mathopt all: xsok.ps
xsok.ps: xsok.dvi
dvips -f < xsok.dvi > xsok.ps
xsok.dvi: xsok.tex
latex xsok
latex xsok
latex xsok
clean:
rm -f *~ *.log *.aux *.toc *.dvi *.ps
xsok-1.02/doc/xsok.tex 100644 144 62 45171 6123003063 13351 0 ustar mbi mathopt \documentstyle[12pt]{article}
\newcommand{\xsok}{{\tt xsok}}
\title{\xsok\ Version 1.02 Manual}
\author{Michael Bischoff}
\date{16-Mar-1996}
\begin{document}
\maketitle
\tableofcontents
\newpage
\section{Introduction}
\subsection{WARNING}
Be warned. This manual is under construction. It may currently contain a lot of typos,
bad English, bad style, and it's unfinished. The information herein should be correct, however.
\subsection{\xsok\ --- A Generic Sokoban Game for X11}
Similar to Tetris, almost every operating system has an implementation of
the Sokoban game. It baffles by its easy rules, but nevertheless
complex strategies.
For Linux, I found a textmode implementation and an X11-based version, both
written 1992. The X11 version seemed to be based on the textmode version,
and both use the same format for the level definition files.
\xsok\ is a generalization of the standard Sokoban game.
All original Sokoban levels may be played with \xsok\ (again, using the same
format for the level definition files). Some levels of a similar MSDOS game,
Cyberbox, may be played also.
\xsok\ is a one-person game played on a square-tiled board. It has no random
elements, and is a pure strategic game. There are different
floor types and walls, and objects standing on the floor. The objective is
to push certain objects onto marked target squares, and to move onto the EXIT
square, if one exists.
The objects on the floor, which we will refer to as boxes, have certain
attributes,
such as weight, value, possible directions of movement, and own power.
A special kind of object is the player itself. He has a certain power, which
represents the maximal sum of the weights of the boxes he can push in a single
move.
The default power is one hundred, and equals the weight of a standard box.
There are lighter (empty) boxes of weight one, which simply block your way,
especially since some of them may be moved either only horizontally or only
vertically.
Some boxes even show interest in moving themselves into a certain direction,
and may even push standard boxes! (I have no explanation for this phenomenon!)
Luckily, most boxes are marked with arrows and color, so that you may
distinguish them.
As well as the boxes, even the floor squares may have attributes.
In Sokoban, the only floor attribute was the distinction between normal floor
and target spaces.
In \xsok, there may be different target spaces, for different box types.
Some squares may be passed in only one direction, or restrict the box types
which may be moved upon them.
There are floor types which turn boxes, by 90 or 180 degrees.
Some squares may give you power cookies, others let you suffer from weakness.
To manage this zoo, all possible levels are partioned into level sets, which
share a common subset of the possible square types.
A level set definition file assigns the attributes to characters.
The definition table for the floor types must follow a line with
the contents ``{\tt ;WALLS}'' in the level subset definition file.
For a floor type, there are the following attributes.
\begin{center}
\begin{tabular}{|l|l|p{8cm}|}
\hline
field & format & description\\
\hline
char & ascii & character, by which this floor type is identified in the level file\\
pic & hex & offset into the graphic data file\\
enter & hex & one bit set for every direction from which any object may enter this square\\
leave & hex & one bit set for every direction to which any object may leave this square\\
mask & hex & one bit set for every object class which is allowed on this square\\
effect & dec & identifies a special effect (rotation, target, exit)\\
\hline
\end{tabular}
\end{center}
The format determines how the entry is interpreted: as ASCII character, as
hexadecimal number, or as decimal number.
You may not use the ';' sign as ASCII representation of an object or a floor
type, since it introduces comments and commands.
Picture id 0 is reserved for the standard walls: There are 16 different
tiles, from which one is selected depending on the 4 neighbour squares.
The effect field has the following meaning.
\begin{center}
\begin{tabular}{|r|l|}
\hline
effect mod 100 & description\\
\hline
0 & no effect \\
1 & rotate counterclockwise by 90 degrees\\
2 & rotate by 180 degrees\\
3 & rotate clockwise by 90 degrees\\
4 & this is a target square for boxes\\
5 & this is the exit square\\
6 & increase power by 1\\
7 & decrease power by 1\\
8 & teleport\\
\hline
\end{tabular}
\end{center}
If the effect field is less than 100, the effect will affect every object once
it enters the square. If the effect is at least 100, but less than 200, the
effect will affect the first object which enters the square, and then the
square
will be substituted by a standard floor square.
The standard floor square is the floor type which occurs first in the table.
If the effect field is at least 200, the square will change to floor type
(effect-200) / 100 once it is free again.
A teleporter will teleport any object on it to the first free teleporter
available,
or do nothing, if all other teleporters are occupied.
The definition table for the object types must follow a line with
the contents ``{\tt ;OBJECTS}'' in the level subset definition file,
and must be behind the floor definition array.
For an object, there are the following attributes.
\begin{center}
\begin{tabular}{|l|l|p{8cm}|}
\hline
field & format & description\\
\hline
char & ascii & character, by which this floor type is identified in the level file\\
pic & hex & offset into the graphic data file\\
movedir & hex & one bit set for every direction in which this object may move\\
pushdir & hex & one bit set for every direction in which this object
will move automatically\\
weight & dec & the weight of this object\\
power & dec & the power which the object has for moving\\
mask & hex & defines the object class (multiple classes are possible)\\
score & hex & the bonus you receive if this object is on a target square\\
\hline
\end{tabular}
\end{center}
The object which represents the man is the first object in the table.
Its {\tt mask} entry must be 1, since this is hardcoded into the program.
The {\tt mask} entry should only use bits 0 to 15. The higher bits are
needed for the {\tt Cyberbox} selectors, their coding is subject to change.
\section{Adding New Level Subsets}
If you want to create new level subsets, create a text file {\tt gametypes}
in the directory {\tt /usr/games/lib/xsok}, where every line of this file
represents the name of a new level subset.
Suppose you want to create the subset {\tt Foo}.
Create a description in the file {\tt /usr/games/lib/xsok/Foo.help}.
Create a subdirectory {\tt /usr/games/lib/xsok/Foo}, and put in a file
{\tt definitions} and the files {\tt screen.??} for the levels.
For examples, see the subdirectories of the {\tt lib} directory in the source
distribution.
Edit the level files until you are satisfied. Then, you may concatenate
all the files in the {\tt /usr/games/lib/xsok/Foo} directory using the {\tt
combine} program, and create the more compact file {\tt
/usr/games/lib/xsok/Foo.def.gz}.
The level file simply is an ASCII representation of the floor types and
objects. The floor below an object is by default the first floor type which
occurs in the {\tt definitions} file. If you need another combination
(for example a box already on a target square), you have to dedicate
a new character for this combination, and add an {\tt ATOP} command in
the {\tt definitions} file. In Sokoban, a box is represented by a dollar
sign and a target square by a dot. For a box on a target square,
a star is used. The defining line in the Sokoban definitions file is
``{\tt ;ATOP *\$.}''.
The possible