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

Reply via email to