training

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

commit 3a9cdb2d3221127d1e251c88bc4295e5d3abb2b8
parent 11fb66043d316e1a6c6867e0feb5cbdfa5635ef6
Author: Claudio Alessi <smoppy@gmail.com>
Date:   Fri, 26 Oct 2012 19:51:54 +0200

Add the "hermit" worm.

Diffstat:
Aworm.c | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 472 insertions(+), 0 deletions(-)

diff --git a/worm.c b/worm.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2006 + * Claudio M. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * TODO + * o fix: eat the numbers with the body too + * o fix: eat the numbers from any digit + * o fix: the numbers with 2 digits are out of borders + * o Create the '?' key which show the help and the commands + * o Ehm.. ehm.. Add the colors :-) Any volunteer? +*/ + +#include <stdio.h> +#include <curses.h> +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define HEAD 'o' /* normal head */ +#define WORM 'w' /* worm's body */ +#define EATH 'O' /* eating head */ + +#define MINROW 21 +#define MINCOL 80 + +#define LEVEAT 5 /* Food eated befor to increment the level */ +#define LEVMAX 9 /* Max speed (to preserve the refresh delay in auto-mode) */ + +/* Recursive list */ +typedef struct list_t { + int x; + int y; + struct list_t *next; +} List; + +/* Routes */ +enum { R_UP = 1, R_LEFT, R_DOWN, R_RIGHT } route; + +/* + * Prototypes +*/ +void newnode(List **listp, int y, int x); +void wormshow(List *worm, char ch, char chhead); +void moveworm(List **worm, int way); +int isout(List *worm); +int isbite(List *worm); +int iseat(List *worm, int row, int col); +void killworm(List **worm); + +/* The main function */ +int main(int argc, char **argv) +{ + int ch = 0, n, eating, trick = 0; + int erow, ecol; /* eat position */ + int level, tricks, moves, score; + + WINDOW *wstat, *wpos, *wepos; /* Status and positions windows */ + List *worm = NULL, *current; /* THe LiST */ + + int auto_mode = 0; /* Play without user interaction */ + + level = tricks = moves = score = 0; + srand( time(NULL) ); + + /* check the flag for auto-mode */ + if( argc == 2 && argv[1][0] == '-' && argv[1][1] == 'a' ) + auto_mode = 1; + + initscr(); /* start of session */ + curs_set(0); /* hide the cursor */ + keypad(stdscr, TRUE); /* enable the function keys */ + cbreak(); + noecho(); + nodelay(stdscr, TRUE); /* unlock getch(3) */ + + /* Check the terminal size */ + if( LINES < MINROW || COLS < MINCOL ) { + endwin(); + printf("This game require a terminal size of %dx%d pixel.\n", + MINROW, MINCOL); + printf("Your terminal size is: %dx%d\n", LINES, COLS); + return -1; + } + + /* Create the windows */ + wstat = subwin(stdscr, 3, COLS - COLS / 3, 2, COLS / 6); /* status */ + wpos = subwin(stdscr, 3, 9, 2, 3); /* worm position */ + wepos = subwin(stdscr, 3, 9, 2, COLS - 9 - 3); /* eat position */ + + /* Add the first position */ + newnode(&worm, 1 + rand() % (COLS - 2), 6 + rand() % (LINES - 7)); + + /* start point for the eat */ + erow = 6 + rand() % (LINES - 7); + ecol = 1 + rand() % (COLS - 2); + + /* choose a random route */ + route = 1 + rand() % 4; + + wormshow(worm, WORM, HEAD); /* put the first WORM */ + mvprintw(erow, ecol, "%d", n = 1); /* put the first eat */ + + /* main loop */ + while( (auto_mode ? getch() : (ch = getch())) != 'q' ) { + /* + * Auto-mode play + * + * NOTE: the worm can to bite himself losing the + * game which will be auto-restarted again. + * + */ + if( auto_mode ) { + ch = 0; /* don't touch the user moves */ + + /* choose the route, if needed */ + if( worm->x < erow ) { + if( route != R_DOWN ) + ch = 'j'; + } + else if( worm->x > erow ) { + if( route != R_UP ) + ch = 'k'; + } + else if( worm->y < ecol ) { + if( route != R_RIGHT ) + ch = 'l'; + } + else { + if( route != R_LEFT ) + ch = 'h'; + } + } + + switch(ch) { + case 'h': + case KEY_LEFT: + route = R_LEFT; + ++moves; + break; + case 'j': + case KEY_DOWN: + route = R_DOWN; + ++moves; + break; + case 'k': + case KEY_UP: + route = R_UP; + ++moves; + break; + case 'l': + case KEY_RIGHT: + route = R_RIGHT; + ++moves; + break; + case 'p': + case ' ': /* space bar */ + /* Pause */ + nodelay(stdscr, FALSE); /* lock the getch() */ + + mvprintw(LINES / 2, COLS / 2, "Pause!"); + while( (ch = getch()) ) { + if( ch == 'p' || ch == ' ' ) + break; + + clear(); + mvprintw(LINES / 2, COLS / 2, "Pause!"); + } + + nodelay(stdscr, TRUE); /* unlock the getch() */ + clear(); + + break; + case 't': /* Tricks */ + if( trick ) { + nodelay(stdscr, TRUE); + trick = 0; + } + else { + nodelay(stdscr, FALSE); + trick = 1; + ++tricks; + } + break; + case KEY_RESIZE: /* Terminal resize */ + nodelay(stdscr, FALSE); + + /* Check the terminal size */ + while( LINES < MINROW || COLS < MINCOL ) { + clear(); + mvprintw(LINES / 2, COLS / 2 - 12, "Terminal size too small!"); + mvprintw(LINES / 2 + 1, COLS / 2 - 16, + "Please, enlarge it and press a key"); + getch(); + } + + nodelay(stdscr, TRUE); + clear(); + + break; + } /* eof switch() */ + moveworm(&worm, route); /* move the worm */ + + /* + * Check if the "new" position is already + * busy or it's out of the window. + */ + if( isout(worm) || isbite(worm) || (auto_mode && level >= LEVMAX) ) { + + if( level >= LEVMAX ) + mvprintw(LINES / 2, COLS / 2 - 5, "Too fast!"); + else + mvprintw(LINES / 2, COLS / 2 - 5, "You lose!"); + + mvprintw(LINES / 2 + 1, COLS / 2 - 10, "Your scores are: %.3d", score); + + if( auto_mode ) { + mvprintw(LINES / 2 + 3, COLS / 2 - 6, "Restarting.."); + refresh(); + + /* reset the game */ + killworm(&worm); + newnode(&worm, 1 + rand() % (COLS - 2), 6 + rand() % (LINES - 7)); + erow = 6 + rand() % (LINES - 7); + ecol = 1 + rand() % (COLS - 2); + score = level = eating = tricks = moves = 0; + n = 1; + + sleep(2); /* leave that the user tastes the messsage :-) */ + continue; + } + else { + mvprintw(LINES / 2 + 2, COLS / 2 - 10, "Press a key to exit.."); + + nodelay(stdscr, FALSE); + getch(); /* wait for input */ + + break; + } + } + + eating = n; /* needed for the wormshow() call */ + + /* If it's eating */ + while( iseat(worm, erow, ecol) ) { + /* Add a node on the top of worm (new head) */ + current = worm; + while( current->next != NULL ) + current = current->next; + newnode(&current->next, worm->y, worm->x); + + /* Change the eat's position */ + erow = 6 + rand() % (LINES - 7); + ecol = 1 + rand() % (COLS - 2); + ++n; /* Change the eat's number */ + + score += level + 1; /* increment the points */ + + /* Increase the level */ + if( !(n % LEVEAT) ) { + ++level; + } + } + + erase(); /* clear the screen */ + box(stdscr, ACS_VLINE, ACS_HLINE); /* create the borders */ + mvprintw(erow, ecol, "%d", n); /* draw the eat */ + + /* Show the status window (and its elemets) */ + mvwprintw(stdscr, 1, COLS / 2 - 37 / 2, /* Copy */ + "The Hermit worm - (C) 2006 Claudio M."); /* left */ + box(wstat, ACS_VLINE, ACS_HLINE); /* status box */ + mvwhline(stdscr, 5, 1, ACS_HLINE, COLS - 2); /* new top limit */ + + /* Show the worm position */ + box(wepos, ACS_VLINE, ACS_HLINE); /* status box */ + mvwprintw(stdscr, 1, 3, "Worm Curs"); + mvwprintw(wepos, 1, 2, "%.2dx%.2d", erow, ecol); + + /* Show the eat position */ + box(wpos, ACS_VLINE, ACS_HLINE); /* status box */ + mvwprintw(stdscr, 1, COLS - 12, "Eat Curs"); + mvwprintw(wpos, 1, 2, "%.2dx%.2d", worm->x, worm->y); + + /* Show the informations */ + mvwprintw(wstat, 1, 3, + "TS:%.2dx%.2d | " /* Terminal size */ + "SL:%.2d | " /* Speed level */ + "EM:%.2d | " /* Eats missing */ + "UM:%.3d | " /* User moves */ + "UT:%.2d | " /* User tricks */ + "Ss:%.3d", /* Scores */ + LINES, COLS, level + 1, LEVEAT - (n % LEVEAT), + moves, tricks, score); + + /* + * Show the whole "new" worm + * NOTE: this statement is not optimized - trash statement :-) + */ + if( iseat(worm, erow + 1, ecol) || iseat(worm, erow - 1, ecol) || + iseat(worm, erow, ecol + 1) || iseat(worm, erow, ecol - 1) ) { + wormshow(worm, WORM, EATH); + } + else + wormshow(worm, WORM, HEAD); + + usleep( 100000 - level * 10000 ); + } + + endwin(); /* end of session */ + + puts("\nEnd of game."); + + return 0; +} /* E0F main */ + +/* + * Returns if the worm is out of the window +*/ +int isout(List *worm) +{ + if( worm->x <= 5 || !worm->y || worm->x >= LINES - 1 || worm->y >= COLS - 1) + return 1; + + return 0; +} /* eof isout() */ + +/* + * Return if the worm is eating +*/ +int iseat(List *worm, int row, int col) +{ + return (worm->x == row && worm->y == col); +} /* eof iseat() */ + +/* + * Check if the worm is biting itself +*/ +int isbite(List *worm) +{ + List *current = worm; + + while( (current = current->next) != NULL ) { + if( worm->x == current->x && worm->y == current->y ) + return 1; + } + + return 0; +} /* eof isbite() */ + +/* + * Show the worm (call refresh(3)) +*/ +void wormshow(List *worm, char ch, char chhead) +{ + List *current = worm->next; + + /* show the worm */ + while( current != NULL ) { + mvprintw(current->x, current->y, "%c", ch); + current = current->next; + } + mvprintw(worm->x, worm->y, "%c", chhead); /* show the head */ + + refresh(); /* print the changes */ + +} /* eof wormshow() */ + +/* + * Add a new node to the list +*/ +void newnode(List **listp, int y, int x) +{ + List *newPtr; + + /* Allocate the memory */ + if( (newPtr = malloc( sizeof(List) )) == NULL ) { + printf("Not enough memory to allocate the \"%d.%d\" element\n", x, y); + return; + } + newPtr->next = *listp; + newPtr->x = x; + newPtr->y = y; + + *listp = newPtr; + +} /* eof newnode() */ + +/* + * Move the whole worm +*/ +void moveworm(List **worm, int way) +{ + List *current = *worm; + int n = 1, i; + + /* count the nodes */ + while( (current = current->next) != NULL ) + ++n; + + current = *worm; /* come back to the start point */ + while( --n ) { + + current = *worm; + i = n; + + while( --i ) + current = current->next; + + /* update the position */ + current->next->x = current->x; + current->next->y = current->y; + } + + /* move the head */ + switch(way) { + case R_UP: + --( *worm )->x; + break; + case R_LEFT: + --( *worm )->y; + break; + case R_DOWN: + ++( *worm )->x; + break; + case R_RIGHT: + ++( *worm )->y; + break; + } + +} /* eof moveworm() */ + +/* + * Kill the worm: free the whole worm's memory +*/ +void killworm(List **worm) +{ + List *current; + + while(*worm != NULL) { + current = *worm; + *worm = (*worm)->next; + free(current); + } + +} /* eof killworm() */ +