diff options
-rw-r--r-- | ncsweeper.c | 332 |
1 files changed, 284 insertions, 48 deletions
diff --git a/ncsweeper.c b/ncsweeper.c index 7ff9e05..2129284 100644 --- a/ncsweeper.c +++ b/ncsweeper.c @@ -16,8 +16,11 @@ */ #include <stdlib.h> +#include <unistd.h> #include <time.h> +#include <sys/time.h> #include <ncurses.h> +#include <string.h> #define WIDTH 15 #define HEIGHT 15 @@ -28,6 +31,45 @@ #define LEFT 2 #define RIGHT 3 +enum DEMO_ACTION_TYPE +{ + NONE = 0, + GOUP, + GODOWN, + GOLEFT, + GORIGHT, + FLAG, + REVEAL, + QUIT, +}; + +struct demo_header +{ + int width; + int height; + int mine_count; +}; + +struct demo_mine +{ + int x; + int y; +}; + +struct demo_action +{ + double action_pre_delay; + enum DEMO_ACTION_TYPE type; + int start_x; + int start_y; +}; + +struct action_node +{ + struct demo_action *action; + struct action_node *next; +} *action_head = NULL; + enum STATE { HIDDEN = 1 << 0, @@ -40,6 +82,10 @@ struct game int width; int height; int minecount; + int action_count; + int is_demo; + int is_recording; + char demo_filename[512]; } game; struct tile @@ -67,6 +113,11 @@ void revealmines(); struct tile *getneighbors(struct tile *tile, struct tile **neighbors); struct tile *gettileat(int x, int y); int checkwin(); +enum DEMO_ACTION_TYPE input(); +void free_action_list(); +struct action_node *generate_action_node(double delay, enum DEMO_ACTION_TYPE type, struct tile *tile); +int append_action_node(struct action_node *node); +void save_demo(); struct tile *getneighbors(struct tile *tile, struct tile **neighbors) { @@ -243,66 +294,245 @@ draw() wrefresh(window); } +enum DEMO_ACTION_TYPE +input() +{ + int ch = getch(); /* blocking */ + enum DEMO_ACTION_TYPE type = NONE; + struct tile *tile = gettileat(cursor.x, cursor.y); + switch(ch) + { + case 'k': + case 'w': + if (canmove(UP)) + { + type = GOUP; + cursor.y--; + } + break; + + case 'j': + case 's': + if (canmove(DOWN)) + { + type = GODOWN; + cursor.y++; + } + break; + + case 'h': + case 'a': + if (canmove(LEFT)) + { + type= GOLEFT; + cursor.x--; + } + break; + case 'l': + case 'd': + if (canmove(RIGHT)) + { + type = GORIGHT; + cursor.x++; + } + break; + case 'f': + { + if (tile && tile->state & HIDDEN) + { + type = FLAG; + tile->state ^= FLAGGED; + draw(); + } + break; + } + case ' ': + if (tile && !(tile->state & FLAGGED)) + { + type = REVEAL; + exitgame = reveal(cursor.x, cursor.y); + } + break; + + case 'q': + type = QUIT; + exitgame = 1; + break; + default: + break; + } + return type; +} + +void +free_action_list() +{ + struct action_node *node; + while (action_head) + { + node = action_head; + action_head = action_head->next; + free(node); + } +} + +struct action_node *generate_action_node(double delay, enum DEMO_ACTION_TYPE type, struct tile *tile) +{ + struct action_node *node = malloc(sizeof(struct action_node)); + struct demo_action *action = malloc(sizeof(struct demo_action)); + if (!node || !action) + return NULL; + action->action_pre_delay = delay; + action->start_x = tile->x; + action->start_y = tile->y; + action->type = type; + node->action = action; + node->next= NULL; + return node; +} + +int append_action_node(struct action_node *node) +{ + struct action_node **finger = &action_head; + while (*finger) + { + finger = &(*finger)->next; + } + node->next = *finger; + *finger = node; + return 1; +} + +void print_nodes() +{ + struct action_node *finger = action_head; + while (finger) + { + if (finger->action) + { + printf("%d,%d\n", finger->action->start_x, finger->action->start_y); + } + finger = finger->next; + } +} + +void +save_demo() +{ + printf("saving demo to %s..\n", game.demo_filename); + + /* open demo file */ + FILE *demo = fopen(game.demo_filename, "wb"); + if (!demo) + { + puts("cannot open demo file"); + return; + } + + /* generate header and write */ + struct demo_header header; + header.width = game.width; + header.height = game.height; + header.mine_count = game.minecount; + + fwrite(&header, sizeof(struct demo_header), 1, demo); + + /* generate mines and write */ + struct demo_mine demo_mines[game.minecount]; + /* loop over every tile and save it if it's a mine */ + int i = 0; + for (int x = 0; x < game.width; x++) + { + for (int y = 0; y < game.height; y++) + { + struct tile *tile = gettileat(x, y); + if (tile->state & MINE) + { + demo_mines[i].x = tile->x; + demo_mines[i].y = tile->y; + fwrite(&demo_mines[i], sizeof(struct demo_mine), 1, demo); + i++; + } + } + } + + /* walk action list and write it to file */ + fwrite(&game.action_count, sizeof game.action_count, 1, demo); + /* skip first node */ + if (!action_head->next) + { + puts("cannot write demo file, no actions exist.."); + fclose(demo); + return; + } + struct action_node *finger = action_head->next; + int x = 0; + while (finger) + { + fwrite(&finger, sizeof(struct action_node), 1, demo); + finger = finger->next; + x++; + } + printf("saved 0x%x nodes\n", x); + fclose(demo); +} + int -main(void) +main(int argc, char **argv) { + game.is_demo = 0; + game.is_recording = 0; + if ((argc > 1 && argc != 3)) + { + printf("usage: %s [-record save.dem | -play load.dem]\n", argv[0]); + goto safe_exit; + } + if (argc == 3) + { + if (strcmp(argv[1], "-record") == 0) + { + game.is_recording = 1; + strncpy(game.demo_filename, argv[2], 511); + } + else if (strcmp(argv[1], "-play") == 0) + { + game.is_demo = 1; + strncpy(game.demo_filename, argv[2], 511); + } + else + { + printf("usage: %s [-record save.dem | -play load.dem]\n", argv[0]); + goto safe_exit; + } + } initscr(); noecho(); game.width = WIDTH; game.height = HEIGHT; game.minecount = MINECOUNT; + game.action_count = 0; window = newwin(game.height+TILEGAP, (game.width*TILEGAP)+1, 1, 8); generateboard(); - int ch; + action_head = malloc(sizeof(struct action_node)); + action_head->action = NULL; + action_head->next = NULL; + if (!action_head) + goto safe_exit; + struct timespec begin, end; + double move_us; while(!exitgame) { draw(); - ch = getch(); /* blocking */ - struct tile *tile = gettileat(cursor.x, cursor.y); - switch(ch) - { - case 'k': - case 'w': - if (canmove(UP)) - cursor.y--; - break; - - case 'j': - case 's': - if (canmove(DOWN)) - cursor.y++; - break; - - case 'h': - case 'a': - if (canmove(LEFT)) - cursor.x--; - break; - case 'l': - case 'd': - if (canmove(RIGHT)) - cursor.x++; - break; - case 'f': - { - if (tile && tile->state & HIDDEN) - { - tile->state ^= FLAGGED; - draw(); - } - break; - } - case ' ': - if (tile && !(tile->state & FLAGGED)) - exitgame = reveal(cursor.x, cursor.y); - break; - - case 'q': - exitgame = 1; - break; - default: - break; - } + clock_gettime(CLOCK_MONOTONIC, &begin); + enum DEMO_ACTION_TYPE type = input(); + clock_gettime(CLOCK_MONOTONIC, &end); + move_us = (end.tv_sec - begin.tv_sec) * 1000.0; + move_us += (end.tv_nsec - begin.tv_nsec) / 1000000.0; + move_us *= 1000; + struct action_node *move = generate_action_node(move_us, type, gettileat(cursor.x, cursor.y)); + append_action_node(move); + game.action_count++; + //printf("%.3f us elapsed\n", move_us); if (checkwin()) { revealmines(); @@ -320,7 +550,13 @@ main(void) } mvprintw(game.height+4, 0, "press any key to exit.."); getch(); +safe_exit: + delwin(window); endwin(); + if (game.is_recording) + save_demo(); free(board); + printf("0x%x\n", game.action_count); + free_action_list(); return 0; } |