training

Code I wrote during training
git clone git://git.bitsmanent.org/training
Log | Files | Refs | README

worm.c (10501B)


      1 /* See LICENSE file for copyright and license details.
      2  *
      3  * The Hermit Worm - Commodore64 snake-like game.
      4  */ 
      5 
      6 #include <stdio.h>
      7 #include <curses.h>
      8 #include <time.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <unistd.h>
     12 
     13 #define HEAD 'o' /* normal head */
     14 #define WORM 'w' /* worm's body */
     15 #define EATH 'O' /* eating head */
     16 
     17 #define MINROW 21
     18 #define MINCOL 80
     19 
     20 #define LEVEAT 5 /* Food eated befor to increment the level */
     21 #define LEVMAX 9 /* Max speed (to preserve the refresh delay in auto-mode) */
     22 
     23 /* Recursive list */
     24 typedef struct list_t {
     25    int x;
     26    int y;
     27    struct list_t *next;
     28 } List;
     29 
     30 /* Routes */
     31 enum { R_UP = 1, R_LEFT, R_DOWN, R_RIGHT } route;
     32 
     33 /* function declarations */
     34 void newnode(List **listp, int y, int x);
     35 void wormshow(List *worm, char ch, char chhead);
     36 void moveworm(List **worm, int way);
     37 int isout(List *worm);
     38 int isbite(List *worm);
     39 int iseat(List *worm, int row, int col);
     40 void killworm(List **worm);
     41 
     42 /* function implementations */
     43 int
     44 main(int argc, char **argv) {
     45    int ch = 0, n, eating, trick = 0;
     46    int erow, ecol; /* eat position */
     47    int level, tricks, moves, score;
     48 
     49    WINDOW *wstat, *wpos, *wepos; /* Status and positions windows */
     50    List *worm = NULL, *current; /* THe LiST */
     51 
     52    int auto_mode = 0; /* Play without user interaction */
     53 
     54    level = tricks = moves = score = 0;
     55    srand( time(NULL) );
     56 
     57    /* check the flag for auto-mode */
     58    if( argc == 2 && argv[1][0] == '-' && argv[1][1] == 'a' )
     59       auto_mode = 1;
     60 
     61    initscr(); /* start of session */
     62    curs_set(0); /* hide the cursor */
     63    keypad(stdscr, TRUE); /* enable the function keys */
     64    cbreak();
     65    noecho();
     66    nodelay(stdscr, TRUE); /* unlock getch(3) */
     67 
     68    /* Check the terminal size */
     69    if( LINES < MINROW || COLS < MINCOL ) {
     70       endwin();
     71       printf("This game require a terminal size of %dx%d pixel.\n",
     72 	 MINROW, MINCOL);
     73       printf("Your terminal size is: %dx%d\n", LINES, COLS);
     74       return -1;
     75    }
     76 
     77    /* Create the windows */
     78    wstat = subwin(stdscr, 3, COLS - COLS / 3, 2, COLS / 6); /* status */
     79    wpos = subwin(stdscr, 3, 9, 2, 3); /* worm position */
     80    wepos = subwin(stdscr, 3, 9, 2, COLS - 9 - 3); /* eat position */
     81 
     82    /* Add the first position */
     83    newnode(&worm, 1 + rand() % (COLS - 2), 6 + rand() % (LINES - 7));
     84 
     85    /* start point for the eat */
     86    erow = 6 + rand() % (LINES - 7);
     87    ecol = 1 + rand() % (COLS - 2);
     88 
     89    /* choose a random route */
     90    route = 1 + rand() % 4;
     91 
     92    wormshow(worm, WORM, HEAD); /* put the first WORM */
     93    mvprintw(erow, ecol, "%d", n = 1); /* put the first eat */
     94 
     95    /* main loop */
     96    while( (auto_mode ? getch() : (ch = getch())) != 'q' ) {
     97       /*
     98        * Auto-mode play
     99        *
    100        * NOTE: the worm can to bite himself losing the
    101        *       game which will be auto-restarted again.
    102        * 
    103       */
    104       if( auto_mode ) {
    105 	 ch = 0; /* don't touch the user moves */
    106 
    107          /* choose the route, if needed */
    108 	 if( worm->x < erow ) {
    109 	    if( route != R_DOWN )
    110 	       ch = 'j';
    111 	 }
    112 	 else if( worm->x > erow ) {
    113 	    if( route != R_UP )
    114 	       ch = 'k';
    115 	 }
    116 	 else if( worm->y < ecol ) {
    117 	    if( route != R_RIGHT )
    118 	       ch = 'l';
    119 	 }
    120 	 else {
    121 	    if( route != R_LEFT )
    122 	       ch = 'h';
    123 	 }
    124       }
    125 
    126       switch(ch) {
    127 	 case 'h':
    128 	 case KEY_LEFT:
    129             route = R_LEFT; 
    130 	    ++moves;
    131 	    break;
    132 	 case 'j':
    133 	 case KEY_DOWN:
    134             route = R_DOWN;
    135 	    ++moves;
    136 	    break;
    137 	 case 'k':
    138 	 case KEY_UP:
    139             route = R_UP;
    140 	    ++moves;
    141 	    break;
    142 	 case 'l':
    143 	 case KEY_RIGHT:
    144             route = R_RIGHT;
    145 	    ++moves;
    146 	    break;
    147 	 case 'p':
    148 	 case ' ': /* space bar */
    149 	    /* Pause */
    150 	    nodelay(stdscr, FALSE); /* lock the getch() */
    151 
    152             mvprintw(LINES / 2, COLS / 2, "Pause!");
    153 	    while( (ch = getch()) ) {
    154 	       if( ch == 'p' || ch == ' ' )
    155 		  break;
    156 	       
    157 	       clear();
    158                mvprintw(LINES / 2, COLS / 2, "Pause!");
    159 	    }
    160 
    161 	    nodelay(stdscr, TRUE); /* unlock the getch() */
    162 	    clear();
    163 
    164 	    break;
    165 	 case 't': /* Tricks */
    166 	    if( trick ) {
    167 	       nodelay(stdscr, TRUE);
    168 	       trick = 0;
    169 	    }
    170 	    else {
    171 	       nodelay(stdscr, FALSE);
    172 	       trick = 1;
    173 	       ++tricks;
    174 	    }
    175 	    break;
    176 	 case KEY_RESIZE: /* Terminal resize */
    177   	    nodelay(stdscr, FALSE);
    178 
    179             /* Check the terminal size */
    180             while( LINES < MINROW || COLS < MINCOL ) {
    181 	       clear();
    182 	       mvprintw(LINES / 2, COLS / 2 - 12, "Terminal size too small!");
    183 	       mvprintw(LINES / 2 + 1, COLS / 2 - 16,
    184 	          "Please, enlarge it and press a key");
    185 	       getch();
    186             }
    187 	    
    188 	    nodelay(stdscr, TRUE);
    189 	    clear();
    190 
    191 	    break;
    192       } /* eof switch() */
    193       moveworm(&worm, route); /* move the worm */
    194 
    195       /*
    196        * Check if the "new" position is already
    197        * busy or it's out of the window.
    198       */
    199       if( isout(worm) || isbite(worm) || (auto_mode && level >= LEVMAX) ) {
    200 
    201 	 if( level >= LEVMAX )
    202 	    mvprintw(LINES / 2, COLS / 2 - 5, "Too fast!");
    203 	 else
    204 	    mvprintw(LINES / 2, COLS / 2 - 5, "You lose!");
    205 
    206 	 mvprintw(LINES / 2 + 1, COLS / 2 - 10, "Your scores are: %.3d", score);
    207 
    208 	 if( auto_mode ) {
    209 	    mvprintw(LINES / 2 + 3, COLS / 2 - 6, "Restarting..");
    210 	    refresh();
    211 
    212             /* reset the game */
    213 	    killworm(&worm);
    214             newnode(&worm, 1 + rand() % (COLS - 2), 6 + rand() % (LINES - 7));
    215             erow = 6 + rand() % (LINES - 7);
    216             ecol = 1 + rand() % (COLS - 2);
    217 	    score = level = eating = tricks = moves = 0;
    218 	    n = 1;
    219 
    220 	    sleep(2); /* leave that the user tastes the messsage :-) */
    221 	    continue;
    222 	 }
    223 	 else {
    224 	    mvprintw(LINES / 2 + 2, COLS / 2 - 10, "Press a key to exit..");
    225 
    226 	    nodelay(stdscr, FALSE);
    227 	    getch(); /* wait for input */
    228 
    229 	    break;
    230 	 }
    231       }
    232 
    233       eating = n; /* needed for the wormshow() call */
    234 
    235       /* If it's eating */
    236       while( iseat(worm, erow, ecol) ) {
    237 	 /* Add a node on the top of worm (new head) */ 
    238 	 current = worm;
    239 	 while( current->next != NULL )
    240 	    current = current->next;
    241 	 newnode(&current->next, worm->y, worm->x);
    242 
    243          /* Change the eat's position */
    244          erow = 6 + rand() % (LINES - 7);
    245          ecol = 1 + rand() % (COLS - 2);
    246          ++n; /* Change the eat's number */ 
    247 
    248          score += level + 1; /* increment the points */
    249 
    250          /* Increase the level */
    251          if( !(n % LEVEAT) ) {
    252             ++level;
    253          }
    254       }
    255 
    256       erase(); /* clear the screen */
    257       box(stdscr, ACS_VLINE, ACS_HLINE); /* create the borders */
    258       mvprintw(erow, ecol, "%d", n); /* draw the eat */
    259 
    260       /* Show the status window (and its elemets) */
    261       mvwprintw(stdscr, 1, COLS / 2 - 37 / 2, /* Copy */
    262          "The Hermit worm - (C) 2006 Claudio M."); /* left */
    263       box(wstat, ACS_VLINE, ACS_HLINE); /* status box */
    264       mvwhline(stdscr, 5, 1, ACS_HLINE, COLS - 2); /* new top limit */
    265 
    266       /* Show the worm position */
    267       box(wepos, ACS_VLINE, ACS_HLINE); /* status box */
    268       mvwprintw(stdscr, 1, 3, "Worm Curs");
    269       mvwprintw(wepos, 1, 2, "%.2dx%.2d", erow, ecol);
    270 
    271       /* Show the eat position */
    272       box(wpos, ACS_VLINE, ACS_HLINE); /* status box */
    273       mvwprintw(stdscr, 1, COLS - 12, "Eat Curs");
    274       mvwprintw(wpos, 1, 2, "%.2dx%.2d", worm->x, worm->y);
    275 
    276       /* Show the informations */
    277       mvwprintw(wstat, 1, 3,
    278 	 "TS:%.2dx%.2d | " /* Terminal size */
    279 	 "SL:%.2d | "      /* Speed level */
    280 	 "EM:%.2d | "      /* Eats missing */
    281 	 "UM:%.3d | "      /* User moves */
    282 	 "UT:%.2d | "      /* User tricks */
    283 	 "Ss:%.3d",        /* Scores */
    284 	 LINES, COLS, level + 1, LEVEAT - (n % LEVEAT),
    285 	 moves, tricks, score);
    286 
    287       /*
    288        * Show the whole "new" worm
    289        * NOTE: this statement is not optimized - trash statement :-)
    290       */
    291       if( iseat(worm, erow + 1, ecol) || iseat(worm, erow - 1, ecol) ||
    292           iseat(worm, erow, ecol + 1) || iseat(worm, erow, ecol - 1) ) {
    293          wormshow(worm, WORM, EATH);
    294       }
    295       else
    296          wormshow(worm, WORM, HEAD);
    297 
    298       usleep( 100000 - level * 10000 );
    299    }
    300 
    301    endwin(); /* end of session */
    302 
    303    puts("\nEnd of game.");
    304 
    305    return 0;
    306 } /* E0F main */
    307 
    308 /*
    309  * Returns if the worm is out of the window
    310 */
    311 int isout(List *worm)
    312 {
    313     if( worm->x <= 5 || !worm->y || worm->x >= LINES - 1 || worm->y >= COLS - 1)
    314        return 1;
    315 
    316    return 0;
    317 } /* eof isout() */
    318 
    319 /*
    320  * Return if the worm is eating
    321 */
    322 int iseat(List *worm, int row, int col)
    323 {
    324    return (worm->x == row && worm->y == col);
    325 } /* eof iseat() */
    326 
    327 /*
    328  * Check if the worm is biting itself
    329 */
    330 int isbite(List *worm)
    331 {
    332    List *current = worm;
    333 
    334    while( (current = current->next) != NULL ) {
    335       if( worm->x == current->x && worm->y == current->y )
    336 	 return 1;
    337    }
    338 
    339    return 0;
    340 } /* eof isbite() */
    341 
    342 /*
    343  * Show the worm (call refresh(3))
    344 */
    345 void wormshow(List *worm, char ch, char chhead)
    346 {
    347    List *current = worm->next;
    348 
    349    /* show the worm */
    350    while( current != NULL ) {
    351       mvprintw(current->x, current->y, "%c", ch);
    352       current = current->next;
    353    }
    354    mvprintw(worm->x, worm->y, "%c", chhead); /* show the head */
    355 
    356    refresh(); /* print the changes */
    357 
    358 } /* eof wormshow() */
    359 
    360 /*
    361  * Add a new node to the list
    362 */
    363 void newnode(List **listp, int y, int x)
    364 {
    365    List *newPtr;
    366 
    367    /* Allocate the memory */
    368    if( (newPtr = malloc( sizeof(List) )) == NULL ) {
    369       printf("Not enough memory to allocate the \"%d.%d\" element\n", x, y);
    370       return;
    371    }
    372    newPtr->next = *listp;
    373    newPtr->x = x;
    374    newPtr->y = y;
    375 
    376    *listp = newPtr;
    377 
    378 } /* eof newnode() */
    379 
    380 /*
    381  * Move the whole worm
    382 */
    383 void moveworm(List **worm, int way)
    384 {
    385    List *current = *worm;
    386    int n = 1, i;
    387 
    388    /* count the nodes */
    389    while( (current = current->next) != NULL )
    390      ++n;
    391 
    392    current = *worm; /* come back to the start point */
    393    while( --n ) {
    394 
    395       current = *worm;
    396       i = n;
    397 
    398       while( --i )
    399          current = current->next;
    400 
    401       /* update the position */
    402       current->next->x = current->x;
    403       current->next->y = current->y;
    404    }
    405 
    406    /* move the head */
    407    switch(way) {
    408       case R_UP:
    409          --( *worm )->x;
    410          break;
    411       case R_LEFT:
    412          --( *worm )->y;
    413          break;
    414       case R_DOWN:
    415          ++( *worm )->x;
    416          break;
    417       case R_RIGHT:
    418          ++( *worm )->y;
    419          break;
    420    }
    421 
    422 } /* eof moveworm() */
    423 
    424 /*
    425  * Kill the worm: free the whole worm's memory
    426 */
    427 void killworm(List **worm)
    428 {
    429    List *current;
    430 
    431    while(*worm != NULL) {
    432       current = *worm;
    433       *worm = (*worm)->next;
    434       free(current);
    435    }
    436 
    437 } /* eof killworm() */
    438