Initial commit.

This commit is contained in:
Bruce Hill 2019-01-02 20:30:11 -08:00
commit 6b20ca721f
2 changed files with 283 additions and 0 deletions

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
PREFIX=
all: nuke
nuke: nuke.c
cc nuke.c -lncurses -O3 -o nuke
clean:
rm -f nuke
install: nuke
@prefix="$(PREFIX)"; \
if [[ ! $$prefix ]]; then \
read -p $$'\033[1mWhere do you want to install? (default: /usr/local) \033[0m' prefix; \
fi; \
if [[ ! $$prefix ]]; then \
prefix="/usr/local"; \
fi; \
mkdir -pv $$prefix/bin; \
cp -v nuke $$prefix/bin/

262
nuke.c Normal file
View File

@ -0,0 +1,262 @@
/**
* ASCII explosion from squeamish ossifrage at:
* https://codegolf.stackexchange.com/questions/24462/display-the-explosion-of-a-star-in-ascii-art/24554#24554
*/
#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <math.h>
#include <poll.h>
#define NUM_FRAMES 150
#define NUM_BLOBS 800
#define PERSPECTIVE 50.0
#define ESCDELAY 10
typedef struct {
double x,y,z;
} spaceblob;
double prng() {
static long long s=1;
s = s * 1488248101 + 981577151;
return ((s % 65536) - 32768) / 32768.0;
}
static void sighandler(int sig) {
(void)sig;
curs_set(1);
endwin();
exit(EXIT_FAILURE);
}
static char* get_piped_input() {
struct pollfd desc;
desc.fd = STDIN_FILENO;
desc.events = POLLIN;
int ret = poll(&desc, 1, 50);
if (ret == 0) {
return NULL;
} else if (ret < 0) {
perror("poll");
raise(SIGINT);
}
size_t chunk = 1024;
size_t capacity = chunk;
size_t size = 0;
char *input = malloc(capacity);
for (size_t consumed; (consumed = fread(&input[size], 1, chunk, stdin)); size += consumed) {
if (consumed == chunk) {
chunk *= 2;
capacity += chunk;
input = realloc(input, capacity);
}
}
return input;
}
/* Populate `lines` with (at most `max_lines`) lines from stdin, each at most
* `max_line` characters long.
* Return length of the longest line on success, -1 otherwise.
* *
static int get_piped_lines(char **lines[], size_t *max_lines, ssize_t *max_line) {
struct pollfd desc;
desc.fd = STDIN_FILENO;
desc.events = POLLIN;
int ret = poll(&desc, 1, 50);
if (ret > 0) {
ssize_t num_lines, longest_line = 0;
for (num_lines = 0; num_lines < *max_lines; num_lines++) {
size_t n = *max_line;
lines[num_lines] = (char*)malloc(n+1);
ssize_t line_len = getline(&lines[num_lines], &n, stdin);
while (lines[num_lines][line_len-1] == '\n')
lines[num_lines][--line_len] = '\0';
if (line_len == -1) break;
if (line_len > longest_line) {
longest_line = line_len > *max_line ? *max_line : line_len;
}
}
*max_lines = num_lines;
*max_line = longest_line;
return 0;
} else if (ret < 0) {
perror("poll");
raise(SIGINT);
}
*max_lines = 0;
*max_line = 0;
return 0;
}
*/
int main(int argc, char *argv[]) {
char *frames[NUM_FRAMES], *frame;
int i,j,x,y,z,v,rows,cols,ith,i0;
int maxx,minx,maxy,miny,delay=1E5;
double bx,by,bz,br,r,th,t;
spaceblob *blobs;
char *input = get_piped_input();
if (!input) goto exit_failure;
/* Initialize ncurses and get window dimensions */
//initscr();
char* term_type = getenv("TERM");
if (term_type == NULL || *term_type == '\0') {
term_type = "unknown";
}
FILE* term_in = fopen("/dev/tty", "r");
if (term_in == NULL) {
perror("fopen(/dev/tty)");
exit(EXIT_FAILURE);
}
SCREEN* main_screen = newterm(term_type, stdout, term_in);
set_term(main_screen);
signal(SIGINT, sighandler);
start_color();
const int BLACK_ON_RED = 1;
init_pair(BLACK_ON_RED, COLOR_BLACK, COLOR_RED);
getmaxyx(stdscr,rows,cols);
minx = -cols / 2;
maxx = cols+minx-1;
miny = -rows / 2;
maxy = rows+miny-1;
/* Generate random blob coordinates */
blobs = (spaceblob*)malloc(NUM_BLOBS * sizeof(spaceblob));
for (i=0; i<NUM_BLOBS; i++) {
bx = prng();
by = prng();
bz = prng();
br = sqrt(bx*bx + by*by + bz*bz);
blobs[i].x = (bx / br) * (1.3 + 0.2 * prng());
blobs[i].y = (0.5 * by / br) * (1.3 + 0.2 * prng());;
blobs[i].z = (bz / br) * (1.3 + 0.2 * prng());;
}
/* Generate animation frames */
for (i=0; i<NUM_FRAMES; i++) {
t = (1. * i) / NUM_FRAMES;
frame = frames[i] = (char*)malloc(cols * rows + 1);
if (i == 0) {
memset(frame, ' ', cols*rows);
int num_lines = 1, max_line = 0;
int linelen = 0;
for (char *p = input; *p && (num_lines < rows); ++p) {
if (*p == '\n') {
if (linelen > max_line)
max_line = linelen;
linelen = 0;
num_lines++;
} else {
linelen++;
}
}
for (int l = 0, line = 0; input[l] && line < num_lines; line++) {
int eol = l;
while (input[eol] && input[eol] != '\n') eol++;
memcpy(&frame[(rows/2 - num_lines/2 + line)*cols + cols/2 - max_line/2],
&input[l], cols < eol-l ? cols : eol-l);
l = input[eol] == '\n' ? eol + 1 : eol;
}
frame += cols*rows;
} else {
char *frame0 = frames[0];
for (y=miny; y<=maxy; y++) {
for (x=minx; x<=maxx; x++) {
char frame0c = *(frame0++);
/* Show expanding star in next 7 frames */
if (i<8) {
r = sqrt(x*x + 4*y*y);
*frame++ = (r < i*2) ? '@' : frame0c;
continue;
}
/* Otherwise show blast wave */
r = sqrt(x*x + 4*y*y) * (0.5 + (prng()/3.0)*cos(16.*atan2(y*2.+0.01,x+0.01))*.3);
ith = 32 + th * 32. * M_1_PI;
v = i - r - 7;
if (v<0) *frame++ = (i<19)?"%@W#H=+~-:."[i-8]:frame0c; /* initial flash */
else if (v<20) *frame++ = " .:!HIOMW#%$&@08O=+-"[v];
else *frame++=' ';
}
}
}
/* Add blobs with perspective effect */
if (i>6) {
i0 = i-6;
for (j=0; j<NUM_BLOBS; j++) {
bx = blobs[j].x * i0;
by = blobs[j].y * i0;
bz = blobs[j].z * i0;
if (bz<5-PERSPECTIVE || bz>PERSPECTIVE) continue;
x = cols / 2 + bx * PERSPECTIVE / (bz+PERSPECTIVE);
y = rows / 2 + by * PERSPECTIVE / (bz+PERSPECTIVE);
if (x>=0 && x<cols && y>=0 && y<rows)
frames[i][x+y*cols] = (bz>40) ? '.' : (bz>-20) ? 'o' : '@';
}
}
/* Terminate the text string for this frame */
*frame = '\0';
}
/* Now play back the frames in sequence */
curs_set(0); /* hide text cursor (supposedly) */
noecho();
for (i=0; i<NUM_FRAMES; i++) {
erase();
mvaddstr(0,0,frames[i]);
refresh();
if (i == 0) {
/* Display confirmation */
attron(COLOR_PAIR(BLACK_ON_RED));
const char confirm[] = " FIRE NUKE? y/n ";
mvprintw((rows*3)/4, cols/2 - strlen(confirm)/2, "%s", confirm);
attroff(COLOR_PAIR(BLACK_ON_RED));
refresh();
if (getch() != 'y') {
goto exit_failure;
}
erase();
mvaddstr(0,0,frames[i]);
refresh();
timeout(10);
}
/* Quit early? */
int ch = getch();
if (ch == 'q' || ch == 27) {
goto exit_success;
}
usleep(delay);
delay=16666; /* Change to 60fps after first frame */
}
exit_success:
curs_set(1); /* unhide cursor */
endwin(); /* Exit ncurses */
if (!isatty(fileno(stdout))) {
// Print stdin to stdout
puts(input);
}
return EXIT_SUCCESS;
exit_failure:
curs_set(1); /* unhide cursor */
endwin(); /* Exit ncurses */
return EXIT_FAILURE;
}