This commit is contained in:
Gwenhael Le Moine 2011-07-03 22:39:58 +02:00
parent 39e7222cf1
commit 02795db58a

438
star.c
View file

@ -262,28 +262,28 @@ char *levels[] = { "################" /* 0 */
#define LEVEL_WIDTH 16 #define LEVEL_WIDTH 16
enum { /* color names for ncurses */ enum { /* color names for ncurses */
color_BALL, color_BALL,
color_CUBE, color_CUBE,
color_VOID, color_VOID,
color_GIFT, color_GIFT,
color_WALL, color_WALL,
color_BALL_SELECTED, color_BALL_SELECTED,
color_CUBE_SELECTED, color_CUBE_SELECTED,
}; };
typedef enum { /* characters used to design levels */ typedef enum { /* characters used to design levels */
WALL = '#', WALL = '#',
BALL = '@', BALL = '@',
CUBE = 'H', CUBE = 'H',
VOID = ' ', VOID = ' ',
GIFT = 'x' GIFT = 'x'
} cell; } cell;
typedef enum { /* values aren't strictly necessary but useful for unit-testing */ typedef enum { /* values aren't strictly necessary but useful for unit-testing */
UP = 'u', UP = 'u',
DOWN = 'd', DOWN = 'd',
LEFT = 'l', LEFT = 'l',
RIGHT = 'r' RIGHT = 'r'
} direction; } direction;
struct options { /* store how the game is started (controlled by --arguments) */ struct options { /* store how the game is started (controlled by --arguments) */
@ -292,197 +292,197 @@ struct options { /* store how the game is started (controlled by
}; };
struct state { /* current state of the game at an instant T */ struct state { /* current state of the game at an instant T */
char board[ LEVEL_HEIGHT * LEVEL_WIDTH ]; char board[ LEVEL_HEIGHT * LEVEL_WIDTH ];
char moving; char moving;
int moves; int moves;
int level; int level;
int nb_levels; int nb_levels;
}; };
/* Count how many gifts are present in the current level /* Count how many gifts are present in the current level
*/ */
int count_gifts( struct state *s ) int count_gifts( struct state *s )
{ {
int i, n = 0; int i, n = 0;
for( i = 0 ; i < LEVEL_HEIGHT * LEVEL_WIDTH ; i++ ) { for( i = 0 ; i < LEVEL_HEIGHT * LEVEL_WIDTH ; i++ ) {
if ( s->board[ i ] == GIFT ) { if ( s->board[ i ] == GIFT ) {
n++; n++;
} }
} }
return n; return n;
} }
/* Write the position of the currently moving actor into pos /* Write the position of the currently moving actor into pos
*/ */
void get_pos( struct state *s, int* pos ) void get_pos( struct state *s, int* pos )
{ {
int p; int p;
p = (int)( strchr( s->board, s->moving ) - s->board ); p = (int)( strchr( s->board, s->moving ) - s->board );
pos[ 1 ] = p / LEVEL_WIDTH; pos[ 1 ] = p / LEVEL_WIDTH;
pos[ 0 ] = p - ( pos[ 1 ] * LEVEL_WIDTH ); pos[ 0 ] = p - ( pos[ 1 ] * LEVEL_WIDTH );
} }
/* Returns the content of cell( x, y ) of the current level /* Returns the content of cell( x, y ) of the current level
*/ */
char get_cell( struct state *s, int x, int y ) char get_cell( struct state *s, int x, int y )
{ {
return s->board[ y * LEVEL_WIDTH + x ]; return s->board[ y * LEVEL_WIDTH + x ];
} }
/* Set cell( x, y ) of the current level to value /* Set cell( x, y ) of the current level to value
*/ */
void set_cell( struct state *s, int x, int y, cell value ) void set_cell( struct state *s, int x, int y, cell value )
{ {
s->board[ y * LEVEL_WIDTH + x ] = value; s->board[ y * LEVEL_WIDTH + x ] = value;
} }
/* Load level nb from levels as the current level /* Load level nb from levels as the current level
*/ */
void load_level( struct state *s, char* levels[], int nb ) void load_level( struct state *s, char* levels[], int nb )
{ {
strncpy( s->board, levels[ nb ], LEVEL_HEIGHT * LEVEL_WIDTH ); strncpy( s->board, levels[ nb ], LEVEL_HEIGHT * LEVEL_WIDTH );
s->level = nb; s->level = nb;
s->moving = BALL; s->moving = BALL;
s->moves = 0; s->moves = 0;
} }
/* Swicth the currently moving actor between BALL and CUBE /* Swicth the currently moving actor between BALL and CUBE
*/ */
void switch_actor( struct state *s ) void switch_actor( struct state *s )
{ {
s->moving = (s->moving == BALL) ? CUBE : BALL; s->moving = (s->moving == BALL) ? CUBE : BALL;
} }
/* Returns a 'boolean' indicating if the current level is finished (no gifts left) /* Returns a 'boolean' indicating if the current level is finished (no gifts left)
*/ */
int won_or_not( struct state *s ) int won_or_not( struct state *s )
{ {
return( count_gifts( s ) == 0 ); return( count_gifts( s ) == 0 );
} }
/* Move the current actor towards direction in the current level, /* Move the current actor towards direction in the current level,
eating gifts on the way if the moving actor is the ball eating gifts on the way if the moving actor is the ball
FIXME: more documentation FIXME: more documentation
*/ */
void make_a_move( struct state *s, direction where ) void make_a_move( struct state *s, direction where )
{ {
int dx = 0, dy = 0, tmpx, tmpy, *item_coord; int dx = 0, dy = 0, tmpx, tmpy, *item_coord;
item_coord = malloc( sizeof( int ) * 2 ); item_coord = malloc( sizeof( int ) * 2 );
get_pos( s, item_coord ); get_pos( s, item_coord );
tmpx = item_coord[ 0 ]; tmpx = item_coord[ 0 ];
tmpy = item_coord[ 1 ]; tmpy = item_coord[ 1 ];
switch( where ) { switch( where ) {
case UP: case UP:
dy--; dy--;
break; break;
case DOWN: case DOWN:
dy++; dy++;
break; break;
case LEFT: case LEFT:
dx--; dx--;
break; break;
case RIGHT: case RIGHT:
dx++; dx++;
break; break;
default: break; default: break;
} }
/* Calculating arrival coordinates */ /* Calculating arrival coordinates */
while ( /* Hairy conditions ahead */ while ( /* Hairy conditions ahead */
/* target cell is within level's boundaries */ /* target cell is within level's boundaries */
( ( tmpx + dx >= 0 ) && ( tmpx + dx < LEVEL_WIDTH ) ) && ( ( tmpx + dx >= 0 ) && ( tmpx + dx < LEVEL_WIDTH ) ) &&
( ( tmpy + dy >= 0 ) && ( tmpy + dy < LEVEL_HEIGHT ) ) && ( ( tmpy + dy >= 0 ) && ( tmpy + dy < LEVEL_HEIGHT ) ) &&
/* and target cell is empty */ /* and target cell is empty */
( get_cell( s, tmpx + dx, tmpy + dy ) == VOID ) ( get_cell( s, tmpx + dx, tmpy + dy ) == VOID )
/* or, in case the ball is moving, target cell can be a gift (which we'll eat) */ /* or, in case the ball is moving, target cell can be a gift (which we'll eat) */
|| ( s->moving == BALL && ( get_cell( s, tmpx + dx, tmpy + dy ) == GIFT ) ) || ( s->moving == BALL && ( get_cell( s, tmpx + dx, tmpy + dy ) == GIFT ) )
) )
{ {
tmpx += dx; tmpx += dx;
tmpy += dy; tmpy += dy;
if ( s->moving == BALL && get_cell( s, tmpx, tmpy ) == GIFT ) { if ( s->moving == BALL && get_cell( s, tmpx, tmpy ) == GIFT ) {
set_cell( s, tmpx, tmpy, VOID ); set_cell( s, tmpx, tmpy, VOID );
} }
} }
/* Moving to arrival coordinates */ /* Moving to arrival coordinates */
set_cell( s, item_coord[ 0 ], item_coord[ 1 ], VOID ); set_cell( s, item_coord[ 0 ], item_coord[ 1 ], VOID );
set_cell( s, tmpx, tmpy, s->moving ); set_cell( s, tmpx, tmpy, s->moving );
s->moves++; s->moves++;
free( item_coord ); free( item_coord );
} }
/* display the current level (with Ncurses) /* display the current level (with Ncurses)
*/ */
void display_level( struct state *s ) void display_level( struct state *s )
{ {
int i, j, *ball, *cube; int i, j, *ball, *cube;
for( i = 0 ; i < LEVEL_HEIGHT ; i++ ) { for( i = 0 ; i < LEVEL_HEIGHT ; i++ ) {
for( j = 0 ; j < LEVEL_WIDTH ; j++ ) { for( j = 0 ; j < LEVEL_WIDTH ; j++ ) {
switch( get_cell( s, j, i ) ) { switch( get_cell( s, j, i ) ) {
case WALL: case WALL:
attron( COLOR_PAIR( color_WALL )); attron( COLOR_PAIR( color_WALL ));
mvprintw( i+1, j*2, "##" ); mvprintw( i+1, j*2, "##" );
attroff( COLOR_PAIR( color_WALL )); attroff( COLOR_PAIR( color_WALL ));
break; break;
case VOID: case VOID:
attron( COLOR_PAIR( color_VOID )); attron( COLOR_PAIR( color_VOID ));
mvprintw( i+1, j*2, " " ); mvprintw( i+1, j*2, " " );
attroff( COLOR_PAIR( color_VOID )); attroff( COLOR_PAIR( color_VOID ));
break; break;
case BALL: case BALL:
if ( s->moving == BALL ) { if ( s->moving == BALL ) {
attron( A_BOLD ); attron( A_BOLD );
attron( COLOR_PAIR( color_BALL_SELECTED )); attron( COLOR_PAIR( color_BALL_SELECTED ));
} }
else { else {
attron( COLOR_PAIR( color_BALL )); attron( COLOR_PAIR( color_BALL ));
} }
mvprintw( i+1, j*2, "()" ); mvprintw( i+1, j*2, "()" );
if ( s->moving == BALL ) { if ( s->moving == BALL ) {
attroff( A_BOLD ); attroff( A_BOLD );
} }
break; break;
case CUBE: case CUBE:
if ( s->moving == CUBE ) { if ( s->moving == CUBE ) {
attron( A_BOLD ); attron( A_BOLD );
attron( COLOR_PAIR( color_BALL_SELECTED )); attron( COLOR_PAIR( color_BALL_SELECTED ));
} }
else { else {
attron( COLOR_PAIR( color_BALL )); attron( COLOR_PAIR( color_BALL ));
} }
mvprintw( i+1, j*2, "[]" ); mvprintw( i+1, j*2, "[]" );
if ( s->moving == CUBE ) { if ( s->moving == CUBE ) {
attroff( A_BOLD ); attroff( A_BOLD );
attroff( COLOR_PAIR( color_CUBE_SELECTED )); attroff( COLOR_PAIR( color_CUBE_SELECTED ));
} }
else { else {
attroff( COLOR_PAIR( color_CUBE )); attroff( COLOR_PAIR( color_CUBE ));
} }
break; break;
case GIFT: case GIFT:
attron( COLOR_PAIR( color_GIFT )); attron( COLOR_PAIR( color_GIFT ));
mvprintw( i+1, j*2, "<>" ); mvprintw( i+1, j*2, "<>" );
attroff( COLOR_PAIR( color_GIFT )); attroff( COLOR_PAIR( color_GIFT ));
break; break;
default: break; /* ignore newlines */ default: break; /* ignore newlines */
} }
} }
} }
mvprintw( 1, 35, "level %i of %i", s->level + 1, s->nb_levels ); mvprintw( 1, 35, "level %i of %i", s->level + 1, s->nb_levels );
mvprintw( 2, 35, "%i gifts left", count_gifts( s ) ); mvprintw( 2, 35, "%i gifts left", count_gifts( s ) );
mvprintw( 3, 35, "%i moves made", s->moves ); mvprintw( 3, 35, "%i moves made", s->moves );
refresh(); refresh();
} }
/* Parse the --arguments, if any, to populate options /* Parse the --arguments, if any, to populate options
@ -550,95 +550,95 @@ int parse_args( int argc, char* argv[], struct options *o, struct state *s )
Initialize Ncurses. Initialize Ncurses.
Load the first level. Load the first level.
Enter the loop where it read the keys to play or 'q' or 'Q' to quit. Enter the loop where it read the keys to play or 'q' or 'Q' to quit.
*/ */
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )
{ {
int key; int key;
struct state *s = malloc( sizeof( struct state ) ); struct state *s = malloc( sizeof( struct state ) );
struct options *o = malloc( sizeof( struct options ) ); struct options *o = malloc( sizeof( struct options ) );
/* trick to count how many levels we have */ /* trick to count how many levels we have */
s->nb_levels = sizeof( levels ) / sizeof( levels[ 0 ] ); s->nb_levels = sizeof( levels ) / sizeof( levels[ 0 ] );
o->starting_level = 0; o->starting_level = 0;
o->black_and_white = 0; o->black_and_white = 0;
parse_args( argc, argv, o, s ); /* not caring about return value here, it's treated inside the function */ parse_args( argc, argv, o, s ); /* not caring about return value here, it's treated inside the function */
/* ncurses */ /* ncurses */
WINDOW *w_main = initscr( ); /* why can't stdscr be used transparently? */ WINDOW *w_main = initscr( ); /* why can't stdscr be used transparently? */
cbreak(); cbreak();
noecho(); noecho();
nonl(); nonl();
intrflush( w_main, FALSE ); intrflush( w_main, FALSE );
keypad( w_main, TRUE ); keypad( w_main, TRUE );
if ( ( ! o->black_and_white ) && has_colors( ) == TRUE ) { if ( ( ! o->black_and_white ) && has_colors( ) == TRUE ) {
start_color( ); start_color( );
init_pair( color_CUBE, COLOR_RED, COLOR_BLACK ); init_pair( color_CUBE, COLOR_RED, COLOR_BLACK );
init_pair( color_BALL, COLOR_BLUE, COLOR_BLACK ); init_pair( color_BALL, COLOR_BLUE, COLOR_BLACK );
init_pair( color_GIFT, COLOR_YELLOW, COLOR_BLACK ); init_pair( color_GIFT, COLOR_YELLOW, COLOR_BLACK );
init_pair( color_WALL, COLOR_WHITE, COLOR_WHITE ); init_pair( color_WALL, COLOR_WHITE, COLOR_WHITE );
init_pair( color_VOID, COLOR_BLACK, COLOR_BLACK ); init_pair( color_VOID, COLOR_BLACK, COLOR_BLACK );
init_pair( color_CUBE_SELECTED, COLOR_RED, COLOR_YELLOW ); init_pair( color_CUBE_SELECTED, COLOR_RED, COLOR_YELLOW );
init_pair( color_BALL_SELECTED, COLOR_BLUE, COLOR_YELLOW ); init_pair( color_BALL_SELECTED, COLOR_BLUE, COLOR_YELLOW );
} }
/* load the first level to start the loop in a correct state */ /* load the first level to start the loop in a correct state */
load_level( s, levels, o->starting_level ); load_level( s, levels, o->starting_level );
do { do {
if ( won_or_not( s ) ) { if ( won_or_not( s ) ) {
if ( s->level < s->nb_levels - 1 ) { if ( s->level < s->nb_levels - 1 ) {
load_level( s, levels, s->level + 1 ); load_level( s, levels, s->level + 1 );
} }
else { else {
// TODO:Damn it you finished the whole game!! // TODO:Damn it you finished the whole game!!
} }
} }
display_level( s ); display_level( s );
key = getch(); key = getch();
switch( key ) { switch( key ) {
case KEY_UP: case KEY_UP:
make_a_move( s, UP ); make_a_move( s, UP );
break; break;
case KEY_DOWN: case KEY_DOWN:
make_a_move( s, DOWN ); make_a_move( s, DOWN );
break; break;
case KEY_LEFT: case KEY_LEFT:
make_a_move( s, LEFT ); make_a_move( s, LEFT );
break; break;
case KEY_RIGHT: case KEY_RIGHT:
make_a_move( s, RIGHT ); make_a_move( s, RIGHT );
break; break;
case ' ': case ' ':
switch_actor( s ); switch_actor( s );
break; break;
case 'n': case 'n':
if ( s->level < s->nb_levels - 1 ) { if ( s->level < s->nb_levels - 1 ) {
load_level( s, levels, s->level + 1 ); load_level( s, levels, s->level + 1 );
} }
break; break;
case 'p': case 'p':
if ( s->level > 0 ) { if ( s->level > 0 ) {
load_level( s, levels, s->level - 1 ); load_level( s, levels, s->level - 1 );
} }
break; break;
case 'r': case 'r':
load_level( s, levels, s->level ); load_level( s, levels, s->level );
break; break;
default: default:
break; break;
} }
} while( ( s->level < s->nb_levels ) && (( key != 'q' ) && ( key != 'Q' )) ); } while( ( s->level < s->nb_levels ) && (( key != 'q' ) && ( key != 'Q' )) );
free( s ); free( s );
free( o ); free( o );
/* Clean ncurses mess */ /* Clean ncurses mess */
echo(); echo();
nocbreak(); nocbreak();
endwin(); endwin();
return 0; return 0;
} }