Module Name: src
Committed By: kre
Date: Wed Apr 26 22:58:09 UTC 2023
Modified Files:
src/games/worms: worms.6 worms.c
Log Message:
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.
To generate a diff of this commit:
cvs rdiff -u -r1.17 -r1.18 src/games/worms/worms.6
cvs rdiff -u -r1.29 -r1.30 src/games/worms/worms.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/games/worms/worms.6
diff -u src/games/worms/worms.6:1.17 src/games/worms/worms.6:1.18
--- src/games/worms/worms.6:1.17 Tue Apr 18 15:02:22 2023
+++ src/games/worms/worms.6 Wed Apr 26 22:58:09 2023
@@ -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
Index: src/games/worms/worms.c
diff -u src/games/worms/worms.c:1.29 src/games/worms/worms.c:1.30
--- src/games/worms/worms.c:1.29 Wed Apr 19 09:39:29 2023
+++ src/games/worms/worms.c Wed Apr 26 22:58:09 2023
@@ -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, 19
#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
#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,10 +537,79 @@ 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