Skip to content

Commit

Permalink
O(1) passive damage detection #189 (#197)
Browse files Browse the repository at this point in the history
* notcurses: set up lastframe #189
* render: o(1) take no prisoners damage detection #189
  • Loading branch information
dankamongmen authored Dec 22, 2019
1 parent 8b1b2eb commit 9b81de3
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 245 deletions.
6 changes: 5 additions & 1 deletion doc/man/man1/notcurses-demo.1
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ notcurses-demo \- Show off some notcurses features
[ \fB\-p \fIpath \fR]
[ \fB\-d \fIdelaymult \fR]
[ \fB\-k \fR]
[ \fB\-c \fR]
[ \fB\-h / \fB\-\-help \fR]
.IR demospec
.SH OPTIONS
Expand All @@ -20,6 +21,9 @@ Apply a (floating-point) multiplier to the standard delay of 1s.
Inhibit use of the alternate screen. Necessary if you want the output left
on your terminal after the program exits.
.TP
.BR \-c
Do not attempt to seed the PRNG. This is useful when benchmarking.
.TP
.BR \-h ", " \-\-help
Print a usage message, and exit with success.
.TP
Expand All @@ -45,7 +49,7 @@ contains a set of text-based demonstrations of capabilities from the notcurses l
.P
(s)liders—a missing-piece puzzle made up of colorful blocks
.P
(b)leachworm—a great Nothing slowly robs the world of color
(w)hiteworm—a great Nothing slowly robs the world of color
.P
(v)iew—images and a video are rendered as text
.P
Expand Down
30 changes: 23 additions & 7 deletions src/demo/demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include <notcurses.h>
#include "demo.h"

// ansi terminal definition-4-life
static const int MIN_SUPPORTED_ROWS = 25;
static const int MIN_SUPPORTED_COLS = 80;

static const char DEFAULT_DEMO[] = "iemlubgswvpo";
static char datadir[PATH_MAX] = "/usr/share/notcurses"; // FIXME

