summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ncsweeper.c332
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;
}