[net.sources] A windows program

chris@umcp-cs.UUCP (07/08/83)

Well, now that you've all been thinking "what do we do with this
wonderful window software" (either that or "why is he sending out this
junk") I have a sample program.  It's a variation on that old favorite,
Towers of Hanoi.  This one has a twist!  It does any number of pegs
from 2 to 40 (though you'll never fit 40 on a screen) with up to 40
discs.  It shows off almost no capabilities, using neither inverse
video nor fancy updates (well, it does do character ins/del on some
terminals); it's not at all useful; but it's fun.

It's interesting to watch it do 10 discs on 3 pegs, compared to 10
discs on 4 pegs (use "hanoi -qq 10 3" and "hanoi -qq 10 4"), to see
just how the complexity of the problem changes.

By the way, we haven't been able to prove that the algorithm used
generates the mininal number of moves.  Can anyone either prove it or
come up with a better algorithm?

Anyway, here's hanoi.c.

				- Chris

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting hanoi.c'
sed 's/^X//' <<'//go.sysin dd *' >hanoi.c
X#ifndef lint
Xstatic char sccsid[] = "@(#)hanoi.c	U of Maryland ACT 24-Apr-1983";
X#endif
X
X#include <stdio.h>
X#include <local/window.h>
X
X#define	MAXDISCS	40
X#define	MAXPEGS		40
X#define	MAXWINS		10
X
X/* Upper left corner of given peg */
X#define	WIN(p)	wins[(p)/perrow]
X#define	TR(p)	0
X		/* (((p)/perrow) * discs2) */
X#define TC(p)	(((p)%perrow) * widest)
X
XWin *wins[MAXWINS];
Xint discs, pegs, rows, perrow, widest,
X    towerdepth[MAXPEGS], qflag, discs2, K[MAXDISCS][MAXPEGS];
X
Xmain (argc, argv)
Xregister argc;
Xregister char **argv;
X{
X	register i, j;
X	char *prog = *argv;
X	int nr, nc;
X	char usepegs[MAXPEGS];
X
X	while (argc > 1 && argv[1][0] == '-') {
X		argc--;
X		++*++argv;
X		while (**argv) switch (*(*argv)++) {
X		case 'q':
X			qflag++;
X			break;
X		default:
X			fprintf (stderr, "%s: option '%c' not recognized.\n",
X				prog, argv[0][-1]);
Xusage:
X			fprintf (stderr, "Usage: %s [-q] [ndisks [npegs]]",
X				prog);
X			exit (1);
X		}
X	}
X	if (argc == 1)
X		discs = 8, pegs = 4;
X	else if (argc == 2)
X		discs = atoi (argv[1]), pegs = 4;
X	else if (argc == 3)
X		discs = atoi (argv[1]), pegs = atoi (argv[2]);
X	else
X		goto usage;
X	if (discs < 2)
X		discs = 2;
X	if (pegs < 3)
X		pegs = 3;
X	if (Winit (0, 0)) {
X		fprintf (stderr, "%s: can't run windows, forget it\n", prog);
X		exit (1);
X	}
X	Wscreensize (&nr, &nc);
X	if (discs > MAXDISCS)
X		discs = MAXDISCS;
X	if ((discs2 = discs + 2) > nr) {
X		discs = nr - 2;
X		discs2 = nr;
X	}
X	if (pegs > MAXPEGS)
X		pegs = MAXPEGS;
X	widest = (discs + 1) * 2;
X	rows = nr / discs2;	/* How many rows will fit? */
X	perrow = nc / widest;	/* How many discs will fit per row? */
X	if (perrow == 0) {
Xtoobig:
X		Wcleanup ();
X		fprintf (stderr, "%s: try smaller values\n", prog);
X		exit (1);
X	}
X	if (perrow * rows < pegs) {
X		pegs = perrow * rows;
X		if (pegs < 3)
X			goto toobig;
X	}
X	rows = (pegs + perrow - 1) / perrow;
X
X	nc = widest * (rows == 1 ? pegs : perrow);
X	nr -= rows * discs2;
X	if (nr)
X		nr--;
X	for (i = 0; i < rows; i++) {
X		wins[i] = Wopen (0, 0, nr + i * discs2, nc, discs2, 0, 0);
X		if (wins[i] == 0) {
X			Wcleanup ();
X			fprintf (stderr, "%s: window open failed\n", prog);
X			exit (1);
X		}
X		Wwrap (wins[i], 0);
X		Woncursor (wins[i], 0);
X	}
X
X	for (i = 0; i < pegs; i++) {
X		register Win *w = WIN(i);
X
X		WAcursor (w, TR(i) + discs + 1, TC(i));
X		for (j = widest/2 - 1; --j >= 0; )
X			Wputc ('-', w);
X		Wputc ('+', w);
X		for (j = widest/2 - 1; --j >= 0; )
X			Wputc ('-', w);
X	}
X	for (i = 1; i <= discs; i++)
X		for (j = 0; j < pegs; j++)
X			Draw (i, i, j, 0, j == 0);
X	i = pegs - 1;
X	for (j = 1; j < i; j++)
X		usepegs[j] = 1;
X	usepegs[0] = 0;
X	usepegs[i] = 0;
X	towerdepth[0] = discs;
X	Wrefresh (0);
X	SeedKArray (discs, pegs - 2);
X	MoveTower (discs, 0, pegs - 1, usepegs, 0);
X	Wexit (0);
X}
X
XSeedKArray (n, p)
Xregister n, p;
X{
X	register i, l;
X
X	for (l = 0; ! (l && K[l-1][p] > n); l++)
X		for (i = 0; i <= p; i++)
X			K[l][i] = i==0 || l==0 ? 1 : K[l-1][i]+K[l][i-1];
X}
X
XMoveTower (n, from, to, others, base)
Xregister n, from, to;
Xregister char *others;
Xint base;
X{
X	char lothers[MAXPEGS];
X	int nums[MAXPEGS], len = 0, b = base;
X	register i;
X
X	for (i = 0; i < pegs; i++)
X		if (lothers[i] = others[i])
X			len++;
X	spread (n, len, nums, others);
X	lothers[to] = 1;
X	for (i = 0; i < pegs; i++) {
X		if (nums[i]) {
X			lothers[i] = 0;
X			MoveTower (nums[i], from, i, lothers, b);
X			b += nums[i];
X		}
X	}
X	MoveADisc (n + base, from, to);
X	for (i = 0; i < pegs; i++)
X		lothers[i] = 0;
X	lothers[from] = 1;
X	for (i = pegs; --i >= 0; ) {
X		if (nums[i]) {
X			b -= nums[i];
X			MoveTower (nums[i], i, to, lothers, b);
X			lothers[i] = 1;
X		}
X	}
X}
X
Xspread (n, p, nums, others)
Xregister n, p, *nums;
Xregister char *others;
X{
X	register i, l, extra, tmp;
X
X	for (l = 0; K[l][p] <= n; l++)
X		;
X	l--;
X	extra = l >= 0 ? n - K[l][p] : (l = 0);
X	for (i = 0; i < pegs; i++) {
X		if (!others[i]) {
X			nums[i] = 0;
X			continue;
X		}
X		nums[i] = l ? K[l-1][p] : 0;
X		if (extra) {
X			tmp = K[l][p] - nums[i];
X			if (tmp > extra)
X				tmp = extra;
X			extra -= tmp;
X			nums[i] += tmp;
X		}
X		p--;
X	}
X}
X
XDraw (disc, yoffset, peg, xoffset, c)
Xregister disc, peg, c;
Xint yoffset, xoffset;
X{
X	register i;
X	register Win *w = WIN(peg);
X
X	c = c ? 'x' : ' ';
X	WAcursor (w, TR(peg)+yoffset, TC(peg)+xoffset + widest/2 - disc - 1);
X	for (i = disc; --i >= 0; )
X		Wputc (c, w);
X	Wputc (yoffset ? '|' : c, w);
X	for (i = disc; --i >= 0; )
X		Wputc (c, w);
X}
X
XMoveADisc (n, from, to)
Xregister n, from, to;
X{
X	register i, j, k;
X
X	/* Should it just 'blip' to where it goes? */
X	if (qflag > 1) {
X		Draw (n, discs - --towerdepth[from], from, 0, 0);
X		Draw (n, discs - towerdepth[to]++, to, 0, 1);
X		Wrefresh (0);
X		return;
X	}
X
X	/* Draw the disc moving up the peg */
X	for (i = discs - --towerdepth[from]; --i >= 0; ) {
X		Draw (n, i+1, from, 0, 0);
X		Draw (n, i,   from, 0, 1);
X		Wrefresh (0);
X	}
X	if (!qflag && WIN(from) == WIN(to)) {
X		/* Now move the disc along the top */
X
X		k = to - from;
X		j = k < 0 ? -1 : 1;
X		k *= widest;
X		for (i = j; i != k; i += j) {
X			Draw (n, 0, from, i - j, 0);
X			Draw (n, 0, from, i, 1);
X			Wrefresh (0);
X		}
X		Draw (n, 0, to, -j, 0);
X		Draw (n, 0, to, 0, 1);
X		Wrefresh (0);
X	}
X	else {
X		/* Just 'blip' it from one tower to the other */
X
X		Draw (n, 0, from, 0, 0);
X		Draw (n, 0, to, 0, 1);
X		Wrefresh (0);
X	}
X
X	/* And now move it down the 'to' peg */
X
X	j = discs - towerdepth[to]++;
X	for (i = 0; i < j; i++) {
X		Draw (n, i,   to, 0, 0);
X		Draw (n, i+1, to, 0, 1);
X		Wrefresh (0);
X	}
X}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 hanoi.c
	/bin/echo -n '	'; /bin/ls -ld hanoi.c
fi
-- 
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs
ARPA:	chris.umcp-cs@UDel-Relay