Add a little optional colour, optionally distinguish worm heads,
and optionally randomise worm lengths. Just exit instead of continuing with a nonsense display if the window shrinks (and for consistency if it grows). Most of the ideas and code from RVP. Bugs and man page mangling from me.
This commit is contained in:
parent
2dbb90eecc
commit
8552e825c7
|
@ -1,4 +1,4 @@
|
|||
.\" $NetBSD: worms.6,v 1.17 2023/04/18 15:02:22 kre Exp $
|
||||
.\" $NetBSD: worms.6,v 1.18 2023/04/26 22:58:09 kre Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1989, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
|
@ -29,7 +29,7 @@
|
|||
.\"
|
||||
.\" @(#)worms.6 8.1 (Berkeley) 5/31/93
|
||||
.\"
|
||||
.Dd April 17, 2023
|
||||
.Dd April 26, 2023
|
||||
.Dt WORMS 6
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -37,7 +37,7 @@
|
|||
.Nd animate worms on a display terminal
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl ft
|
||||
.Op Fl CfHrt
|
||||
.Op Fl d Ar delay
|
||||
.Op Fl l Ar length
|
||||
.Op Fl n Ar number
|
||||
|
@ -50,7 +50,11 @@ version of the DEC-2136 program
|
|||
.Dq worms .
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Fl
|
||||
.Bl -compact -tag -width Fl
|
||||
.Pp
|
||||
.It Fl C
|
||||
Use colours, if available, to make the worms easier to distinguish.
|
||||
.Pp
|
||||
.It Fl d Ar delay
|
||||
Specifies a
|
||||
.Ar delay ,
|
||||
|
@ -58,24 +62,53 @@ in milliseconds, between each update.
|
|||
This is useful for fast terminals.
|
||||
Reasonable values are around 20-200;
|
||||
the default is 20.
|
||||
.Pp
|
||||
.It Fl f
|
||||
Makes a
|
||||
.Dq field
|
||||
for the worm(s) to eat.
|
||||
.Pp
|
||||
.It Fl H
|
||||
Display the head of the worm differently than its body.
|
||||
.Pp
|
||||
.It Fl l Ar length
|
||||
.It Fl l Ar min Ns \(mi Ns Ar max
|
||||
Specifies the
|
||||
.Ar length
|
||||
of each worm; the default is 16, the minimum is 2.
|
||||
In the second form, worm lengths are randomly chosen
|
||||
between
|
||||
.Ar min
|
||||
.Pq which must be at least 2
|
||||
and
|
||||
.Ar max .
|
||||
The
|
||||
.Ar max
|
||||
worm length will be reduced if required by the screen
|
||||
size and the
|
||||
.Ar number
|
||||
of worms selected.
|
||||
This option overrides any earlier
|
||||
.Fl r .
|
||||
.Pp
|
||||
.It Fl n Ar number
|
||||
Specifies the
|
||||
.Ar number
|
||||
of worms; the default is 3.
|
||||
There must be at least one.
|
||||
.Pp
|
||||
.It Fl r
|
||||
Use random lengths for the worms, within a range of
|
||||
sizes chosen to suit the screen size.
|
||||
Note this option overrides any earlier
|
||||
.Fl l .
|
||||
.Pp
|
||||
.It Fl S Ar seed
|
||||
Provide an integer
|
||||
.Ar seed
|
||||
for the random number generator.
|
||||
Specifying zero (0, the default) causes a random seed to be used.
|
||||
.Pp
|
||||
.It Fl t
|
||||
Makes each worm leave a trail behind it.
|
||||
.El
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: worms.c,v 1.29 2023/04/19 09:39:29 kre Exp $ */
|
||||
/* $NetBSD: worms.c,v 1.30 2023/04/26 22:58:09 kre Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1980, 1993
|
||||
|
@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
|
|||
#if 0
|
||||
static char sccsid[] = "@(#)worms.c 8.1 (Berkeley) 5/31/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: worms.c,v 1.29 2023/04/19 09:39:29 kre Exp $");
|
||||
__RCSID("$NetBSD: worms.c,v 1.30 2023/04/26 22:58:09 kre Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
|
@ -71,6 +71,7 @@ __RCSID("$NetBSD: worms.c,v 1.29 2023/04/19 09:39:29 kre Exp $");
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <term.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const struct options {
|
||||
|
@ -86,7 +87,8 @@ static const struct options {
|
|||
{ 3, { 4, 5, 6 } },
|
||||
{ 3, { 5, 6, 7 } },
|
||||
{ 3, { 6, 7, 0 } }
|
||||
}, upper[8] = {
|
||||
},
|
||||
upper[8] = {
|
||||
{ 1, { 1, 0, 0 } },
|
||||
{ 2, { 1, 2, 0 } },
|
||||
{ 0, { 0, 0, 0 } },
|
||||
|
@ -166,50 +168,110 @@ static const struct options {
|
|||
{ 0, { 0, 0, 0 } },
|
||||
{ 0, { 0, 0, 0 } }
|
||||
};
|
||||
|
||||
|
||||
static const char flavor[] = {
|
||||
'O', '*', '#', '$', '%', '0', '@', '~',
|
||||
'+', 'w', ':', '^', '_', '&', 'x', 'o'
|
||||
'O', '*', '#', '$', '%', '0', 'o', '~',
|
||||
'+', 'x', ':', '^', '_', '&', '@', 'w'
|
||||
};
|
||||
static const int flavors = __arraycount(flavor);
|
||||
|
||||
static const short xinc[] = {
|
||||
1, 1, 1, 0, -1, -1, -1, 0
|
||||
}, yinc[] = {
|
||||
-1, 0, 1, 1, 1, 0, -1, -1
|
||||
};
|
||||
static struct worm {
|
||||
int orientation, head;
|
||||
int orientation, head, len;
|
||||
short *xpos, *ypos;
|
||||
chtype ch, attr;
|
||||
} *worm;
|
||||
|
||||
static volatile sig_atomic_t sig_caught = 0;
|
||||
static volatile sig_atomic_t sig_caught;
|
||||
|
||||
static int initclr(int**);
|
||||
static void nomem(void) __dead;
|
||||
static void onsig(int);
|
||||
static int worm_length(int, int);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int x, y, h, n;
|
||||
struct worm *w;
|
||||
int CO, LI, last, bottom, ch, number, trail;
|
||||
int x, y, h, n, nc;
|
||||
int maxlength, minlength;
|
||||
unsigned int seed, delay;
|
||||
const struct options *op;
|
||||
short *ip;
|
||||
int CO, LI, last, bottom, ch, length, number, trail;
|
||||
unsigned int seed;
|
||||
struct worm *w;
|
||||
short **ref;
|
||||
short *ip;
|
||||
const char *field;
|
||||
char *ep;
|
||||
unsigned int delay = 20000;
|
||||
unsigned long ul;
|
||||
unsigned long ul, up;
|
||||
bool argerror = false;
|
||||
bool docolour = false; /* -C, use coloured worms */
|
||||
bool docaput = false; /* -H, show which end of worm is head */
|
||||
int *ctab = NULL;
|
||||
|
||||
length = 16;
|
||||
delay = 20000;
|
||||
maxlength = minlength = 16;
|
||||
number = 3;
|
||||
seed = 0;
|
||||
trail = ' ';
|
||||
field = NULL;
|
||||
seed = 0;
|
||||
while ((ch = getopt(argc, argv, "d:fl:n:S:t")) != -1) {
|
||||
|
||||
if ((ep = getenv("WORMS")) != NULL) {
|
||||
ul = up = 0;
|
||||
while ((ch = *ep++) != '\0') {
|
||||
switch (ch) {
|
||||
case 'C':
|
||||
docolour = !docolour;
|
||||
continue;
|
||||
case 'f':
|
||||
if (field)
|
||||
field = NULL;
|
||||
else
|
||||
field = "WORM";
|
||||
continue;
|
||||
case 'H':
|
||||
docaput = !docaput;
|
||||
continue;
|
||||
case 'r':
|
||||
minlength = 5;
|
||||
maxlength = 0;
|
||||
continue;
|
||||
case 't':
|
||||
if (trail == ' ')
|
||||
trail = '.';
|
||||
else
|
||||
trail = ' ';
|
||||
continue;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
if (up > 1)
|
||||
continue;
|
||||
if (ul >= 100000) /* 1/10 second, in us */
|
||||
continue;
|
||||
ul *= 10;
|
||||
ul += (ch - '0');
|
||||
up = 1;
|
||||
continue;
|
||||
case 'm':
|
||||
if (up == 1 && ul <= 1000)
|
||||
ul *= 1000;
|
||||
up += 2;
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((up & 1) != 0) /* up == 1 || up == 3 */
|
||||
delay = ul;
|
||||
}
|
||||
|
||||
while ((ch = getopt(argc, argv, "Cd:fHl:n:rS:t")) != -1) {
|
||||
switch(ch) {
|
||||
case 'C':
|
||||
docolour = !docolour;
|
||||
continue;
|
||||
case 'd':
|
||||
ul = strtoul(optarg, &ep, 10);
|
||||
if (ep != optarg) {
|
||||
|
@ -239,16 +301,29 @@ main(int argc, char *argv[])
|
|||
delay = (unsigned int)ul;
|
||||
continue;
|
||||
case 'f':
|
||||
field = "WORM";
|
||||
if (field == NULL)
|
||||
field = "WORM";
|
||||
else
|
||||
field = NULL;
|
||||
continue;
|
||||
case 'H':
|
||||
docaput = !docaput;
|
||||
continue;
|
||||
case 'l':
|
||||
ul = strtoul(optarg, &ep, 10);
|
||||
up = ul = strtoul(optarg, &ep, 10);
|
||||
if (ep != optarg) {
|
||||
while (isspace(*(unsigned char *)ep))
|
||||
ep++;
|
||||
if (*ep == '-')
|
||||
up = strtoul(++ep, &ep, 10);
|
||||
}
|
||||
if (ep == optarg || *ep != '\0' ||
|
||||
ul < 2 || ul > 1024) {
|
||||
ul < 2 || up < ul || up > 1024) {
|
||||
errx(1, "-l: invalid length (%s) [%d - %d].",
|
||||
optarg, 2, 1024);
|
||||
}
|
||||
length = (int)ul;
|
||||
minlength = (int)ul;
|
||||
maxlength = (int)up;
|
||||
continue;
|
||||
case 'n':
|
||||
ul = strtoul(optarg, &ep, 10);
|
||||
|
@ -260,6 +335,10 @@ main(int argc, char *argv[])
|
|||
/* upper bound is further limited later */
|
||||
number = (int)ul;
|
||||
continue;
|
||||
case 'r':
|
||||
minlength = 5;
|
||||
maxlength = 0;
|
||||
continue;
|
||||
case 'S':
|
||||
ul = strtoul(optarg, &ep, 0);
|
||||
if (ep == optarg || *ep != '\0' ||
|
||||
|
@ -269,7 +348,10 @@ main(int argc, char *argv[])
|
|||
seed = (unsigned int)ul;
|
||||
continue;
|
||||
case 't':
|
||||
trail = '.';
|
||||
if (trail == ' ')
|
||||
trail = '.';
|
||||
else
|
||||
trail = ' ';
|
||||
continue;
|
||||
case '?':
|
||||
default:
|
||||
|
@ -281,12 +363,13 @@ main(int argc, char *argv[])
|
|||
|
||||
if (argerror || argc > optind)
|
||||
errx(1,
|
||||
"Usage: worms [-ft] [-d delay] [-l length] [-n number]");
|
||||
"Usage: worms [-CfHrt] [-d delay] [-l length] [-n number]");
|
||||
/* -S omitted deliberately, not useful often enough */
|
||||
|
||||
if (!initscr())
|
||||
errx(1, "couldn't initialize screen");
|
||||
curs_set(0);
|
||||
nc = docolour ? initclr(&ctab) : 0;
|
||||
CO = COLS;
|
||||
LI = LINES;
|
||||
|
||||
|
@ -294,7 +377,12 @@ main(int argc, char *argv[])
|
|||
endwin();
|
||||
errx(1, "screen must be a rectangle, not (%dx%d)", CO, LI);
|
||||
}
|
||||
if (CO >= INT_MAX / LI) {
|
||||
|
||||
/*
|
||||
* The "sizeof(short *)" noise is to abslutely guarantee
|
||||
* that a LI * CO * sizeof(short *) cannot overflow an int
|
||||
*/
|
||||
if (CO >= (int)(INT_MAX / (2 * sizeof(short *)) / LI)) {
|
||||
endwin();
|
||||
errx(1, "screen (%dx%d) too large for worms", CO, LI);
|
||||
}
|
||||
|
@ -311,14 +399,25 @@ main(int argc, char *argv[])
|
|||
errx(1, "screen (%dx%d) too small for worms", CO, LI);
|
||||
}
|
||||
|
||||
if (maxlength == 0)
|
||||
maxlength = minlength + (CO * LI / 40);
|
||||
|
||||
ul = (unsigned long)CO * LI;
|
||||
if ((unsigned long)length > ul / 20) {
|
||||
if ((unsigned long)maxlength > ul / 20) {
|
||||
endwin();
|
||||
errx(1, "-l: worms too long (%d) for screen; max: %lu",
|
||||
length, ul / 20);
|
||||
maxlength, ul / 20);
|
||||
}
|
||||
|
||||
ul /= maxlength * 3; /* no more than 33% arena occupancy */
|
||||
|
||||
if ((unsigned long)(unsigned)number > ul && maxlength > minlength) {
|
||||
maxlength = CO * LI / 3 / number;
|
||||
if (maxlength < minlength)
|
||||
maxlength = minlength;
|
||||
ul = (CO * LI) / ((minlength + maxlength)/2 * 3);;
|
||||
}
|
||||
|
||||
ul /= (length * 3); /* no more than 33% arena occupancy */
|
||||
if ((unsigned long)(unsigned)number > ul) {
|
||||
endwin();
|
||||
errx(1, "-n: too many worms (%d) max: %lu", number, ul);
|
||||
|
@ -326,37 +425,48 @@ main(int argc, char *argv[])
|
|||
if (!(worm = calloc((size_t)number, sizeof(struct worm))))
|
||||
nomem();
|
||||
|
||||
srandom(seed ? seed : arc4random());
|
||||
|
||||
last = CO - 1;
|
||||
bottom = LI - 1;
|
||||
if (!(ip = malloc((size_t)(LI * CO * sizeof(short)))))
|
||||
|
||||
if (!(ip = calloc(LI * CO, sizeof(short))))
|
||||
nomem();
|
||||
if (!(ref = malloc((size_t)(LI * sizeof(short *)))))
|
||||
if (!(ref = malloc((size_t)LI * sizeof(short *))))
|
||||
nomem();
|
||||
for (n = 0; n < LI; ++n) {
|
||||
ref[n] = ip;
|
||||
ip += CO;
|
||||
}
|
||||
for (ip = ref[0], n = LI * CO; --n >= 0;)
|
||||
*ip++ = 0;
|
||||
|
||||
for (n = number, w = &worm[0]; --n >= 0; w++) {
|
||||
int i;
|
||||
|
||||
w->orientation = w->head = 0;
|
||||
if (!(ip = malloc((size_t)(length * sizeof(short)))))
|
||||
w->len = worm_length(minlength, maxlength);
|
||||
w->attr = nc ? ctab[n % nc] : 0;
|
||||
i = (nc && number > flavors ? n / nc : n) % flavors;
|
||||
w->ch = flavor[i];
|
||||
|
||||
if (!(ip = malloc((size_t)(w->len * sizeof(short)))))
|
||||
nomem();
|
||||
w->xpos = ip;
|
||||
for (x = length; --x >= 0;)
|
||||
for (x = w->len; --x >= 0;)
|
||||
*ip++ = -1;
|
||||
if (!(ip = malloc((size_t)(length * sizeof(short)))))
|
||||
if (!(ip = malloc((size_t)(w->len * sizeof(short)))))
|
||||
nomem();
|
||||
w->ypos = ip;
|
||||
for (y = length; --y >= 0;)
|
||||
for (y = w->len; --y >= 0;)
|
||||
*ip++ = -1;
|
||||
}
|
||||
free(ctab); /* not needed any more */
|
||||
|
||||
(void)signal(SIGHUP, onsig);
|
||||
(void)signal(SIGINT, onsig);
|
||||
(void)signal(SIGQUIT, onsig);
|
||||
(void)signal(SIGTSTP, onsig);
|
||||
(void)signal(SIGTERM, onsig);
|
||||
(void)signal(SIGTSTP, onsig);
|
||||
(void)signal(SIGWINCH, onsig);
|
||||
|
||||
if (field) {
|
||||
const char *p = field;
|
||||
|
@ -367,10 +477,9 @@ main(int argc, char *argv[])
|
|||
if (!*p)
|
||||
p = field;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
srandom(seed ? seed : arc4random());
|
||||
|
||||
for (;;) {
|
||||
refresh();
|
||||
if (sig_caught) {
|
||||
|
@ -384,24 +493,23 @@ main(int argc, char *argv[])
|
|||
sleep(delay / 1000000);
|
||||
}
|
||||
for (n = 0, w = &worm[0]; n < number; n++, w++) {
|
||||
chtype c = docaput ? (w->ch == '@' ? '0' : '@') : w->ch;
|
||||
|
||||
if ((x = w->xpos[h = w->head]) < 0) {
|
||||
mvaddch(y = w->ypos[h] = bottom,
|
||||
x = w->xpos[h] = 0,
|
||||
flavor[n % sizeof(flavor)]);
|
||||
x = w->xpos[h] = 0, c | w->attr);
|
||||
ref[y][x]++;
|
||||
}
|
||||
else
|
||||
} else
|
||||
y = w->ypos[h];
|
||||
if (++h == length)
|
||||
if (++h == w->len)
|
||||
h = 0;
|
||||
if (w->xpos[w->head = h] >= 0) {
|
||||
int x1, y1;
|
||||
|
||||
x1 = w->xpos[h];
|
||||
y1 = w->ypos[h];
|
||||
if (--ref[y1][x1] == 0) {
|
||||
if (--ref[y1][x1] == 0)
|
||||
mvaddch(y1, x1, trail);
|
||||
}
|
||||
}
|
||||
|
||||
op = &(!x
|
||||
|
@ -417,7 +525,6 @@ main(int argc, char *argv[])
|
|||
|
||||
switch (op->nopts) {
|
||||
case 0:
|
||||
refresh();
|
||||
endwin();
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
|
@ -430,12 +537,81 @@ main(int argc, char *argv[])
|
|||
}
|
||||
mvaddch(y += yinc[w->orientation],
|
||||
x += xinc[w->orientation],
|
||||
flavor[n % sizeof(flavor)]);
|
||||
c | w->attr);
|
||||
ref[w->ypos[h] = y][w->xpos[h] = x]++;
|
||||
if (docaput && w->len > 1) {
|
||||
int prev = (h ? h : w->len) - 1;
|
||||
|
||||
mvaddch(w->ypos[prev], w->xpos[prev],
|
||||
w->ch | w->attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
initclr(int** ctab)
|
||||
{
|
||||
int *ip, clr[] = {
|
||||
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
|
||||
COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE
|
||||
}, attr[] = {
|
||||
A_NORMAL, A_BOLD, A_DIM
|
||||
};
|
||||
int nattr = __arraycount(attr);
|
||||
int nclr = __arraycount(clr);
|
||||
int nc = 0;
|
||||
|
||||
/* terminfo first */
|
||||
char* s;
|
||||
bool canbold = (s = tigetstr("bold")) != (char* )-1 && s != NULL;
|
||||
bool candim = (s = tigetstr("dim")) != (char* )-1 && s != NULL;
|
||||
|
||||
#ifdef DO_TERMCAP
|
||||
/* termcap if terminfo fails */
|
||||
canbold = canbold || (s = tgetstr("md", NULL)) != NULL;
|
||||
candim = candim || (s = tgetstr("mh", NULL)) != NULL;
|
||||
#endif
|
||||
|
||||
if (has_colors() == FALSE)
|
||||
return 0;
|
||||
use_default_colors();
|
||||
if (start_color() == ERR)
|
||||
return 0;
|
||||
if ((*ctab = calloc(COLOR_PAIRS, sizeof(int))) == NULL)
|
||||
nomem();
|
||||
ip = *ctab;
|
||||
|
||||
for (int i = 0; i < nattr; i++) {
|
||||
if (!canbold && attr[i] == A_BOLD)
|
||||
continue;
|
||||
if (!candim && attr[i] == A_DIM)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < nclr; j++) {
|
||||
if (clr[j] == COLOR_BLACK && attr[i] != A_BOLD)
|
||||
continue; /* invisible */
|
||||
if (nc + 1 >= COLOR_PAIRS)
|
||||
break;
|
||||
if (init_pair(nc + 1, clr[j], -1) == ERR)
|
||||
break;
|
||||
*ip++ = COLOR_PAIR(nc + 1) | attr[i];
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
static int
|
||||
worm_length(int low, int high)
|
||||
{
|
||||
if (low >= high)
|
||||
return low;
|
||||
|
||||
return low + (random() % (high - low + 1));
|
||||
}
|
||||
|
||||
static void
|
||||
onsig(int signo __unused)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue