1
0
vt100-games/Sokoban/sokoban.c

595 lines
16 KiB
C
Raw Normal View History

2019-10-24 15:10:47 +02:00
/* A fairly hacked-up version of sokoban, for CP/M.
* ported 95/7/28 by Russell Marks
*
* It compiles ok (with the usual warnings :-)) under Hitech C.
* All the source files were basically cat'ted together to make compliation
* less awkward.
*
* You'll need to patch the 'clear' and 'move' routines with your
* machine/terminal's clear screen and cursor move codes. (They're currently
* set for ZCN.) For very simple terminals, you may even be able to patch
* the binary directly and not bother recompiling.
*
* This version is for VT100 compatible terminals, patched by
* Anna Christina Nass <acn@acn.wtf>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define refresh()
#define printw printf
void clear()
{
printf("%c[2J%c[H",27,27);
}
void move(int y,int x)
{
printf("%c[%d;%dH",27,y+1,x+1);
}
short readscreen();
/**/
/* OBJECT: this typedef is used for internal and external representation */
/* of objects */
/**/
typedef struct {
char obj_intern; /* internal representation of the object */
char obj_display1; /* first display char for the object */
char obj_display2; /* second display char for the object */
short invers; /* if set to 1 the object will be shown invers */
} OBJECT;
/**/
/* You can now alter the definitions below.
/* Attention: Do not alter `obj_intern'. This would cause an error */
/* when reading the screenfiles */
/**/
static OBJECT
player = { '@', '*', '*', 0 },
playerstore = { '+', '*', '*', 1 },
store = { '.', '.', '.', 0 },
packet = { '$', '[', ']', 0 },
save = { '*', '<', '>', 1 },
ground = { ' ', ' ', ' ', 0 },
wall = { '#', '#', '#', 1 };
/*************************************************************************
********************** DO NOT CHANGE BELOW THIS LINE *********************
*************************************************************************/
#define MAXROW 20
#define MAXCOL 40
typedef struct {
short x, y;
} POS;
#define E_FOPENSCREEN 1
#define E_PLAYPOS1 2
#define E_ILLCHAR 3
#define E_PLAYPOS2 4
#define E_TOMUCHROWS 5
#define E_TOMUCHCOLS 6
#define E_ENDGAME 7
#define E_NOUSER 9
#define E_FOPENSAVE 10
#define E_WRITESAVE 11
#define E_STATSAVE 12
#define E_READSAVE 13
#define E_ALTERSAVE 14
#define E_SAVED 15
#define E_TOMUCHSE 16
#define E_FOPENSCORE 17
#define E_READSCORE 18
#define E_WRITESCORE 19
#define E_USAGE 20
#define E_ILLPASSWORD 21
#define E_LEVELTOOHIGH 22
#define E_NOSUPER 23
#define E_NOSAVEFILE 24
/* defining the types of move */
#define MOVE 1
#define PUSH 2
#define SAVE 3
#define UNSAVE 4
#define STOREMOVE 5
#define STOREPUSH 6
/* defines for control characters */
#define CNTL_L '\014'
#define CNTL_K '\013'
#define CNTL_H '\010'
#define CNTL_J '\012'
#define CNTL_R '\022'
#define CNTL_U '\025'
static POS tpos1, /* testpos1: 1 pos. over/under/left/right */
tpos2, /* testpos2: 2 pos. " */
lastppos, /* the last player position (for undo) */
last_p1, last_p2; /* last test positions (for undo) */
static char lppc, ltp1c, ltp2c; /* the char for the above pos. (for undo) */
static char action, lastaction;
/** For the temporary save **/
static char tmp_map[MAXROW+1][MAXCOL+1];
static short tmp_pushes, tmp_moves, tmp_savepack;
static POS tmp_ppos;
short scoring = 1;
short level, packets, savepack, moves, pushes, rows, cols;
short scorelevel, scoremoves, scorepushes;
char map[MAXROW+1][MAXCOL+1];
POS ppos;
char username[]="Player", *prgname;
short play() {
short c;
short ret;
short undolock = 1; /* locked for undo */
showscreen();
tmpsave();
ret = 0;
while( ret == 0) {
c=getch();
switch(c) { case '8': c = 'k'; break;
case '2': c = 'j'; break;
case '4': c = 'h'; break;
case '6': c = 'l'; break;
case '5': c = 'u'; break;
case 'E'&0x3f: c='k'; break;
case 'S'&0x3f: c='h'; break;
case 'D'&0x3f: c='l'; break;
case 'X'&0x3f: c='j'; break;
default: break; };
switch(c) {
case 'q': /* quit the game */
ret = E_ENDGAME;
break;
case CNTL_R: /* refresh the screen */
clear();
showscreen();
break;
case 'c': /* temporary save */
case 's':
tmpsave();
break;
case CNTL_U: /* reset to temporary save */
case 'r':
tmpreset();
undolock = 1;
showscreen();
break;
case 'U': /* undo this level */
moves = pushes = 0;
if( (ret = readscreen()) == 0) {
showscreen();
undolock = 1;
}
break;
case 'u': /* undo last move */
if( ! undolock) {
undomove();
undolock = 1;
}
break;
case 'k': /* up */
case 'K': /* run up */
case CNTL_K: /* run up, stop before object */
case 'j': /* down */
case 'J': /* run down */
case CNTL_J: /* run down, stop before object */
case 'l': /* right */
case 'L': /* run right */
case CNTL_L: /* run right, stop before object */
case 'h': /* left */
case 'H': /* run left */
case CNTL_H: /* run left, stop before object */
do {
if( (action = testmove( c)) != 0) {
lastaction = action;
lastppos.x = ppos.x; lastppos.y = ppos.y;
lppc = map[ppos.x][ppos.y];
last_p1.x = tpos1.x; last_p1.y = tpos1.y;
ltp1c = map[tpos1.x][tpos1.y];
last_p2.x = tpos2.x; last_p2.y = tpos2.y;
ltp2c = map[tpos2.x][tpos2.y];
domove( lastaction);
undolock = 0;
}
} while( (action != 0) && (! islower( c))
&& (packets != savepack));
break;
default: helpmessage(); break;
}
if( (ret == 0) && (packets == savepack)) {
scorelevel = level;
scoremoves = moves;
scorepushes = pushes;
break;
}
}
return( ret);
}
testmove( action)
register short action;
{
register short ret;
register char tc;
register short stop_at_object;
if( (stop_at_object = iscntrl( action))) action = action + 'A' - 1;
action = (isupper( action)) ? tolower( action) : action;
if( (action == 'k') || (action == 'j')) {
tpos1.x = (action == 'k') ? ppos.x-1 : ppos.x+1;
tpos2.x = (action == 'k') ? ppos.x-2 : ppos.x+2;
tpos1.y = tpos2.y = ppos.y;
}
else {
tpos1.y = (action == 'h') ? ppos.y-1 : ppos.y+1;
tpos2.y = (action == 'h') ? ppos.y-2 : ppos.y+2;
tpos1.x = tpos2.x = ppos.x;
}
tc = map[tpos1.x][tpos1.y];
if( (tc == packet.obj_intern) || (tc == save.obj_intern)) {
if( ! stop_at_object) {
if( map[tpos2.x][tpos2.y] == ground.obj_intern)
ret = (tc == save.obj_intern) ? UNSAVE : PUSH;
else if( map[tpos2.x][tpos2.y] == store.obj_intern)
ret = (tc == save.obj_intern) ? STOREPUSH : SAVE;
else ret = 0;
}
else ret = 0;
}
else if( tc == ground.obj_intern)
ret = MOVE;
else if( tc == store.obj_intern)
ret = STOREMOVE;
else ret = 0;
return( ret);
}
domove( moveaction)
register short moveaction;
{
map[ppos.x][ppos.y] = (map[ppos.x][ppos.y] == player.obj_intern)
? ground.obj_intern
: store.obj_intern;
switch( moveaction) {
case MOVE: map[tpos1.x][tpos1.y] = player.obj_intern; break;
case STOREMOVE: map[tpos1.x][tpos1.y] = playerstore.obj_intern; break;
case PUSH: map[tpos2.x][tpos2.y] = map[tpos1.x][tpos1.y];
map[tpos1.x][tpos1.y] = player.obj_intern;
pushes++; break;
case UNSAVE: map[tpos2.x][tpos2.y] = packet.obj_intern;
map[tpos1.x][tpos1.y] = playerstore.obj_intern;
pushes++; savepack--; break;
case SAVE: map[tpos2.x][tpos2.y] = save.obj_intern;
map[tpos1.x][tpos1.y] = player.obj_intern;
savepack++; pushes++; break;
case STOREPUSH: map[tpos2.x][tpos2.y] = save.obj_intern;
map[tpos1.x][tpos1.y] = playerstore.obj_intern;
pushes++; break;
}
moves++;
dispmoves(); disppushes(); dispsave();
mapchar( map[ppos.x][ppos.y], ppos.x, ppos.y);
mapchar( map[tpos1.x][tpos1.y], tpos1.x, tpos1.y);
mapchar( map[tpos2.x][tpos2.y], tpos2.x, tpos2.y);
move( MAXROW+1, 0);
refresh();
ppos.x = tpos1.x; ppos.y = tpos1.y;
}
undomove() {
map[lastppos.x][lastppos.y] = lppc;
map[last_p1.x][last_p1.y] = ltp1c;
map[last_p2.x][last_p2.y] = ltp2c;
ppos.x = lastppos.x; ppos.y = lastppos.y;
switch( lastaction) {
case MOVE: moves--; break;
case STOREMOVE: moves--; break;
case PUSH: moves--; pushes--; break;
case UNSAVE: moves--; pushes--; savepack++; break;
case SAVE: moves--; pushes--; savepack--; break;
case STOREPUSH: moves--; pushes--; break;
}
dispmoves(); disppushes(); dispsave();
mapchar( map[ppos.x][ppos.y], ppos.x, ppos.y);
mapchar( map[last_p1.x][last_p1.y], last_p1.x, last_p1.y);
mapchar( map[last_p2.x][last_p2.y], last_p2.x, last_p2.y);
move( MAXROW+1, 0);
refresh();
}
tmpsave() {
register short i, j;
for( i = 0; i < rows; i++) for( j = 0; j < cols; j++)
tmp_map[i][j] = map[i][j];
tmp_pushes = pushes;
tmp_moves = moves;
tmp_savepack = savepack;
tmp_ppos.x = ppos.x; tmp_ppos.y = ppos.y;
}
tmpreset() {
register short i, j;
for( i = 0; i < rows; i++) for( j = 0; j < cols; j++)
map[i][j] = tmp_map[i][j];
pushes = tmp_pushes;
moves = tmp_moves;
savepack = tmp_savepack;
ppos.x = tmp_ppos.x; ppos.y = tmp_ppos.y;
}
short readscreen() {
FILE *screen;
short j, c, f, ret = 0;
if( (screen = fopen( "soklevls.dat", "r")) == NULL)
ret = E_FOPENSCREEN;
else {
if(level>1) {
for(f=1;f<level;f++) {
while((c=getc(screen))!=12 && c!=EOF) ;
getc(screen); /* get the \n after */
}
}
packets = savepack = rows = j = cols = 0;
ppos.x = -1; ppos.y = -1;
while( (ret == 0) && ((c = getc( screen)) != 12) && c!=EOF) {
if( c == '\n') {
map[rows++][j] = '\0';
if( rows > MAXROW)
ret = E_TOMUCHROWS;
else {
if( j > cols) cols = j;
j = 0;
}
}
else if( (c == player.obj_intern) || (c == playerstore.obj_intern)) {
if( ppos.x != -1)
ret = E_PLAYPOS1;
else {
ppos.x = rows; ppos.y = j;
map[rows][j++] = c;
if( j > MAXCOL) ret = E_TOMUCHCOLS;
}
}
else if( (c == save.obj_intern) || (c == packet.obj_intern) ||
(c == wall.obj_intern) || (c == store.obj_intern) ||
(c == ground.obj_intern)) {
if( c == save.obj_intern) { savepack++; packets++; }
if( c == packet.obj_intern) packets++;
map[rows][j++] = c;
if( j > MAXCOL) ret = E_TOMUCHCOLS;
}
else ret = E_ILLCHAR;
}
fclose( screen);
if( (ret == 0) && (ppos.x == -1)) ret = E_PLAYPOS2;
}
return( ret);
}
showscreen() {
register short i, j;
clear();
for( i = 0; i < rows; i++)
for( j = 0; map[i][j] != '\0'; j++)
mapchar( map[i][j], i, j);
move( MAXROW, 0);
printw( "Level: Packets: Saved: Moves: Pushes:");
displevel();
disppackets();
dispsave();
dispmoves();
disppushes();
move( MAXROW+2,0);
refresh();
}
mapchar( c, i, j)
register char c;
register short i, j;
{
OBJECT *obj, *get_obj_adr();
register short offset_row = 0; /*(MAXROW - rows) / 2;*/
register short offset_col = MAXCOL - cols;
obj = get_obj_adr( c);
/* if( obj->invers) standout();*/
move( i + offset_row, 2*j + offset_col);
printw( "%c%c", obj ->obj_display1, obj ->obj_display2);
/* if( obj->invers) standend();*/
}
OBJECT *get_obj_adr( c)
register char c;
{
register OBJECT *ret;
if( c == player.obj_intern) ret = &player;
else if( c == playerstore.obj_intern) ret = &playerstore;
else if( c == store.obj_intern) ret = &store;
else if( c == save.obj_intern) ret = &save;
else if( c == packet.obj_intern) ret = &packet;
else if( c == wall.obj_intern) ret = &wall;
else if( c == ground.obj_intern) ret = &ground;
else ret = &ground;
return( ret);
}
displevel() {
move( MAXROW, 7); printw( "%3d", level);
}
disppackets() {
move( MAXROW, 21); printw( "%3d", packets);
}
dispsave() {
move( MAXROW, 33); printw( "%3d", savepack);
}
dispmoves() {
move( MAXROW, 45); printw( "%5d", moves);
}
disppushes() {
move( MAXROW, 59); printw( "%5d", pushes);
}
static short optrestore = 0,
optlevel = 1;
helpmessage() {
#if 0
move( MAXROW+2, 0);
printw( "Press ? for help.");
refresh();
sleep( 1);
move( MAXROW+2, 0); deleteln();
refresh();
#endif
}
showhelp() {
}
main( argc, argv)
short argc;
char *argv[];
{
short ret;
scorelevel = 0;
moves = pushes = packets = savepack = 0;
if( (prgname = strrchr( argv[0], '/')) == NULL)
prgname = argv[0];
else prgname++;
{
if( (ret = checkcmdline( argc, argv)) == 0) {
level=optlevel;
}
}
ret = gameloop();
errmess( ret);
}
checkcmdline( argc, argv)
short argc;
char *argv[];
{
short ret = 0;
if(argc==2) {
if( (optlevel = atoi(argv[1])) == 0)
ret = E_USAGE;
}
return( ret);
}
gameloop() {
short ret = 0;
/* initscr(); cbreak(); noecho();*/
if( ! optrestore) ret = readscreen();
while( ret == 0) {
if( (ret = play()) == 0) {
level++;
moves = pushes = packets = savepack = 0;
ret = readscreen();
}
}
clear(); refresh();
/* nocbreak(); echo(); endwin();*/
return( ret);
}
char *message[] = {
"illegal error number",
"cannot open screen file",
"more than one player position in screen file",
"illegal char in screen file",
"no player position in screenfile",
"too much rows in screen file",
"too much columns in screenfile",
"quit the game",
NULL, /* errmessage deleted */
"cannot get your username",
"cannot open savefile",
"error writing to savefile",
"cannot stat savefile",
"error reading savefile",
"cannot restore, your savefile has been altered",
"game saved",
"too much users in score table",
"cannot open score file",
"error reading scorefile",
"error writing scorefile",
"illegal command line syntax",
"illegal password",
"level number too big in command line",
"only superuser is allowed to make a new score table",
"cannot find file to restore"
};
errmess( ret)
register short ret;
{
if( ret != E_ENDGAME) {
fprintf( stderr, "%s: ", prgname);
switch( ret) {
case E_FOPENSCREEN: case E_PLAYPOS1: case E_ILLCHAR:
case E_PLAYPOS2: case E_TOMUCHROWS: case E_TOMUCHCOLS:
case E_ENDGAME: case E_NOUSER:
case E_FOPENSAVE: case E_WRITESAVE: case E_STATSAVE:
case E_READSAVE: case E_ALTERSAVE: case E_SAVED:
case E_TOMUCHSE: case E_FOPENSCORE: case E_READSCORE:
case E_WRITESCORE: case E_USAGE: case E_ILLPASSWORD:
case E_LEVELTOOHIGH: case E_NOSUPER: case E_NOSAVEFILE:
fprintf( stderr, "%s\n", message[ret]);
break;
default: fprintf( stderr, "%s\n", message[0]);
break;
}
if( ret == E_USAGE) usage();
}
}
usage() {
fprintf( stderr, "Usage: %s [start_level]", prgname);
}