Build an engine for a maze game

9

5

This is a follow up of Print a maze question. If you like this question, please add more maze generation algorithms ;).

For this task you'll have to implement a game engine for one player who must find the treasure in a maze and get out of the dungeon.

The engine starts by reading the maze from standard input followed by a line containing a . (dot) a file given as argument in the command line. Next the player @ is placed in a random location on the map. Then engine starts interacting with the player through standard io:

Commands from engine to player:

  • continue: Game not finished. The surroundings are printed followed by a .. Player is represented by @ character. Unobservable cells are represented by ?.
  • finished: Game finished. The number of steps is printed and the game stops.

Commands from player to engine:

  • north: Moves player up.
  • south: Moves player down.
  • west: Move player left.
  • east: Move player right.

Any invalid command (such as hitting a wall) from player is ignored, but still counted. You are free to define the surroundings to your liking.

  • Points for the shortest code.
  • Points for complex surroundings (e.g. print large regions and replace cells that are not visible by ?).
  • No points for code that doesn't respect the io format

Example:

In this example the surroundings is defined as the 3x3 cell with the player in the middle.

$ cat maze
+-+-+
  |#|
|   |
+---+
$ python engine.py maze
 |#
 @ 
---
.
east
|#|
 @|
--+
.
north
+-+
|@|
  |
.
south
|#|
 @|
--+
.
west
 |#
 @ 
---
.
west
  |
|@ 
+--
.
north
+-+
 @|
|  
.
west
finished
7

Alexandru

Posted 2011-02-02T12:17:11.147

Reputation: 5 485

I was thinking about trying this one---maybe as a curses exercise---but I find the rigid specifications for the interaction environment unappealing (I mean, why "continue" and not just a prompt?!? Why 4--5 character directions; I want to play nethack, man!) and the "both parts on the standard input" requirement to be nonsensical. – dmckee --- ex-moderator kitten – 2011-02-13T02:58:48.627

@dmckee: It easier to interact with an AI player. I will change to read the maze from file. – Alexandru – 2011-02-13T11:10:43.777

This blog has excellent articles on maze generation using various and mixed algorithms

http://weblog.jamisbuck.org/

Check out the growing tree algorithm in-particular

http://weblog.jamisbuck.org/2011/1/27/maze-generation-growing-tree-algorithm

– Dve – 2011-02-04T03:28:32.030

@Alexandru: What are we using to generate our mazes? Can we use other peoples maze algorithms (obviously with due credit)? Or must we complete your first assignment? – snmcdonald – 2011-02-02T15:06:49.980

@snmcdonald: Fixed typo. Use other people's mazes. Remember that the engine reads the maze from standard input. – Alexandru – 2011-02-02T15:07:02.787

I'm confused as to how both the maze and the user interaction come from standard input. Is the user supposed to type in his maze and then solve it? Kinda defeats the purpose of only showing a portion of the maze... – Keith Randall – 2011-02-04T04:22:03.900

You can build an app (this task is left for another question) on top of it to separate maze input from commands input. – Alexandru – 2011-02-04T11:46:00.680

Answers

7

C99, 771 characters

#include <ncurses.h>
#include <string.h>
#define MIN(A,B) (A<B?A:B)
#define MAX(A,B) (A>B?A:B)
#define T(C,X,Y) case C:if((m[x+X][y+Y]==' ')||(m[x+X][y+Y]=='#'))x+=X,y+=Y;s++;break;
char m[24][81],M[24][81];int i,j,I=0,J,x,y,s=0;
int main(int c,char**v){FILE*f=fopen(v[1],"r");
for(I=0;fgets(m[I],80,f);I++)J=MAX(J,strlen(m[I]));
J--;f=fopen("/dev/random","r");do{x=fgetc(f)%I;y=fgetc(f)%J;}
while(m[x][y]!=' ');initscr();curs_set(0);do{
switch(c){T('e',0,1)T('n',-1,0)T('s',1,0)T('w',0,-1)}
for(i=MAX(0,x-1);i<MIN(x+2,I);i++)for(j=MAX(0,y-1);j<MIN(y+2,J);j++)M[i][j]=1;
for(i=0;i<I;i++)for(j=0;j<J;j++)mvaddch(i,j,M[i][j]?m[i][j]:'?');
mvaddch(x,y,'@');refresh();}while((m[x][y]!='#')&&(c=getch())!='q');
if(m[x][y]=='#')mvprintw(I,0,"Finished in %d steps!",s),getch();endwin();}

Requires and makes use of ncurses. Only one macroization for length, and the N and M macros are to replace the missing minimum and maximum opperators, and I don't think there is much more to do on that.

It assumes the input maze does not exceed 80 characters wide, and that the a maze filename has been passed on the command-line, and that the number of parameters is low enough that the initial value of c isn't a movement command.

  • Deviates from the standard in that it takes single character direction commands as the lowercase first letter of the ones suggested.

  • Does show unknown regions as '?'s.

More readable with comments:

#include <ncurses.h>
#include <string.h>

#define MIN(A,B) (A<B?A:B)/*unsafe,but short*/
#define MAX(A,B) (A>B?A:B)/*unsafe,but short*/
// #define MAX(A,B) ((_A=A)>(_B=B)?_A:_B) /* safe but verbose */
#define T(C,X,Y) case C:if((m[x+X][y+Y]==' ')||(m[x+X][y+Y]=='#'))x+=X,y+=Y;s++;break;
char m[24][81],M[24][81];/* [m]ap and [M]ask; NB:mask intialized by default */
int i,j, /* loop indicies over the map */
  I=0,J, /* limits of the map */
  x,y,   /* player position */
  s=0;   /* steps taken */
int main(int c,char**v){
  FILE*f=fopen(v[1],"r"); /* fragile, assumes that the argument is present */
  /* Read the input file */
  for(I=0;fgets(m[I],80,f);I++)J=MAX(J,strlen(m[I])); /* Read in the map */ 
  J--;
  /* note that I leak a file handle here */
  f=fopen("/dev/random","r");
  /* Find a open starting square */
  do{ 
    x=fgetc(f)%I; /* Poor numeric properties, but good enough for code golf */
    y=fgetc(f)%J;
  } while(m[x][y]!=' ');
  /* setup curses */
  initscr(); /* start curses */
  //  raw();     /* WARNING! intercepts C-c, C-s, C-z, etc...
  //          * but shorter than cbreak() 
  //          */
  curs_set(0); /* make the cursor invisible */
  /* main loop */
  do {
    switch(c){
      T('e',0,1)
      T('n',-1,0)
      T('s',1,0)
      T('w',0,-1)
    }
    /* Update the mask */
    for(i=MAX(0,x-1);i<MIN(x+2,I);i++)
      for(j=MAX(0,y-1);j<MIN(y+2,J);j++)
    M[i][j]=1;
    /* draw the maze as masked */
    for(i=0;i<I;i++)
      for(j=0;j<J;j++)
    mvaddch(i,j,M[i][j]?m[i][j]:'?');
    /* draw the player figure */
    mvaddch(x,y,'@');
    refresh(); /* Refresh the display */
  } while((m[x][y]!='#')&&(c=getch())!='q');
  if(m[x][y]=='#')mvprintw(I,0,"Finished in %d steps!",s),getch();
  endwin();
}

dmckee --- ex-moderator kitten

Posted 2011-02-02T12:17:11.147

Reputation: 2 726