Expand Down Expand Up @@ -46,11 +50,12 @@ struct timespec demodelay = {
static void
usage(const char* exe, int status){
FILE* out = status == EXIT_SUCCESS ? stdout : stderr;
fprintf(out, "usage: %s [ -h ] [ -k ] [ -d mult ] [ -f renderfile ] demospec\n", exe);
fprintf(out, "usage: %s [ -h ] [ -k ] [ -d mult ] [ -c ] [ -f renderfile ] demospec\n", exe);
fprintf(out, " -h: this message\n");
fprintf(out, " -k: keep screen; do not switch to alternate\n");
fprintf(out, " -d: delay multiplier (float)\n");
fprintf(out, " -f: render to file in addition to stdout\n");
fprintf(out, " -c: constant PRNG seed, useful for benchmarking\n");
fprintf(out, "all demos are run if no specification is provided\n");
fprintf(out, " b: run box\n");
fprintf(out, " e: run eagles\n");
Expand All @@ -63,7 +68,7 @@ usage(const char* exe, int status){
fprintf(out, " s: run shuffle\n");
fprintf(out, " u: run uniblock\n");
fprintf(out, " v: run view\n");
fprintf(out, " w: run bleachworm\n");
fprintf(out, " w: run witherworm\n");
exit(status);
}

Expand Down Expand Up @@ -183,7 +188,7 @@ ext_demos(struct notcurses* nc, const char* demos){
case 'l': ret = luigi_demo(nc); break;
case 'v': ret = view_demo(nc); break;
case 'e': ret = eagle_demo(nc); break;
case 'w': ret = bleachworm_demo(nc); break;
case 'w': ret = witherworm_demo(nc); break;
case 'p': ret = panelreel_demo(nc); break;
default:
fprintf(stderr, "Unknown demo specification: %c\n", *demos);
Expand All @@ -208,13 +213,17 @@ ext_demos(struct notcurses* nc, const char* demos){
// if it's NULL, there were valid options, but no spec.
static const char*
handle_opts(int argc, char** argv, notcurses_options* opts){
bool constant_seed = false;
int c;
memset(opts, 0, sizeof(*opts));
while((c = getopt(argc, argv, "hkd:f:p:")) != EOF){
while((c = getopt(argc, argv, "hckd:f:p:")) != EOF){
switch(c){
case 'h':
usage(*argv, EXIT_SUCCESS);
break;
case 'c':
constant_seed = true;
break;
case 'k':
opts->inhibit_alternate_screen = true;
break;
Expand Down Expand Up @@ -244,6 +253,9 @@ handle_opts(int argc, char** argv, notcurses_options* opts){
usage(*argv, EXIT_FAILURE);
}
}
if(!constant_seed){
srand(time(NULL)); // a classic blunder lol
}
const char* demos = argv[optind];
return demos;
}
Expand All @@ -252,7 +264,6 @@ handle_opts(int argc, char** argv, notcurses_options* opts){
int main(int argc, char** argv){
struct notcurses* nc;
notcurses_options nopts;
struct ncplane* ncp;
if(!setlocale(LC_ALL, "")){
fprintf(stderr, "Couldn't set locale based on user preferences\n");
return EXIT_FAILURE;
Expand All @@ -267,8 +278,9 @@ int main(int argc, char** argv){
if((nc = notcurses_init(&nopts, stdout)) == NULL){
return EXIT_FAILURE;
}
if((ncp = notcurses_stdplane(nc)) == NULL){
fprintf(stderr, "Couldn't get standard plane\n");
int dimx, dimy;
notcurses_term_dim_yx(nc, &dimy, &dimx);
if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){
goto err;
}
// no one cares about the leaderscreen. 1s max.
Expand Down Expand Up @@ -306,6 +318,10 @@ int main(int argc, char** argv){
return EXIT_SUCCESS;

err:
notcurses_term_dim_yx(nc, &dimy, &dimx);
notcurses_stop(nc);
if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){
fprintf(stderr, "At least an 80x25 terminal is required (current: %dx%d)\n", dimx, dimy);
}
return EXIT_FAILURE;
}
2 changes: 1 addition & 1 deletion src/demo/demo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extern struct timespec demodelay;
char* find_data(const char* datum);

int unicodeblocks_demo(struct notcurses* nc);
int bleachworm_demo(struct notcurses* nc);
int witherworm_demo(struct notcurses* nc);
int box_demo(struct notcurses* nc);
int maxcolor_demo(struct notcurses* nc);
int grid_demo(struct notcurses* nc);
Expand Down
155 changes: 93 additions & 62 deletions src/demo/bleachworm.c → src/demo/witherworm.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,81 +160,112 @@ lightup_surrounding_cells(struct ncplane* n, const cell* cells, int y, int x){
return 0;
}

typedef struct snake {
cell lightup[13];
int x, y;
uint64_t channels;
int prevx, prevy;
} snake;

static void
init_snake(snake* s, int dimy, int dimx){
for(size_t i = 0 ; i < sizeof(s->lightup) / sizeof(*s->lightup) ; ++i){
cell_init(&s->lightup[i]);
}
// start it in the lower center of the screen
s->x = (random() % (dimx / 2)) + (dimx / 4);
s->y = (random() % (dimy * 2 / 3)) + (dimy / 3);
s->channels = 0;
channels_set_fg_rgb(&s->channels, 255, 255, 255);
channels_set_bg_rgb(&s->channels, 20, 20, 20);
s->prevx = 0;
s->prevy = 0;
}

static int
snakey_top(struct notcurses* nc, snake* s){
struct ncplane* n = notcurses_stdplane(nc);
get_surrounding_cells(n, s->lightup, s->y, s->x);
ncplane_cursor_move_yx(n, s->y, s->x);
if(lightup_surrounding_cells(n, s->lightup, s->y, s->x)){
return -1;
}
return 0;
}

static int
snakey(struct notcurses* nc, snake* s, int dimy, int dimx, const struct timespec* iterdelay){
struct ncplane* n = notcurses_stdplane(nc);
int oldy, oldx;
clock_nanosleep(CLOCK_MONOTONIC, 0, iterdelay, NULL);
cell c = CELL_TRIVIAL_INITIALIZER;
do{ // force a move
oldy = s->y;
oldx = s->x;
// FIXME he ought be weighted to avoid light; he's a snake after all
int direction = random() % 4;
switch(direction){
case 0: --s->y; break;
case 1: ++s->x; break;
case 2: ++s->y; break;
case 3: --s->x; break;
}
// keep him away from the sides due to width irregularities
if(s->x < (dimx / 4)){
s->x = dimx / 4;
}else if(s->x >= dimx * 3 / 4){
s->x = dimx * 3 / 4;
}
if(s->y < 0){
s->y = 0;
}else if(s->y >= dimy){
s->y = dimy - 1;
}
ncplane_cursor_move_yx(n, s->y, s->x);
ncplane_at_cursor(n, &c);
// don't allow the snake into the summary zone (test for walls)
if(wall_p(n, &c)){
s->x = oldx;
s->y = oldy;
}
}while((oldx == s->x && oldy == s->y) || (s->x == s->prevx && s->y == s->prevy));
s->prevy = oldy;
s->prevx = oldx;
cell_release(n, &c);
return 0;
}

// each snake wanders around aimlessly, prohibited from entering the summary
// section. it ought light up the cells around it; to do this, we keep an array
// of 13 cells with the original colors, which we tune up for the duration of
// our colocality (unless they're summary area walls).
static void *
snake_thread(void* vnc){
const int snakecount = 3; // FIXME base count off area
struct notcurses* nc = vnc;
struct ncplane* n = notcurses_stdplane(nc);
cell lightup[13];
size_t i;
for(i = 0 ; i < sizeof(lightup) / sizeof(*lightup) ; ++i){
cell_init(&lightup[i]);
}
int dimy, dimx;
ncplane_dim_yx(n, &dimy, &dimx);
int x, y;
// start it in the lower center of the screen
x = (random() % (dimx / 2)) + (dimx / 4);
y = (random() % (dimy / 2)) + (dimy / 2);
cell head = CELL_TRIVIAL_INITIALIZER;
uint64_t channels = 0;
channels_set_fg_rgb(&channels, 255, 255, 255);
channels_set_bg_rgb(&channels, 20, 20, 20);
cell_prime(n, &head, "א", 0, channels);
cell c = CELL_TRIVIAL_INITIALIZER;
snake snakes[snakecount];
for(int s = 0 ; s < snakecount ; ++s){
init_snake(&snakes[s], dimy, dimx);
}
struct timespec iterdelay = { .tv_sec = 0, .tv_nsec = 1000000000ul / 20, };
int prevx = 0, prevy = 0;
while(true){
pthread_testcancel();
get_surrounding_cells(n, lightup, y, x);
ncplane_cursor_move_yx(n, y, x);
ncplane_at_cursor(n, &c);
if(lightup_surrounding_cells(n, lightup, y, x)){
for(int s = 0 ; s < snakecount ; ++s){
if(snakey_top(nc, &snakes[s])){
return NULL;
}
}
if(notcurses_render(nc)){
return NULL;
}
notcurses_render(nc);
int oldy, oldx;
clock_nanosleep(CLOCK_MONOTONIC, 0, &iterdelay, NULL);
do{ // force a move
oldy = y;
oldx = x;
// FIXME he ought be weighted to avoid light; he's a snake after all
int direction = random() % 4;
switch(direction){
case 0: --y; break;
case 1: ++x; break;
case 2: ++y; break;
case 3: --x; break;
for(int s = 0 ; s < snakecount ; ++s){
if(snakey(nc, &snakes[s], dimy, dimx, &iterdelay)){
return NULL;
}
// keep him away from the sides due to width irregularities
if(x < (dimx / 4)){
x = dimx / 4;
}else if(x >= dimx * 3 / 4){
x = dimx * 3 / 4;
}
if(y < 0){
y = 0;
}else if(y >= dimy){
y = dimy - 1;
}
ncplane_cursor_move_yx(n, y, x);
ncplane_at_cursor(n, &c);
// don't allow the snake into the summary zone (test for walls)
if(wall_p(n, &c)){
x = oldx;
y = oldy;
}
}while((oldx == x && oldy == y) || (x == prevx && y == prevy));
prevy = oldy;
prevx = oldx;
}
cell_release(n, &head); // FIXME won't be released when cancelled
cell_release(n, &c); // FIXME won't be released when cancelled
for(i = 0 ; i < sizeof(lightup) / sizeof(*lightup) ; ++i){
cell_release(n, &lightup[i]);
}
}
return NULL;
}
Expand Down Expand Up @@ -306,7 +337,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
}

// Much of this text comes from http://kermitproject.org/utf8.html
int bleachworm_demo(struct notcurses* nc){
int witherworm_demo(struct notcurses* nc){
static const char* strs[] = {
"Война и мир",
"Бра́тья Карама́зовы",
Expand Down Expand Up @@ -537,8 +568,8 @@ int bleachworm_demo(struct notcurses* nc){
NULL
};
const char** s;
const int steps[] = { 0x100, 0x100, 0x40000, 0x10001, };
const int starts[] = { 0x004000, 0x000040, 0x010101, 0x400040, };
const int steps[] = { 0x10040, 0x100, 0x100, 0x10001, };
const int starts[] = { 0x10101, 0x004000, 0x000040, 0x400040, };

struct ncplane* n = notcurses_stdplane(nc);
size_t i;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/egcpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ egcpool_dump(egcpool* pool){
pool->poolused = 0;
}

static inline const char*
egcpool_extended_gcluster(const egcpool* pool, const cell* c){
uint32_t idx = cell_egc_idx(c);
return pool->pool + idx;
}

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 0 additions & 2 deletions src/lib/fade.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ int ncplane_fadein(ncplane* n, const struct timespec* ts){
}
}
}
ncplane_updamage(n);
notcurses_render(n->nc);
uint64_t nextwake = (iter + 1) * nanosecs_step + startns;
struct timespec sleepspec;
Expand Down Expand Up @@ -204,7 +203,6 @@ int ncplane_fadeout(ncplane* n, const struct timespec* ts){
}
}
}
ncplane_updamage(n);
cell* c = &n->defcell;
if(!cell_fg_default_p(c)){
channels_get_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b);
Expand Down
Loading

0 comments on commit 9b81de3

Please sign in to comment.