curssses

Snake game for the linux terminal.
git clone git://git.amin.space/curssses.git
Log | Files | Refs | LICENSE

commit c0172bb9d476d0b0ec4d03e22977f0dae01154d2
parent 154dbdbd5ba9d4450d699c621d8c5ff9a66ae9da
Author: amin <dev@aminmesbah.com>
Date:   Wed,  7 Dec 2016 01:46:26 +0000

Decouple framerate from sim rate and CPU speed.

FossilOrigin-Name: c2f17f12033d7dee177889043717cb96ff4da141e4793c05c8147cb6b57a87e6
Diffstat:
Mcurssses.c | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 61 insertions(+), 9 deletions(-)

diff --git a/curssses.c b/curssses.c @@ -1,11 +1,19 @@ #include <curses.h> #include <locale.h> -#include <stdio.h> #include <stdlib.h> +#include <time.h> #include <unistd.h> -#define SECOND 1000000 -#define FPS 60 +#define SECOND 1000 + +#define FPS 60 +#define MS_PER_FRAME (SECOND / FPS) + +#define UPDATES_PER_SECOND 100 +#define MS_PER_UPDATE (SECOND / UPDATES_PER_SECOND) + +#define MOVEMENTS_PER_SECOND 10 +#define UPDATES_PER_MOVEMENT (UPDATES_PER_SECOND / MOVEMENTS_PER_SECOND) typedef enum { @@ -67,6 +75,7 @@ void snake_add_segment(snake *s) void snake_move(snake *s) { + // move body segment *current = s->tail; while (current != s->head) { @@ -75,6 +84,7 @@ void snake_move(snake *s) current = current->prev; } + // move head switch (s->d) { case LEFT: @@ -113,6 +123,15 @@ void snake_move(snake *s) } } +uint64_t get_current_time_ms() +{ + struct timespec current; + // TODO(amin): Fallback to other time sources when CLOCK_MONOTONIC is unavailable. + clock_gettime(CLOCK_MONOTONIC, &current); + uint64_t milliseconds = ((current.tv_sec * 1000000000) + current.tv_nsec) / 1000000; + return milliseconds; +} + int main(void) { setlocale(LC_ALL, ""); @@ -134,8 +153,19 @@ int main(void) } int input = 0; + int updates = 0; + int frames = 0; + int lag = 0; + uint64_t previous_ms = get_current_time_ms(); + while (1) { + frames++; + uint64_t current_ms = get_current_time_ms(); + uint64_t elapsed_ms = current_ms - previous_ms; + previous_ms = current_ms; + lag += elapsed_ms; + input = getch(); switch (input) @@ -166,6 +196,7 @@ int main(void) nodelay(stdscr, FALSE); getch(); nodelay(stdscr, TRUE); + previous_ms = get_current_time_ms(); } break; case 'i': { @@ -173,16 +204,28 @@ int main(void) } break; } - erase(); - snake_move(s); + // TODO(amin): Test on slow computers. + // Have I sufficiently decoupled simulation speed from processor speed? + while (lag >= MS_PER_UPDATE) + { + if (updates % UPDATES_PER_MOVEMENT == 0) + { + snake_move(s); + } + updates++; + lag -= MS_PER_UPDATE; + } + + + erase(); if (debug) { mvprintw( 0, 0, - "Screen: [%d, %d]\nSnake Length: %d\nHead: (%d, %d)\nTail: (%d, %d)", - COLS, LINES, s->length, s->head->x, s->head->y, s->tail->x, s->tail->y); + "Screen: [%d, %d]\nSnake Length: %d\nHead: (%d, %d)\nTail: (%d, %d)\nFrame Time: %dms\nUpdates: %d\nFrames: %d", + COLS, LINES, s->length, s->head->x, s->head->y, s->tail->x, s->tail->y, elapsed_ms, updates, frames); } segment *current = s->head; @@ -192,15 +235,24 @@ int main(void) mvaddch(current->y, current->x, s->symbol); if (debug) { - mvprintw((i%20)+4, (i/20)*25, "Segment %d (%d, %d)", i+1, current->x, current->y); + mvprintw((i%20)+8, (i/20)*25, "Segment %d (%d, %d)", i+1, current->x, current->y); } current = current->next; i++; } refresh(); - usleep(SECOND / FPS); + if (elapsed_ms <= MS_PER_FRAME) + { + usleep((MS_PER_FRAME - elapsed_ms) * SECOND); + } + // TODO(amin): Is this ever needed? + else + { + usleep(MS_PER_FRAME * SECOND); + } } + clear(); endwin(); exit(0);