📄 snake.c
字号:
/* 贪食蛇 Snake by kikistar.com@gmail.com version 0.4.1 #20060420 * * 以下注释,k&r 表示 The C Programming Language. ISBN 7-302-02412-X/TP.1214 * algo 表示 严蔚敏、吴伟民编著的<数据结构> ISBN 7-900643-22-2 * * Press '2' to run faster and press '1' to run slower when playing the game. * The faster it runs the more scores you can get. */#include <curses.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <unistd.h>#define INIT_LEN 18 // the length of the snake is INIT_LEN+2 when init.#define FOODS 3 // how many foods display at a time#define BORDER_COLOR 1 // color pairs#define SCORE_COLOR 2#define HEAD_COLOR 3#define BODY_COLOR 4#define FOOD_COLOR 5#define DEVIL_COLOR 6enum GO {NONE, LF, UP, DN, RT};struct snake { int x; int y; enum GO go; char shape[3]; struct snake *prev; struct snake *next;};struct FOOD { int x; int y; bool shown; bool devil; int age; // how old is the food. The food turns to devil when struct FOOD *next; // it getting too old ( >= food2devil)};void die(int score);void rebirth(struct snake *head);int showlife(int life, int addlife);int myrand(int m, double n);struct snake *init_snake(int y, int x, struct snake **tailp);struct snake *add_node(struct snake *tail);struct FOOD *add_food(struct FOOD *, int row, int col);void show_food(struct snake *, struct FOOD *root, struct FOOD *food, int row, int col);struct snake *move_snake(struct snake *head, struct snake *tail);int main(){ struct FOOD *food; struct FOOD *foodp; struct snake *head; struct snake *sp; struct snake *tail; int ch, lastch, i, lastcolor, bodycolor; int x, y, row, col; int score, step, speed; int changecolor, food2devil, life; initscr(); cbreak(); keypad(stdscr, TRUE); noecho(); curs_set(0); start_color(); init_pair(BORDER_COLOR, COLOR_WHITE, COLOR_BLUE); init_pair(SCORE_COLOR, COLOR_BLACK, COLOR_WHITE); init_pair(HEAD_COLOR, COLOR_CYAN, COLOR_BLACK); init_pair(BODY_COLOR, COLOR_GREEN, COLOR_BLACK); init_pair(FOOD_COLOR, COLOR_YELLOW, COLOR_BLACK); init_pair(DEVIL_COLOR, COLOR_RED, COLOR_BLACK); init_pair(7, COLOR_RED, COLOR_YELLOW); init_pair(8, COLOR_YELLOW, COLOR_RED); init_pair(9, COLOR_BLUE, COLOR_RED); srand((unsigned) time(0)); /* init snake */ getmaxyx(stdscr, row, col); y = 1; x = 2; if ((head = init_snake(y, x, &tail)) == NULL) die(-1); for (i = 1; i <= INIT_LEN; ++i) if ((tail = add_node(tail)) == NULL) die(-1); /* init food */ if ((food = (struct FOOD *) malloc(sizeof(*food))) == NULL) die(-1); food->shown = FALSE; food->age = 0; food->devil = FALSE; food->next = NULL; for (i = 1; i < FOODS; i++) food = add_food(food, row, col); /* show border, snake */ attron(COLOR_PAIR(BORDER_COLOR)); box(stdscr, 0, 0); mvprintw(row - 1, 1, " Press \"2\" to run faster, \"1\" to run slower, SPACE to pause "); attroff(COLOR_PAIR(BORDER_COLOR)); life = 3; attron(COLOR_PAIR(SCORE_COLOR)); mvprintw(0, 16, " LIFE: %d ", life-1); attroff(COLOR_PAIR(SCORE_COLOR)); attron(COLOR_PAIR(BODY_COLOR)); mvaddstr(head->y, head->x, head->shape); attroff(COLOR_PAIR(BODY_COLOR)); /* game starts */ score = 0; changecolor = 1000; step = 1; speed = 500; lastch = ERR; bodycolor = COLOR_GREEN; lastcolor = bodycolor; while (life > 0){ timeout(speed); flushinp(); ch = getch(); if (lastch != ERR && lastch == ch) // when double press napms(speed); else lastch = ch; switch (ch) { case KEY_LEFT: if (head->go == UP || head->go == DN) head->go = LF; break; case KEY_UP: if (head->go == LF || head->go == RT) head->go = UP; break; case KEY_DOWN: if (head->go == LF || head->go == RT) head->go = DN; break; case KEY_RIGHT: if (head->go == UP || head->go == DN) head->go = RT; break; case '2': attron(COLOR_PAIR(SCORE_COLOR)); if ((speed = speed - 50) >= 50) mvprintw(0, col - 17, " SPEED %3d km/h ", 550 - speed); else speed = speed + 50; attroff(COLOR_PAIR(SCORE_COLOR)); break; case '1': attron(COLOR_PAIR(SCORE_COLOR)); if ((speed = speed + 50) <= 500) mvprintw(0, col - 17, " SPEED %3d km/h ", 550 - speed); else speed = speed - 50; attroff(COLOR_PAIR(SCORE_COLOR)); break; case ' ': if (score >= step) score = score - step; attron(COLOR_PAIR(SCORE_COLOR)); mvprintw(0, 1, " SCORE %d ", score); attroff(COLOR_PAIR(SCORE_COLOR)); timeout(-1); getch(); continue; break; case ERR: break; default: napms(speed); break; } /* food or devil */ food2devil = row + col; for (foodp = food; foodp != NULL; foodp = foodp->next) { show_food(head, food, foodp, row, col); if (foodp->devil == TRUE) /* do nothing */; else if (foodp->shown == TRUE && foodp->age >= food2devil-20 && foodp->age < food2devil) { attron(A_BLINK); mvaddch(foodp->y, foodp->x, '$'); attroff(A_BLINK); foodp->age++; } else if (foodp->shown == TRUE && foodp->age >= food2devil) { foodp->devil = TRUE; attron(COLOR_PAIR(DEVIL_COLOR)); mvaddch(foodp->y, foodp->x, 'x'); attroff(COLOR_PAIR(DEVIL_COLOR)); food = add_food(food, row, col); } else foodp->age++; } /* move snake */ if ((tail = move_snake(head, tail)) == NULL) die(-1); /* die or grow */ if (head->x == 0) { // hit the wall mvaddch(head->y, head->x, ' '); head->x = col-2; life = showlife(life, -1); } else if (head->y == 0) { mvaddch(head->y, head->x, ' '); head->y = row-2; life = showlife(life, -1); } else if (head->x == col-1) { mvaddch(head->y, head->x, ' '); head->x = 1; life = showlife(life, -1); } else if (head->y == row-1) { mvaddch(head->y, head->x, ' '); head->y = 1; life = showlife(life, -1); } else // hit the snake itself for (sp = head->next; sp != NULL; sp = sp->next) //k&r section 6.6 p145 if (head->x == sp->x && head->y == sp->y) { life = showlife(life, -1); if (life == 0) die(score); else rebirth(head); break; } for (foodp = food; foodp != NULL; foodp = foodp->next) if (head->x == foodp->x && head->y == foodp->y) { if (foodp->devil == TRUE) { // hit the devil life = showlife(life, -1); attron(COLOR_PAIR(DEVIL_COLOR)); mvaddch(foodp->y, foodp->x, 'x'); attroff(COLOR_PAIR(DEVIL_COLOR)); if (life == 0) die(score); else rebirth(head); break; } else { // hit the food foodp->shown = FALSE; step = 51 - (speed / 10); score = score + step; beep(); if (score > changecolor) { while ((bodycolor=myrand(1, 6)) == lastcolor) /* do nothing */; init_pair(BODY_COLOR, bodycolor, COLOR_BLACK); lastcolor = bodycolor; changecolor = changecolor + 1000; life = showlife(life, 1); } attron(COLOR_PAIR(SCORE_COLOR)); mvprintw(0, 1, " SCORE %d ", score); attroff(COLOR_PAIR(SCORE_COLOR)); tail = add_node(tail); } } } die(score);}int myrand(int m, double n) // rand from m to n. k&r section 7.8.7. man rand.{ // it's not a good function because I don't know how to use rand() return m + (unsigned) (n*rand()/(RAND_MAX+1.0));}struct snake *init_snake(int y, int x, struct snake **tailp){ struct snake *head; head = (struct snake *) malloc(sizeof(*head)); *tailp = (struct snake *) malloc(sizeof(**tailp)); if (head == NULL || *tailp == NULL) return NULL; head->x = x; head->y = y; head->go = RT; strcpy(head->shape, "@"); head->prev = NULL; (*tailp)->x = head->x-1; (*tailp)->y = head->y; (*tailp)->go = NONE; strcpy((*tailp)->shape, "#"); (*tailp)->prev = head; (*tailp)->next = NULL; head->next = *tailp; return head;}void die(int score){ beep(); attron(COLOR_PAIR(BORDER_COLOR)); mvprintw(LINES/2-3, (COLS-25)/2, " GAME OVER !!! "); mvprintw(LINES/2-2, (COLS-25)/2, " "); mvprintw(LINES/2-1, (COLS-25)/2, " Press SPACE to exit. "); mvprintw(LINES-2, 1, "http://my.opera.com/419/"); mvprintw(LINES-2, COLS-23, "kikistar.com@gmail.com"); attroff(COLOR_PAIR(BORDER_COLOR)); while (getch() != ' ') /* do nothing */; endwin(); if (score >= 0) { printf("snake: Your score was %d\n", score); exit(0); } else { printf("snake: Not enough memory!\n"); exit(1); }}void rebirth(struct snake *head){ napms(500); struct snake *sp; head->go = RT; head->y = 1; head->x = 2; for (sp = head->next; sp != NULL; sp = sp->next) { napms(10); mvaddch(sp->y, sp->x, ' '); refresh(); sp->y = 1; sp->x = 1; }}int showlife(int life, int addlife){ beep(); int i = life + addlife; if (i >= 1) { attron(COLOR_PAIR(SCORE_COLOR)); mvprintw(0, 16, " LIFE: %d ", i-1); attroff(COLOR_PAIR(SCORE_COLOR)); } return i;}struct snake *add_node(struct snake *tail) { struct snake *sp; sp = (struct snake *) malloc(sizeof(*sp)); if (sp == NULL) return NULL; sp->x = tail->x; sp->y = tail->y; sp->go = tail->go; // sp->shape[0] = tail->shape[0];sp->shape[1]='\0'; strcpy(sp->shape, tail->shape); sp->next = tail->next; // tailp->next always be NULL tail->next = sp; sp->prev = tail; tail = sp; return tail;}struct FOOD *add_food(struct FOOD *food, int row, int col){ struct FOOD *newfood; if ((newfood = (struct FOOD *) malloc(sizeof(*newfood))) == NULL) die(-1); newfood->shown = FALSE; newfood->age = 0; newfood->devil = FALSE; newfood->next = food->next; food->next = newfood; return food;}void show_food(struct snake *head, struct FOOD *foodroot, struct FOOD *food, int row, int col){ if (food->shown == TRUE) return; else { struct snake *sp; struct FOOD *foodp; struct FOOD *foodq; food->y = myrand(1, row-2); food->x = myrand(1, col-2); /* the loop below will not work when foodq = foodroot */ if (foodroot->shown == FALSE) foodq = foodroot->next; else foodq = food; /* avoid the wall (I don't trust myrand) */ if (food->y < 2 || food->y > row-2 || food->x < 1 || food->x > col-2) return; /* avoid the other food */ for (foodp = foodq; foodp != NULL; foodp = foodp->next) if (food->x == foodp->x && food->y == foodp->y) if (food != foodp) return; /* avoid the snake */ for (sp = head; sp != NULL; sp = sp->next) if (food->x == sp->x && food->y == sp->y) return; food->age = 0; food->shown = TRUE; attron(COLOR_PAIR(FOOD_COLOR)); mvaddch(food->y, food->x, '$'); attroff(COLOR_PAIR(FOOD_COLOR)); refresh(); }}/* 1.擦掉蛇尾 2.蛇尾移到蛇头位置 3.移动蛇头 4.更改相关指针 */struct snake *move_snake(struct snake *head, struct snake *tail){ if (head == NULL || tail == NULL) return NULL; mvaddstr(tail->y, tail->x, " "); // 1. tail->y = head->y; // 2. tail->x = head->x; attron(COLOR_PAIR(BODY_COLOR)); mvaddstr(tail->y, tail->x, tail->shape); attroff(COLOR_PAIR(BODY_COLOR)); switch (head->go) { // 3. case LF: head->x--; break; case UP: head->y--; break; case DN: head->y++; break; case RT: head->x++; break; } attron(COLOR_PAIR(HEAD_COLOR)); mvaddstr(head->y, head->x, head->shape); attroff(COLOR_PAIR(HEAD_COLOR)); struct snake *tailprev; // 4. struct snake *headnext; tailprev = tail->prev; // save tail->prev headnext = head->next; // save head->next head->next = tail; tail->next = headnext; headnext->prev = tail; tail->prev = head; tailprev->next = NULL; refresh(); return tailprev;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -