home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
games
/
volume6
/
puzzle15-2
/
puzzle15.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-03-15
|
23KB
|
900 lines
/*
* This program simulates a simple toy with many many variations.
*
* Compile with -DARROWKEYS if your curses library understands about
* arrow keys (and your keyboards have them).
* Compile with -DNOSIGNAL if your machine does not support the signal
* function to catch ctrl-C.
* Compile with "-DDIECHAR='<somechar>'" if you would like the program
* to exit gracefully when <somechar> is typed. All the quotes in and
* around this argument are necessary. The double quotes prevent the
* shell from stripping the single quotes. Instead of '<somechar' you
* can also type the ascii value of the key. If you do so, the double
* quotes are not needed.
*
* The copyright message is there to prevent uncontrolled spreading of
* a zillion different versions.
*
* You can mail your improvements and suggestions to me. I may include
* them in a future version of the program.
*
* I do not guarantee the fitness of this program for any purpose.
* USE IT AT YOUR OWN RISK.
*
* Peter Knoppers - knop@duteca.UUCP
* Bilderdijkhof 59
* 2624 ZG Delft
* The Netherlands
*
*
* The following lines are put in the compiled program by the C compiler
* where they can be found by programs such as (Bsd) strings.
*/
char *this_is = "puzzle15 V2.0 Feb 24 1989";
char *author0 = "(C) Copyright 1985, 1988, 1989 Peter Knoppers";
char *author1 = "Arrow keys added by Paul Lew 1988";
char *author2 = "Clock added by Bo Kullmar 1988";
char *author3 = "Select-tile method suggested by Larry Hastings 1988";
char *authorx = "Additions merged by Peter Knoppers";
char *release1 = "Permission to use and redistribute unmodified ";
char *release2 = "copies with source is granted to everyone";
char *release3 = "Distribution of modified copies is NOT allowed";
#include <curses.h>
#ifndef NOSIGNAL
#include <signal.h>
#endif
#include <ctype.h>
#ifndef DIECHAR
#define DIECHAR 999 /* any non-ASCII value will do */
#endif
#define DEFSIZE 4 /* default size of board */
#define BLANK 0
#define UNSPECIFIED (-1)
/* classification of arguments */
#define MOVEMETHOD 1
#define CURSORMETHOD 2
#define FACEMETHOD 3
/* magic values are required to be different, no other restrictions apply */
/* magic values for Movemethods */
#define MOVEBLANK 4
#define MOVETILE 5
#define SELECTTILE 6
/* magic values for Cursormethods */
#define VIKEYS 7
#ifdef ARROWKEYS
#define ARROWK 8
#endif
#define NUMBERKEYS 9
#define FACEKEYS 10
#define KEYS 11
/* magic values for Facemethods */
#define NUMBERFACES 12
#define ALPHAFACES 13
#define NUMBERALPHAFACES 14
#define ALPHANUMBERFACES 15
#define FACES 16
unsigned long Time_start;
int Movecnt = 0;
char *malloc (); /* to satisfy lint */
long time ();
int Sizex = UNSPECIFIED; /* x-size of the board */
int Sizey = UNSPECIFIED; /* y-size of the board */
int Size2; /* total surface of the board */
int Movemethod = UNSPECIFIED;
int Cursormethod = UNSPECIFIED;
int Facemethod = UNSPECIFIED;
char *Movename; /* name of the selected Movemethod */
char *Cursorname; /* name of the selected Cursormethod */
char *Cursorkeys; /* the user-defined Cursorkeys */
char *Facename; /* name of the selected Facemethod */
char *Facechars; /* the user-defined tile-faces */
int Up; /* key-codes for cursor movements */
int Down;
int Left;
int Right;
int Newpos; /* new position for empty field */
char *Myname; /* name used to invoke this program */
/*
* All possible keyword arguments are listed in the Argopts table.
* This is done in order to
* - ensure that every keyword appears at only one place in the program
* (this makes the program easier to modify/maintain)
* - put most of the knowledge about incompatible arguments in one place
* instead of scattering it all over the program.
* - simplify the argument parser
* (I haven't been completely succesfull in this respect...)
*/
struct argtablemember
{
char *a_name; /* what the user types */
int a_type; /* what kind of spec. it is */
int a_magic; /* magic value */
} Argopts[] =
{
{
"moveblank", MOVEMETHOD, MOVEBLANK
},
{
"movetile", MOVEMETHOD, MOVETILE
},
{ /* SELECTTILE must be before FACEKEYS */
"selecttile", MOVEMETHOD, SELECTTILE
},
{
"vikeys", CURSORMETHOD, VIKEYS
},
#ifdef ARROWKEYS
{
"arrowkeys", CURSORMETHOD, ARROWK
},
#endif
{
"numberkeys", CURSORMETHOD, NUMBERKEYS
},
{
"facekeys", CURSORMETHOD, FACEKEYS
},
{
"keys", CURSORMETHOD, KEYS
},
{
"numberfaces", FACEMETHOD, NUMBERFACES
},
{
"alphafaces", FACEMETHOD, ALPHAFACES
},
{
"numberalphafaces", FACEMETHOD, NUMBERALPHAFACES
},
{
"alphanumberfaces", FACEMETHOD, ALPHANUMBERFACES
},
{
"faces", FACEMETHOD, FACES
},
{
(char *) 0, 0, 0
}
};
die () /* nice exit on ctrl-C */
{
#ifndef NOSIGNAL
signal (SIGINT, SIG_IGN); /* ignore ctrl-C */
#endif
mvprintw (LINES - 1, 0, "Goodbye. ");
clrtoeol ();
refresh ();
endwin (); /* shutdown curses, restore ttymode */
exit (0);
}
main (argc, argv) /* scan the arguments, call game() */
char **argv;
{
struct argtablemember *atmp;/* to walk Argopts table */
int swap; /* to revers directions */
Myname = *argv; /* save this for usage () */
/*
* scan the arguments
*/
while (*++argv != ((char *) 0))
{ /* walk argument list */
for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
{ /* scan the keyword list */
if (strcmp (*argv, atmp -> a_name) == 0)
{ /* found keyword */
switch (atmp -> a_type)
{
case MOVEMETHOD:
if (Movemethod != UNSPECIFIED)
conflict (Movename, atmp -> a_name);
Movename = atmp -> a_name;
Movemethod = atmp -> a_magic;
break;
case CURSORMETHOD:
if (Cursormethod != UNSPECIFIED)
conflict (Cursorname, atmp -> a_name);
Cursorname = atmp -> a_name;
Cursormethod = atmp -> a_magic;
if (atmp -> a_magic == KEYS)
{
if ((Cursorkeys = *++argv) == (char *) 0)
novalue (*--argv);/* never returns */
if (strlen (Cursorkeys) != 4)
{
printf ("%s need 4 cursorkeys\n",
Cursorname);
usage ();
}
}
break;
case FACEMETHOD:
if (Facemethod != UNSPECIFIED)
conflict (Facename, atmp -> a_name);
Facename = atmp -> a_name;
Facemethod = atmp -> a_magic;
if (atmp -> a_magic == FACES)
if ((Facechars = *++argv) == (char *) 0)
novalue (*--argv);
break;
default:
printf ("aargh: bad switch (atmp -> a_type = %d)\n",
atmp -> a_type);
exit (1);
}
break;
}
}
if (atmp -> a_name == (char *) 0)/* not a keyword */
if (isdigit (**argv) && (Sizex == UNSPECIFIED))
{ /* it's a boardsize specification */
if (sscanf (*argv, "%dx%d", &Sizex, &Sizey) != 2)
if (sscanf (*argv, "%d", &Sizex) == 1)
Sizey = Sizex;
else
{
printf ("bad argument %s\n", *argv);
usage ();
}
}
else /* it's garbage */
{
printf ("bad argument %s\n", *argv);
usage ();
}
}
/*
* insert default values
*/
if (Sizex == UNSPECIFIED)
Sizex = Sizey = DEFSIZE;
if (Cursormethod == UNSPECIFIED)
{ /* find first CURSORMETHOD in Argopts */
for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
if (atmp -> a_type == CURSORMETHOD)
break;
if (atmp -> a_name == (char *) 0)
{
printf ("aargh: can't find default Cursormethod\n");
exit (1);
}
Cursormethod = atmp -> a_magic;
Cursorname = atmp -> a_name;
}
if (Facemethod == UNSPECIFIED)
{ /* find first FACEMETHOD in Argopts */
for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
if (atmp -> a_type == FACEMETHOD)
break;
if (atmp -> a_name == (char *) 0)
{
printf ("aargh: can't find default Facemethod\n");
exit (1);
}
Facemethod = atmp -> a_magic;
Facename = atmp -> a_name;
}
if (Movemethod == UNSPECIFIED)
if (Cursormethod == FACEKEYS)
Movemethod = SELECTTILE;/* by implication */
else
{ /* find first MOVEMETHOD in Argopts */
for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
if (atmp -> a_type == MOVEMETHOD)
break;
if (atmp -> a_name == (char *) 0)
{
printf ("aargh: can't find default Movemethod\n");
exit (1);
}
Movemethod = atmp -> a_magic;
Movename = atmp -> a_name;
}
if ((Cursormethod == FACEKEYS) && (Movemethod != SELECTTILE))
conflict (Cursorname, Movename);/* does not return */
switch (Cursormethod)
{
case VIKEYS:
Up = 'k';
Down = 'j';
Left = 'h';
Right = 'l';
break;
#ifdef ARROWKEYS
case ARROWK:
Up = KEY_UP;
Down = KEY_DOWN;
Left = KEY_LEFT;
Right = KEY_RIGHT;
break;
#endif
case NUMBERKEYS:
Up = '8';
Down = '2';
Left = '4';
Right = '6';
break;
case FACEKEYS:
Up = 1000; /* values must not correspond to any ASCII */
Down = 1001; /* value, otherwise no restrictions */
Left = 1002;
Right = 1003;
break;
case KEYS:
Up = Cursorkeys[0];
Down = Cursorkeys[1];
Left = Cursorkeys[2];
Right = Cursorkeys[3];
break;
default:
printf ("aargh: bad switch (Cursormethod = %d)\n",
Cursormethod);
exit (1);
}
if (Movemethod == MOVETILE)
{ /* revers cursor directions */
swap = Up;
Up = Down;
Down = swap;
swap = Left;
Left = Right;
Right = swap;
}
Size2 = Sizex * Sizey; /* surface area of the board */
switch (Facemethod)
{
case NUMBERFACES:
if (Size2 <= 10) /* tiles can be labeled with 1 char */
{
Facechars = "123456789";
Facemethod = NUMBERALPHAFACES;
}
break;
case ALPHAFACES:
Facechars = "abcdefghijklmnopqrstuvwxyz";
break;
case NUMBERALPHAFACES:
Facechars = "0123456789abcdefghijklmnopqrstuvwxyz";
break;
case ALPHANUMBERFACES:
Facechars = "abcdefghijklmnopqrstuvwxyz0123456789";
break;
}
if ((Size2 - 1) > ((Facemethod == NUMBERFACES) ? 99 :
strlen (Facechars)))
{
printf ("sorry, too many tiles - not enough labels\n");
exit (0);
}
if ((Sizex < 2) || (Sizey < 2))
{
printf ("sorry, board is too small for a non-trivial game\n");
exit (0);
}
/*
* Initialize the curses screen handling functions
*/
#ifndef NOSIGNAL
signal (SIGINT, SIG_IGN); /* protect this critical section */
#endif
initscr ();
crmode ();
noecho ();
#ifdef ARROWKEYS
keypad (stdscr, TRUE);
#endif
#ifndef NOSIGNAL
signal (SIGINT, die); /* die() knows how to restore ttymode */
#endif
/*
* From now on use die() to exit the program, otherwise the
* ttymode will not be restored.
*
* Now that curses has been initialized we can check whether the board
* fits on the screen.
*/
if (Sizex > (COLS - 1) / 5) /* COLS works only after initscr() */
{
mvprintw (LINES - 2, 0,
"sorry, your screen is not wide enough\n");
die ();
}
if (Sizey > (LINES - 4) / 2)
{
mvprintw (LINES - 2, 0,
"sorry, your screen is not tall enough\n");
die ();
}
game (); /* play the game */
mvprintw (LINES - 2, 0,
"You've reached the solution in %d moves.\n", Movecnt);
die (); /* restore ttymode and exit */
}
usage ()
{
int curtype = UNSPECIFIED;
int prevtype = UNSPECIFIED;
char *selecttilename = (char *) 0;
struct argtablemember *atmp;/* to walk Argopts table */
printf ("usage: %s [<width[x<height>]] %s",
Myname, "[movemethod] [cursormethod] [facemethod]\n");
for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
{
if (atmp -> a_magic == SELECTTILE)
selecttilename = atmp -> a_name;
if (atmp -> a_type != curtype)
{
curtype = atmp -> a_type;
if (curtype == MOVEMETHOD)
printf ("possible movemethods:\n");
if (curtype == CURSORMETHOD)
printf ("possible cursormethods:\n");
if (curtype == FACEMETHOD)
printf ("possible facemethods:\n");
}
switch (atmp -> a_magic)
{
case KEYS:
printf ("\t\t%s <up><down><left><right>", atmp -> a_name);
break;
case FACES:
printf ("\t\t%s <facecharacters>", atmp -> a_name);
break;
case FACEKEYS:
if (selecttilename == (char *) 0)
{
printf ("aargh: can't find string for SELECTTILE\n");
exit (1);
}
printf ("\t\t%-20.20s (implies movemethod %s)",
atmp -> a_name, selecttilename);
break;
default:
printf ("\t\t%-20.20s", atmp -> a_name);
break;
}
if (curtype != prevtype)
printf (" (this is default)\n");
else
printf ("\n");
prevtype = curtype;
}
exit (0);
}
conflict (word1, word2)
char *word1;
char *word2;
{
printf ("You may not specify both %s and %s\n", word1, word2);
usage ();
}
novalue (word)
char *word;
{
printf ("%s requires an argument\n", word);
usage ();
}
game () /* initialize board, execute moves */
{
register int i, j; /* generally used indices and counters */
int *board; /* pointer to malloc-ed board */
int empty; /* position of empty field */
int swap; /* used to swap two tiles */
int nswap = 0; /* to determine reachability */
int steps; /* number of tiles that move */
int unchanged = 0; /* used to indicate that board has changed */
int cursorx; /* to save cursorposition while */
int cursory; /* printing error messages */
int m; /* move, first character / direction */
int m2; /* second character of move */
/*
* Set up the board and shuffle the tiles
*/
board = (int *) malloc ((unsigned) Size2 * sizeof (int));
if (board == NULL)
{
printf ("aargh: malloc failed\n");
die ();
}
srand ((int) time ((long) 0));/* initialize random number generator */
for (i = 1; i < Size2; i++) /* put tiles on the board in their */
board[i - 1] = i; /* final positions */
for (i = Size2 - 2; i > 0; i--)/* permutate tiles */
{
j = rand () % i;
swap = board[i];
board[i] = board[j];
board[j] = swap;
}
/*
* Check that final position can be reached from current permutation
*/
for (i = 0; i < Size2 - 1; i++)
for (j = 0; j < i; j++)
if (board[j] > board[i])
nswap++;
if ((nswap % 2) != 0) /* this position is unreachable */
{ /* swap two adjacent tiles */
swap = board[1];
board[1] = board[0];
board[0] = swap;
}
empty = Size2 - 1; /* empty field starts in lower right */
board[empty] = BLANK; /* corner */
Newpos = empty;
Time_start = time ((long *) 0);/* start the clock */
while (1) /* until final position is reached */
{
if (unchanged == 0) /* the board must be (re-)printed */
{
printboard (board); /* also puts cursor at Newpos */
unchanged++; /* the board on the screen is up to date */
/*
* Check if final position is reached
*/
for (i = 0; i < Size2 - 1; i++)
if (board[i] != i + 1)
break; /* final position is not yet reached */
if (i == Size2 - 1) /* all tiles are in final positions */
return; /* game ends */
}
/*
* Let the user make a move
*/
m = getch ();
if (m == DIECHAR)
die ();
if (Movemethod == SELECTTILE)
{
if (Cursormethod == FACEKEYS)
{
if (Facemethod == NUMBERFACES)
{
if (!isdigit (m))
{
getyx (stdscr, cursory, cursorx);
mvprintw (LINES - 1, 0,
"use one of the keys");
for (i = 0; (i <= Size2) && (i < 10); i++)
printw (" %d", i);
clrtoeol ();
move (cursory, cursorx);
refresh ();
continue;
}
if ((m - '0') > (Size2 / 10))
m -= '0';
else /* we need a second digit */
{
m = (m - '0') * 10;
m2 = getch ();
if (m == DIECHAR)
die ();
if ((!isdigit (m2)) || (m + m2 - '0' >= Size2))
{
getyx (stdscr, cursory, cursorx);
mvprintw (LINES - 1, 0,
"use one of the keys");
for (i = 0; (i < Size2 % 10) && (i + m < Size2);
i++)
printw (" %d", i);
clrtoeol ();
move (cursory, cursorx);
refresh ();
continue;
}
m += m2 - '0';
}
/*
* find out where this tile is on the board
*/
for (Newpos = 0; Newpos < Size2; Newpos++)
if (board[Newpos] == m)
break;/* found tile */
if (Newpos == Size2)/* no tile with face m */
{
mvprintw (LINES - 2, 0,
"aargh: can't find tile %d on board\n", m);
die ();
}
}
else
{
/*
* Facemethod != NUMBERFACES
* This means that a single keystroke identifies the
* tile that is to be moved.
*/
for (Newpos = 0; Newpos < Size2; Newpos++)
if (board[Newpos] > 0)
if (Facechars[board[Newpos] - 1] == m)
break;/* found tile */
if (Newpos == Size2)
{
getyx (stdscr, cursory, cursorx);
mvprintw (LINES - 1, 0,
"use one of the keys ");
for (i = 0; (i < Size2 - 1) && (i < 30); i++)
printw ("%c", Facechars[i]);
if (i < Size2 - 1)
printw ("...");
clrtoeol ();
move (cursory, cursorx);
refresh ();
continue;
}
}
}
else /* Cursormethod != FACEKEYS */
{
if (m == Up)
{
if (Newpos >= Sizex)
Newpos -= Sizex;
unchanged = 0;/* board must be reprinted */
continue;
}
if (m == Down)
{
if (Newpos + Sizex < Size2)
Newpos += Sizex;
unchanged = 0;
continue;
}
if (m == Left)
{
if ((Newpos % Sizex) != 0)
Newpos--;
unchanged = 0;
continue;
}
if (m == Right)
{
if (((Newpos + 1) % Sizex) != 0)
Newpos++;
unchanged = 0;
continue;
}
/*
* If a key not in the set { Up, Down, Left, Right } was
* typed we fall through and try to move the empty field to
* Newpos.
*/
}
/*
* The user has indicated a new location for the empty field.
* The new position of the empty field in the array board is in
* Newpos.
* We must now check that the new position is on the same row
* or the same column as the current position and we must
* determine how many tiles must be moved.
*/
if (Newpos == empty)
continue; /* nothing changed */
steps = 0;
if (Newpos > empty)
{
if ((empty % Sizex + Newpos - empty) < Sizex)
{
m = Right;
steps = Newpos - empty;
}
else
if (((Newpos - empty) % Sizex) == 0)
{
m = Down;
steps = (Newpos - empty) / Sizex;
}
}
else
{
if (empty % Sizex + Newpos - empty >= 0)
{
m = Left;
steps = empty - Newpos;
}
else
if (((empty - Newpos) % Sizex) == 0)
{
m = Up;
steps = (empty - Newpos) / Sizex;
}
}
if (steps == 0)
{
getyx (stdscr, cursory, cursorx);
mvprintw (LINES - 1, 0,
"tile must be in same row as empty field\n");
move (cursory, cursorx);
refresh ();
continue;
}
}
else /* Movemethod is MOVEBLANK or MOVETILE */
steps = 1; /* one step per move */
/*
* m should now be one of the four directions, but it may be an
* illegal key. This can not happen if Movemethod == SELECTTILE.
*
* steps indicates how many tiles are to be moved
*/
if ((m != Up) && (m != Down) && (m != Left) && (m != Right))
{
getyx (stdscr, cursory, cursorx);
#ifdef ARROWKEYS
if (Cursormethod == ARROWK)
mvprintw (LINES - 1, 0,
"Use the arrow keys for up, down, left and right");
else
#endif
if (Movemethod == MOVETILE)
{
mvprintw (LINES - 1, 0,
"use %c for up, %c for down, ", Down, Up);
printw ("%c for left and %c for right", Right, Left);
}
else
{
mvprintw (LINES - 1, 0,
"use %c for up, %c for down, ", Up, Down);
printw ("%c for left and %c for right", Left, Right);
}
clrtoeol ();
move (cursory, cursorx);
refresh ();
continue;
}
/*
* m contains the direction to move
* steps contains the number of tiles to move
* Apply the move to the board.
*/
if (m == Up)
if (empty >= Sizex)
while (steps-- > 0)
{
board[empty] = board[empty - Sizex];
board[empty - Sizex] = BLANK;
empty -= Sizex;
Movecnt++;
}
if (m == Down)
if (empty + Sizex < Size2)
while (steps-- > 0)
{
board[empty] = board[empty + Sizex];
board[empty + Sizex] = BLANK;
empty += Sizex;
Movecnt++;
}
if (m == Left)
if ((empty % Sizex) != 0)
while (steps-- > 0)
{
board[empty] = board[empty - 1];
board[empty - 1] = BLANK;
empty--;
Movecnt++;
}
if (m == Right)
if (((empty + 1) % Sizex) != 0)
while (steps-- > 0)
{
board[empty] = board[empty + 1];
board[empty + 1] = BLANK;
empty++;
Movecnt++;
}
if (steps == 1) /* you ran into a wall */
{
getyx (stdscr, cursory, cursorx);
mvprintw (LINES - 1, 0,
"Your can't cross that wall\n");
clrtoeol ();
move (cursory, cursorx);
refresh ();
continue;
}
if (steps != -1) /* something is very wrong */
{
mvprintw (LINES - 2, 0,
"aargh: couldn't move enough tiles (steps = %d)\n",
steps);
die ();
}
Newpos = empty;
unchanged = 0; /* the board must be reprinted */
}
}
printboard (board)
int *board;
{
register int i, j;
int tilewidth;
unsigned long time_used;
unsigned minutes;
unsigned seconds;
unsigned long time_now;
tilewidth = ((Facemethod == NUMBERFACES) && (Size2 > 10)) ? 5 : 4;
mvprintw ((LINES - 4 - 2 * Sizey) / 2,
(COLS - 1 - tilewidth * Sizex) / 2,
"+"); /* print top edge of board */
for (j = 0; j < Sizex; j++)
if (tilewidth == 5)
printw ("----+");
else
printw ("---+");
for (i = 0; i < Sizey; i++)
{
mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 1,
(COLS - 1 - tilewidth * Sizex) / 2, "| ");
for (j = 0; j < Sizex; j++)
if (tilewidth == 5)
if (board[Sizex * i + j] != BLANK)
if ((Size2 > 9) && (Cursormethod == FACEKEYS) &&
(board[Sizex * i + j] <= Size2 / 10))
printw ("%02d | ", board[Sizex * i + j]);
else
printw ("%2d | ", board[Sizex * i + j]);
else
printw (" | ");
else
if (board[Sizex * i + j] != BLANK)
printw ("%c | ", Facechars[board[Sizex * i + j] - 1]);
else
printw (" | ");
mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 2,
(COLS - 1 - tilewidth * Sizex) / 2, "+");
for (j = 0; j < Sizex; j++)
if (tilewidth == 5)
printw ("----+");
else
printw ("---+");
}
mvprintw (LINES - 1, 0, "\n");/* erase error messages */
/*
* Update the clock
*/
time_now = time ((long *) 0);
time_used = time_now - Time_start;
minutes = time_used / 60;
seconds = time_used % 60;
mvprintw (LINES - 3, 0, "Time used: %02d min %02d sec Move: %d",
minutes, seconds, Movecnt);
/*
* Put cursor on the position indicated by Newpos
*/
move ((LINES - 4 - 2 * Sizey) / 2 + (Newpos / Sizex) * 2 + 1,
(COLS - 1 - tilewidth * Sizex) / 2 +
(Newpos % Sizex) * tilewidth + tilewidth / 2);
refresh (); /* put all this on the screen */
}