All pastes #1554505 Raw Edit

richard

public text v1 · immutable
#1554505 ·published 2009-09-05 02:02 UTC
rendered paste body
#include "lib.h"// this is a simple nibbles like game for the OS.// it should actually be portable to other unix systems// because most of lib.c will be as well.#define FRAME_DELAY 500#define SPEEDUP 20#define CHERRIES_PER_LEVEL 10#define POINTS_PER_CHERRY 8#define N_LEVELS 5#define START_LEVEL 0#define INITIAL_LENGTH 6#define MAX_SEGMENTS ((CHERRIES_PER_LEVEL*GROW_AMOUNT)+(CHERRIES_PER_LEVEL*N_LEVELS)+INITIAL_LENGTH)#define DISP_DELAY 500#define GROW_AMOUNT 5#define QUEUE_KEYPRESSES 0#define SNAKE_CHAR 0xdb#define GOAL_CHAR 0xeb#define GOAL_COLOUR LIGHTRED#define WALL_VERTICAL 0xba#define WALL_HORIZ 0xCD#define WALL_TOPLEFT 0xc9#define WALL_TOPRIGHT 0xbb#define WALL_BOTTOMLEFT 0xc8#define WALL_BOTTOMRIGHT 0xbc#define WIDTH 60#define HEIGHT 20typedef unsigned char map_el;map_el _map[WIDTH][HEIGHT];void set_map(int x,int y,map_el e) {	if(x<0||x>=WIDTH||y<0||y>HEIGHT) {		colour(LIGHTGREY,BLACK);		cls();		printf("Invalid map coord: %d,%d\n",x,y);		sys_exit(1);	}	_map[x][y]=e;}typedef enum {	GAME_RUNNING,GAME_HITSELF,GAME_HITWALL,GAME_WON,GAME_QUIT} game_state;// one bit of the snaketypedef struct snake_seg{	u32 x,y;	struct snake_seg *next;} snake_seg;// this actually goes against conventional linked list terminology.// the snake_tail is the head of the list, whereas snake_head is the tail.snake_seg *snake_head=0;snake_seg *snake_tail=0;int current_level=START_LEVEL;// simple pool allocator.snake_seg segments[MAX_SEGMENTS];int curr_segment=0;void draw_box(int x0,int y0,int x1,int y1) {	for(int y=y0;y<y1;++y) {		cursor_to(x0,y); putch(WALL_VERTICAL);		for(int x=x0;x<x1-1;++x) putch(' ');		cursor_to(x1,y); putch(WALL_VERTICAL);	}	for(int x=x0;x<x1;++x) {		cursor_to(x,y0); putch(WALL_HORIZ);		cursor_to(x,y1); putch(WALL_HORIZ);	}	cursor_to(x0,y0); putch(WALL_TOPLEFT);	cursor_to(x1,y0); putch(WALL_TOPRIGHT);	cursor_to(x0,y1); putch(WALL_BOTTOMLEFT);	cursor_to(x1,y1); putch(WALL_BOTTOMRIGHT);}void draw_title() {	draw_box(20,5,60,20);	cursor_to(23,7); printf("rNibbles - (C) Richard Whitty 2009");	cursor_to(28,9); printf("Controls: Up: W");	cursor_to(28,10); printf("        Down: S");	cursor_to(28,11); printf("        Left: A");	cursor_to(28,12); printf("       Right: D");	cursor_to(28,13); printf("        Quit: Q");	cursor_to(25,19); printf("Press any key to start...");	flush();	getch();}void update_score(int c,int s) {	int BGC=BLACK;	int TC=LIGHTGREY;	cursor_to(WIDTH+2,4);	colour(GOAL_COLOUR,BLACK);	putch(GOAL_CHAR);	colour(TC,BGC);	printf("     : %d",c);	cursor_to(WIDTH+2,6);	printf("Score : %d",s);	cursor_to(WIDTH+2,7);	printf("Length: %d",curr_segment);	cursor_to(WIDTH+2,8);	printf("Level : %d",current_level);}void draw_score(int c,int s) {	int BGC=BLACK;	int TC=LIGHTGREY;	colour(TC,BGC);	draw_box(WIDTH,0,79,HEIGHT-1);	cursor_to(WIDTH+2,2);	print("rNibbles - Score");	update_score(c,s);}int cherries=0;int draw_end(int  s) {	while(!sys_poll(stdin,P_READ)) {		getch();	}	colour(WHITE,BLACK);	draw_box(20,5,60,20);	colour(LIGHTRED,BLACK);	cursor_to(28,7); printf("GAME OVER!");	colour(LIGHTGREY,BLACK);	cursor_to(28,9);	switch(s) {		case GAME_RUNNING: print("Loop somehow exited while game was running..."); break;		case GAME_WON:			print("Congratulations, you win!");break;		case GAME_HITSELF:			print(" Stop hitting yourself!!"); break;		case GAME_HITWALL:			print("        Oops..."); break;		case GAME_QUIT:			print(" Well thanks anyway...");break;	}	cursor_to(25,12); printf("You got %d cherries",cherries);	cursor_to(25,13); printf("The snake was %d feet long!",curr_segment);	cursor_to(25,18); print("Play again? (Y/N[N])");	flush();	sys_sleep(DISP_DELAY);	for(;;) {		switch(getch()) {			case 'Y': case 'y':				return 0;			case 'n': case 'N': case '\n': case ' ':				return 1;		}	}}void init_map() {	memset(_map,0,sizeof(_map));	for(int y=1;y<HEIGHT-1;++y) {		set_map(0,y,WALL_VERTICAL);		set_map(WIDTH-1,y,WALL_VERTICAL);	}	for(int x=1;x<WIDTH-1;++x) {		set_map(x,0,WALL_HORIZ);		set_map(x,HEIGHT-1,WALL_HORIZ);	}	set_map(0,0,WALL_TOPLEFT);	set_map(WIDTH-1,0,WALL_TOPRIGHT);	set_map(0,HEIGHT-1,WALL_BOTTOMLEFT);	set_map(WIDTH-1,HEIGHT-1,WALL_BOTTOMRIGHT);}void loadlevel(int i) {	init_map();	if(i==0) {		// bare map	} else if(i==1) {// single line  down middle		int x1=WIDTH/2;		int y1=HEIGHT/4;		int y2=y1*3;		while(y1<y2) {			set_map(x1,y1,WALL_VERTICAL);			++y1;		}	}else if(i==2) {	// two lines down middle		int x1=WIDTH/3;		int x2=x1*2;		int y1=HEIGHT/4;		int y2=y1*3;		while(y1<y2) {			set_map(x1,y1,WALL_VERTICAL);			set_map(x2,y1,WALL_VERTICAL);			++y1;		}	}else if(i==3) {	// upside-down U in centre		int x1=WIDTH/3;		int x2=x1*2;		int y1=HEIGHT/4;		int y2=y1*3;		set_map(x1,y1,WALL_TOPLEFT);		set_map(x2,y1,WALL_TOPRIGHT);		for(int y=y1+1;y<y2;++y) {			set_map(x1,y,WALL_VERTICAL);			set_map(x2,y,WALL_VERTICAL);		}		for(int x=x1+1;x<x2;++x) {			set_map(x,y1,WALL_HORIZ);		}	} else if(i==4) { 	// like a cross empty in the middle		int x1=WIDTH/3;		for(int x=1;x<x1;++x) {			set_map(x,HEIGHT/2,WALL_HORIZ);			set_map(WIDTH-x,HEIGHT/2,WALL_HORIZ);		}		int y1=HEIGHT/3;		for(int y=1;y<y1;++y) {			set_map(WIDTH/2,y,WALL_VERTICAL);			set_map(WIDTH/2,HEIGHT-y,WALL_VERTICAL);		}	}}void draw_map() {	for(int x=0;x<WIDTH;++x) {		for(int y=0;y<HEIGHT;++y) {			if(_map[x][y]) {				cursor_to(x,y);				putch(_map[x][y]);			}		}	}}// allocates a segment.snake_seg* get_seg(u32 x,u32 y) {	if(curr_segment>=MAX_SEGMENTS)  {		return 0;		printf("\nsnake got too long, sorry :(\n");		sys_exit(1);	}	snake_seg *ret=segments+(curr_segment++);	ret->x=x;	ret->y=y;	ret->next=0;	return ret;}// draws the segment on the screen.map_el draw_seg(snake_seg* seg) {	map_el retval=_map[seg->x][seg->y];	set_map(seg->x,seg->y,SNAKE_CHAR);	colour(LIGHTGREEN,0); 	cursor_to(seg->x,seg->y); 	putch(SNAKE_CHAR);	return retval;}// erases the segment from the screen.void erase_seg(snake_seg* seg) {	set_map(seg->x,seg->y,0);	cursor_to(seg->x,seg->y);	putch(' ');}int rand(int lim) {	static long a = 100001;	a = (a * 125) % 2796203;	return ((a % lim));}void place_new_goal() {	u32 x,y;	do {		x=rand(WIDTH);		y=rand(HEIGHT);	} while(_map[x][y]);	set_map(x,y,GOAL_CHAR);	cursor_to(x,y);	colour(GOAL_COLOUR,BLACK);	putch(GOAL_CHAR);}static inline char poll_char(int queue) {	char c=0;	if(queue) {		if(!sys_poll(stdin,P_READ))			c=getch();	} else {		while(!sys_poll(stdin,P_READ))			c=getch();	}	return c;}// 'nuff said.int main(int argc,char**argv) {	int score=0;	cherries=0;	draw_title();	current_level=START_LEVEL;	for(;;){	char direction='U';	score+=cherries;	int growing=score/POINTS_PER_CHERRY;	cherries=0;	curr_segment=0;	snake_tail=snake_head=get_seg(5,5);	growing+=INITIAL_LENGTH;	snake_tail->next=snake_head;	colour(LIGHTGREY,BLACK);	cls();	loadlevel(current_level);	draw_map();	draw_score(cherries,score);	draw_seg(snake_head);	draw_seg(snake_tail);	place_new_goal();	cursor_to(0,0);	flush();	poll_char(QUEUE_KEYPRESSES);	getch(); // wait for keypress	char c=0;	game_state state=GAME_RUNNING;	int dx=0,dy=-1;	while(state==GAME_RUNNING) {		c=poll_char(QUEUE_KEYPRESSES);		if(c) {	// if there's no key pressed, skip this processing.		switch(c) {			case 'w': if(direction!='D') direction='U'; break;			case 's': if(direction!='U') direction='D'; break;			case 'a': if(direction!='R') direction='L'; break;			case 'd': if(direction!='L') direction='R'; break;			case 'q': state=GAME_QUIT; continue;			case 'p': sys_sleep(200); poll_char(QUEUE_KEYPRESSES) ; c=getch(); continue;		}		dx=dy=0;		switch(direction) {			case 'U': dy=-1;break;			case 'D': dy=1;break;			case 'L': dx=-1;break;			case 'R': dx=1;break;			default:				printf("Direction set incorrectly: '%c'\n",direction);				sys_exit(1);		}		}		u32 new_x=snake_head->x+dx;		u32 new_y=snake_head->y+dy;		if(growing) {	// grow the snake			--growing;			snake_head->next=get_seg(new_x,new_y);			update_score(cherries,score);		} else {			// if it's not growing, then get rid of the last			// segment.			erase_seg(snake_tail);			snake_seg* keep=snake_tail;			snake_tail=keep->next;			// and reuse the structure to be the new head.			keep->next=0;			keep->x=new_x;keep->y=new_y;			snake_head->next=keep;		}		// the head moves down the list.		snake_head=snake_head->next;		switch(draw_seg(snake_head)) {			case SNAKE_CHAR:				state=GAME_HITSELF;				break;			case WALL_HORIZ: case WALL_BOTTOMLEFT:case WALL_BOTTOMRIGHT:			case WALL_VERTICAL: case WALL_TOPLEFT:case WALL_TOPRIGHT:				state=GAME_HITWALL;				break;			case GOAL_CHAR:				++cherries;				score+=POINTS_PER_CHERRY;				growing+=GROW_AMOUNT;				if(cherries>=CHERRIES_PER_LEVEL)					state=GAME_WON;				else					place_new_goal();				update_score(cherries,score);				break;			case 0:  // empty space, continue;				break;			default:				printf("Other char\n"); // shouldn't happen.				break;		}		cursor_to(0,0);	// local echo is still enabled....		flush(); 	// lib.c might be buffering this.		// speed up the game as the player gets more cherries.		sys_sleep(FRAME_DELAY-(cherries*SPEEDUP));	}	sys_sleep(300);	if(state==GAME_WON && ++current_level<N_LEVELS) {		continue;	}	poll_char(0);	// show the game over screen. If the user wants to quit, let him.	if(draw_end(state)) {		break;	}	cherries=0;current_level=0; score=0;	}	// reset the screen to something sane.	colour(LIGHTGREY,BLACK);	cls();	cursor_to(0,0);	printf("\nThank you for playing rNibbles :p\n"		  "You got %d Cherries and scored %d points!\n",cherries,score);	return 0;}