🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: mod.mac.sources
1 messages
1 total messages Started by macintosh Sat, 31 Jan 1987 07:00
BlobDemo3 - Blob Manager Demo source (part 3 of 5)
#37
Author: macintosh
Date: Sat, 31 Jan 1987 07:00
1324 lines
32407 bytes
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	BlobDemo/DemoPong.c
#	BlobDemo/DemoPyramid.c
#	BlobDemo/DemoRadix.c
mkdir BlobDemo
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoPong.c
X/*
X	Blob Manager Demonstration:  Pong hau k'i module
X
X	This is an oriental game played on a 5-position board.  Each player
X	has 2 pieces, and the goal is to position to pieces so that the
X	other player cannot move.  Pieces may be moved onto the empty
X	position only if the two are connected by a line.
X
X	*     *
X	|\  / |
X	|  *  |
X	|/  \ |
X	*-----*
X
X	Since it's so simple, the game gives no feedback.  You simply
X	cannot move if the game is over.  The reset "button" can be
X	used to start over.
X
X	5 August 1986		Paul DuBois
X*/
X
X
X# include	"BlobDemo.h"
X# include	<ToolboxUtil.h>
X
X
X# define	pieceSize	20		/* size of each piece */
X# define	hOff		15		/* top left corner of playing area */
X# define	vOff		20
X# define	hSize		80		/* size of playing area */
X# define	vSize		60
X# define	vMesg		100
X
X
Xstatic GrafPtr			pongPort;
X
Xstatic BlobSetHandle	boardBlobs = nil;
Xstatic BlobHandle		board[5];
Xstatic BlobSetHandle	donors = nil;
Xstatic BlobHandle		current;
Xstatic BlobHandle		previous;
Xstatic BlobSetHandle	misc;
Xstatic BlobHandle		resetBlob;	/* simulated control */
Xstatic Str255			statusStr;
Xstatic BlobHandle		statusBlob;
X
X
X/*
X	Define center points of each position
X*/
X
Xstatic Point			intersection[5] =
X{
X	{ vOff, hOff },
X	{ vOff, hOff + hSize },
X	{ vOff + vSize/2, hOff + hSize/2 },
X	{ vOff + vSize, hOff },
X	{ vOff + vSize, hOff + hSize }
X};
X
X/*
X	Define connections between positions
X*/
X
Xstatic Boolean			connected[5][5] =
X{
X	{ false,	false,	true,	true,	false },
X	{ false,	false,	true,	false,	true },
X	{ true,	true,	false,	true,	true },
X	{ true,	false,	true,	false,	true },
X	{ false,	true,	true,	true,	false } 
X};
X	
X
Xstatic StatusMesg (s, b)
XStr255		s;
XBlobHandle	b;
X{
XRect	r;
X
X	SetRect (&r, 0, vMesg, 60, vMesg + 20);
X	TextBox (s+1, (long) s[0], &r, 2);
X	GlueGlob (b, GetBlobHandle (misc, 0));
X	StrCpy (statusStr, s);
X	statusBlob = b;
X}
X
X
X/*
X	The donor set consists of two blobs, each filled with a different
X	pattern
X*/
X
Xstatic MakeDonors ()
X{
XBlobHandle	b;
XRect		r;
XRgnHandle	rgn;
XPattern		p;
Xint			i;
X
X	donors = NewBlobSet ();
X	for (i = 9; i <= 16; i += 7)	/* use patterns 9, 16 */
X	{
X		rgn = NewRgn ();
X		OpenRgn ();
X		SetRect (&r, 0, 0, pieceSize, pieceSize);
X		FrameOval (&r);
X		CloseRgn (rgn);
X
X		b = NewBlob (donors, false, infiniteGlue, false, 0L);
X		OpenBlob ();
X		GetIndPattern (&p, sysPatListID, i);
X		FillOval (&r, p);
X		FrameOval (&r);
X		CloseRgnBlob (b, rgn, rgn);
X		DisposeRgn (rgn);
X	}
X}
X
X
Xstatic BlobHandle MakeReceptor (bSet, h, v)
XBlobSetHandle	bSet;
Xint				h, v;
X{
XBlobHandle	b;
XRect		r;
XRgnHandle	rgn;
X
X	SetRect (&r, h, v, h + pieceSize, v + pieceSize);
X	rgn = NewRgn ();
X	OpenRgn ();
X	FrameOval (&r);
X	CloseRgn (rgn);
X
X	b = NewBlob (bSet, false, 0, false, 0L);
X	OpenBlob ();
X	EraseOval (&r);
X	FrameOval (&r);
X	CloseRgnBlob (b, rgn, rgn);
X	DisposeRgn (rgn);
X	return (b);
X}
X
X
Xstatic MakeBoard ()
X{
Xint			i;
XRect		r;
X
X	boardBlobs = NewBlobSet ();
X	for (i = 0; i < 5; i++)
X	{
X		board[i] = MakeReceptor (boardBlobs,
X									intersection[i].h - pieceSize/2,
X									intersection[i].v - pieceSize/2);
X	}
X
X	misc = NewBlobSet ();
X	(void) MakeReceptor (misc, 60, vMesg - 2);
X	SetRect (&r, 0, 0, 20, 90);
X	OffsetRect (&r, pongPort->portRect.right - 25, 5);
X	resetBlob = NewVButtonBlob (misc, &r, "\pReset", false, 0L);
X}
X
X
X/*
X	Set the board to the initial configuration
X*/
X
Xstatic Reset ()
X{
Xint		i;
X
X	UnglueGlobSet (boardBlobs);		/* clear all globs */
X	GlueGlob (GetBlobHandle (donors, 0), board[0]);
X	GlueGlob (GetBlobHandle (donors, 0), board[1]);
X	GlueGlob (GetBlobHandle (donors, 1), board[3]);
X	GlueGlob (GetBlobHandle (donors, 1), board[4]);
X	current = FirstBlob (donors);
X	previous = NextBlob (current);
X	StatusMesg ("\pMove:", current);
X	HiliteBlob (resetBlob, inDragBlob, dimDraw);
X}
X
X
X/*
X	Given a blob handle, find the board position that corresponds to it.
X*/
X
Xstatic BoardPos (b)
XBlobHandle	b;
X{
Xint		i;
X
X	for (i = 0; i < 5; ++i)
X	{
X		if (board[i] == b)
X			return (i);
X	}
X	/* shouldn't ever get here */
X}
X
X
X/*
X	Test whether a piece can move or not.  This is true only if
X	the current position is connected to the empty position (of which
X	there is only one).
X*/
X
Xstatic Boolean CanMove (n)
Xint		n;
X{
Xint		i;
X
X	for (i = 0; i < 5; ++i)
X	{
X		if (i != n && connected [i][n] && BGlob (board[i]) == nil)
X			return (true);
X	}
X	return (false);
X}
X
X
X/*
X	See whether the game is over or not.  It's over if no piece 
X	of the current player can move.
X*/
X
Xstatic Boolean GameOver ()
X{
Xint		i;
X
X	for (i = 0; i < 5; ++i)
X	{
X		if (BGlob (board[i]) == current && CanMove (i))
X			return (false);
X	}
X	return (true);
X}
X
X
X
Xstatic Mouse (pt, t, mods)
XPoint	pt;
Xlong	t;
Xint		mods;
X{
XBlobHandle	b;
Xint			i;
X
X	if (TestBlob (resetBlob, pt) == inDragBlob)
X	{
X		if (BTrackMouse (resetBlob, pt, inFullBlob))
X			Reset ();
X	}
X	else
X	{
X		BlobClick (pt, t, nil, boardBlobs);
X		if (BClickResult () == bcXfer)
X		{
X			b = current;				/* switch player */
X			current = previous;
X			previous = b;
X			if (GameOver ())			/* see if board is locked */
X			{
X				StatusMesg ("\pWins:", previous);
X				HiliteBlob (resetBlob, inDragBlob, normalDraw);
X			}
X			else
X				StatusMesg ("\pMove:", current);
X		}
X	}
X}
X
X
X
X/*
X	When a piece is clicked on, the advisory checks whether the piece
X	belongs to the correct player and whether the piece has
X	any legal moves available to it.  If so, it returns true, so that
X	BlobClick is allowed to drag the piece.  After the piece has been
X	dragged, the advisory checks whether the position it was dragged to
X	is legal (connected to original position), and returns true if so.
X
X	Messages passed to the advisory follow the pattern
X
X	{ { advRClick advRClick* } advXfer* }*
X
X	where * means 0 or more instances of the thing *'ed.  In particular,
X	the advXfer message is never seen without a preceding advRClick.
X*/
X
Xstatic Boolean Advisory (mesg, b)
Xint			mesg;
XBlobHandle	b;
X{
Xstatic int	n;		/* static to save board position of click on piece */
X
X	switch (mesg)
X	{
X
X	case advRClick:	/* first click on piece */
X
X		if (BGlob (b) != current)
X			return (false);			/* can only move current piece(s) */
X		n = BoardPos (b);			/* find where it is */
X		return (CanMove (n));
X
X	case advXfer:	/* Mouse released after dragging piece */
X
X		return (connected [n][BoardPos (b)]);
X	}
X}
X
X
X
X/*
X	Activate the window:  Set the advisory function and set the permissions
X	to transfer-only.  Clear, replace, duplication and swap are off.
X	Since replace is off, the advisory doesn't have to check whether
X	the dragged piece was dragged onto a blob that already has a glob.
X
X	Deactivate the window:  Clear the advisory.
X*/
X
Xstatic Activate (active)
XBoolean	active;
X{
X
X	if (active)
X	{
X		SetDragRects (pongPort);
X		SetBCPermissions (false, true, false, false, false);	/* xfer only */
X		SetBCAdvisory (Advisory);
X	}
X	else
X	{
X		SetBCAdvisory (nil);
X	}
X}
X
X
X
X
Xstatic Update (resized)
XBoolean	resized;
X{
X	MoveTo (intersection[3].h, intersection[3].v);	/* draw board */
X	LineTo (intersection[0].h, intersection[0].v);
X	LineTo (intersection[4].h, intersection[4].v);
X	LineTo (intersection[1].h, intersection[1].v);
X	LineTo (intersection[3].h, intersection[3].v);
X	LineTo (intersection[4].h, intersection[4].v);
X	DrawBlobSet (boardBlobs);
X	DrawBlobSet (misc);
X	StatusMesg (statusStr, statusBlob);
X}
X
X
XPongInit	()
X{
X	SkelWindow (pongPort = GetDemoWind (pongWindRes),
X				Mouse,			/* mouse clicks */
X				nil,			/* key clicks */
X				Update,			/* updates */
X				Activate,		/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	MakeDonors ();
X	MakeBoard ();
X	Reset ();
X	EnableBlobSet (boardBlobs);
X	EnableBlobSet (misc);
X	Update ();
X	ValidRect (&pongPort->portRect);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoPyramid.c
X/*
X	Blob Manager Demonstration:  Pyramid module
X
X	This is played on a checkerboard.  Each player has 10 pieces
X	arranged in a pyramid.  Moves are into adjacent diagonal squares,
X	in either direction.  Jumps are also allowed, over either side's
X	pieces.  The first player to reassemble a pyramid on the side
X	opposite to that which he started on wins.
X
X	I haven't thought about deadlock detection yet.
X
X	11 August 1986		Paul DuBois
X*/
X
X
X# include	"BlobDemo.h"
X
X
X# define	rows		8		/* number of rows on board */
X# define	columns		8		/* number of columns on board */
X# define	pieceSize	20		/* size of each piece */
X# define	vMessage	165		/* vertical position of message */
X
X
Xstatic GrafPtr			pyrPort;
X
X
Xstatic BlobSetHandle	boardBlobs = nil;
Xstatic BlobHandle		board[columns][rows];
Xstatic BlobSetHandle	donors = nil;
Xstatic BlobHandle		current;
Xstatic BlobHandle		pBlack;
Xstatic BlobHandle		pWhite;
Xstatic BlobHandle		lastXferPiece;
Xstatic Boolean			lastWasJump;
Xstatic BlobSetHandle	misc;
Xstatic BlobHandle		resetBlob;	/* simulated control */
X
Xstatic int				hMid;
Xstatic Boolean			paused;
Xstatic Str255			statusStr = "\p";
X
X/*
X	Initial configuration
X*/
X
Xstatic int	layout[rows][columns] =	/* 0 = black, 1 = white, 2 = none */
X{
X	0,	2,	2,	2,	2,	2,	2,	2,
X	2,	0,	2,	2,	2,	2,	2,	1,
X	0,	2,	0,	2,	2,	2,	1,	2,
X	2,	0,	2,	0,	2,	1,	2,	1,
X	0,	2,	0,	2,	1,	2,	1,	2,
X	2,	0,	2,	2,	2,	1,	2,	1,
X	0,	2,	2,	2,	2,	2,	1,	2,
X	2,	2,	2,	2,	2,	2,	2,	1
X};
X
X
Xstatic StatusMesg (s)
XStr255	s;
X{
XRect	r;
X
X	SetRect (&r, 0, vMessage, pyrPort->portRect.right - 25, vMessage + 12);
X	TextBox	(s+1, (long) s[0], &r, 1);
X	StrCpy (statusStr, s);
X}
X
X
X/*
X	The donor set consists of two blobs, a white piece and a black
X	piece.
X*/
X
Xstatic MakeDonors ()
X{
XRect		r;
X
X	donors = NewBlobSet ();
X	SetRect (&r, 0, 0, pieceSize - 1, pieceSize - 1);
X	pBlack = NewBlob (donors, false, infiniteGlue, false, 0L);
X	OpenBlob ();
X	PaintRect (&r);
X	PenMode (patBic);
X	FrameOval (&r);
X	PenNormal ();
X	CloseRectBlob (pBlack, &r, &r);
X	pWhite = NewBlob (donors, false, infiniteGlue, false, 0L);
X	OpenBlob ();
X	PaintRect (&r);
X	EraseOval (&r);
X	CloseRectBlob (pWhite, &r, &r);
X}
X
X
X/*
X	Board position drawing procedure.  All positions are drawn the
X	same - a black rectangle.  However, the unused positions are set
X	dimmed by InitBoard, so the drawing mechanisms make them gray.
X	For the drag region of used positions,
X	this proc is only called if the position has no piece (i.e., no
X	glob) because the pieces are picture blobs.
X
X	For these reasons, bSrc and bDst are always equal when
X	DrawPos is called.
X*/
X
Xstatic DrawPos (bSrc, bDst, partCode)
XBlobHandle	bSrc, bDst;
Xint			partCode;		/* ignored - always draw entire blob */
X{
XRect	r;
X
X	r = BStatBox (bDst);
X	PaintRect (&r);
X}
X
X
X/*
X	Build the board, which consists of alternating black and gray
X	squares.  They are actually the same as far as the drawing proc
X	goes, but the gray squares are set to be dimmed so that the
X	normal drawing mechanisms do the work of making the two types
X	of square look distinct.  The fact that the gray squares are
X	dimmed also makes the hit-testing mechanisms ignore them.
X*/
X
Xstatic InitBoard ()
X{
Xint			h, v;
XRect		r, r2;
XBlobHandle	b;
X
X	boardBlobs = NewBlobSet ();
X	for (v = 0; v < rows; v++)
X	{
X		for (h = 0; h < columns; h++)
X		{
X			b = NewBlob (boardBlobs, false, 0, false, 0L);
X			board[h][v] = b;
X			SetRect (&r, 0, 0, pieceSize, pieceSize);
X			OffsetRect (&r, h * pieceSize, v * pieceSize);
X			r2 = r;
X			InsetRect (&r2, 1, 1);
X			SetProcRectBlob (b, DrawPos, &r2, &r);
X
X			/*
X				All unused postions have coordinates that sum to an
X				odd number.
X			*/
X
X			if ((h + v) % 2 == 1)
X				SetBDrawMode (b, inFullBlob, dimDraw);
X		}
X	}
X	ShowBlobSet (boardBlobs);
X}
X
X
X/*
X	Set the board to the initial configuration
X*/
X
Xstatic Reset ()
X{
Xint			i, j, val;
XBlobHandle	b;
X
X	UnglueGlobSet (boardBlobs);		/* clear all globs */
X	for (i = 0; i < columns; ++i)
X	{
X		for (j = 0; j < rows; ++j)
X		{
X			val = layout[i][j];
X			b = board[i][j];
X			DisposeBlobMatchSet (b);
X			if (val != 2)
X			{
X				GlueGlob (GetBlobHandle (donors, val), b);
X				NewBlobMatch (GetBlobHandle (donors, 1 - val), b);
X			}
X		}
X	}
X	current = pWhite;
X	StatusMesg ("\p");
X	paused = false;
X	lastWasJump = false;	/* last move wasn't a jump */
X}
X
X
Xstatic Pause (msg)
XStringPtr	msg;
X{
X	StatusMesg (msg);
X	paused = true;
X}
X
X
X/*
X	Given a blob handle, find the board position that corresponds to it.
X	This is used to map hits in the blob set (a list) to the position in
X	the board (a 2-d array).
X*/
X
Xstatic BoardPos (b, h, v)
XBlobHandle	b;
Xint			*h, *v;
X{
Xint		i, j;
X
X	for (i = 0; i < columns; ++i)
X	{
X		for (j = 0; j < rows; ++j)
X		{
X			if (board[i][j] == b)
X			{
X				*h = i;
X				*v = j;
X				return;
X			}
X		}
X	}
X	/* shouldn't ever get here */
X}
X
X
X/*
X	Check whether a board position is empty.  The coordinates must be
X	legal, the position must be used in the current configuration,
X	and the position must have a piece on it.
X*/
X
Xstatic Boolean PosEmpty (h, v)
Xint		h, v;
X{
X	return (h >= 0 && h < columns && v >= 0 && v < rows
X			&& (h + v) % 2 == 0
X			&& BGlob (board[h][v]) == nil);
X}
X
X
Xstatic Boolean PosFilled (h, v)
Xint		h, v;
X{
X	return (h >= 0 && h < columns && v >= 0 && v < rows
X			&& (h + v) % 2 == 0
X			&& BGlob (board[h][v]) != nil);
X}
X
X
X/*
X	Test whether a piece can move or not.  It can move if there's an
X	empty adjacent diagonal, or a piece adjacent with an empty square
X	on the other side of it.  If the last move was a jump, and the
X	piece is the same one that jumped, then can only move if can
X	keep jumping.
X*/
X
Xstatic Boolean CanMove (h, v)
Xint		h, v;
X{
X	if (   (PosFilled (h - 1, v - 1) && PosEmpty (h - 2, v - 2))
X		|| (PosFilled (h + 1, v - 1) && PosEmpty (h + 2, v - 2))
X		|| (PosFilled (h - 1, v + 1) && PosEmpty (h - 2, v + 2))
X		|| (PosFilled (h + 1, v + 1) && PosEmpty (h + 2, v + 2)))
X		return (true);
X
X	if (board[h][v] == lastXferPiece && lastWasJump)
X		return (false);
X
X	return (   PosEmpty (h - 1, v - 1)
X			|| PosEmpty (h + 1, v - 1)
X			|| PosEmpty (h - 1, v + 1)
X			|| PosEmpty (h + 1, v + 1) );
X}
X
X
X/*
X	Test for a win.  Pass the upper and lower limits on the rows to
X	test (either the top or botton half of the board).
X*/
X
X
Xstatic Boolean TestWin (loRow)
Xint		loRow;
X{
Xint			i, j;
XMatchHandle	m;
X
X	for (i = loRow; i < loRow + rows / 2; ++i)
X	{
X		for (j = 0; j < columns; ++j)
X		{
X			if ((m = (**board[j][i]).matches) != nil
X				&& BGlob (board[j][i]) != (**m).mBlob)
X				return (false);			/* at least one mismatch */
X		}
X	}
X	return (true);
X}
X
X
X
Xstatic Mouse (pt, t, mods)
XPoint	pt;
Xlong	t;
Xint		mods;
X{
X
X
X	if (TestBlob (resetBlob, pt))
X	{
X		if (BTrackMouse (resetBlob, pt, inFullBlob))
X			Reset ();
X	}
X	else if (!paused)
X	{
X		BlobClick (pt, t, nil, boardBlobs);
X		if (BClickResult () == bcXfer)
X		{
X			if (TestWin (0))
X				Pause ("\pWhite Wins");
X			else if (TestWin (rows / 2))
X				Pause ("\pBlack Wins");
X			else if (BGlob (lastXferPiece) == current)	/* switch if didn't move prev player */
X				current = (current == pBlack ? pWhite : pBlack);
X		}
X	}
X}
X
X
X
X/*
X	When a piece is clicked on, the advisory checks whether the piece has
X	any legal moves available to it.  If so, it returns true, so that
X	BlobClick is allowed to drag the piece.  After the piece has been
X	dragged, the advisory checks whether the position it was dragged to
X	is legal, and returns true if so.
X
X	Messages passed to the advisory follow the pattern
X
X	{ { advRClick advRClick* } advXfer* }*
X
X	where * means 0 or more instances of the thing *'ed.  In particular,
X	the advXfer message is never seen without a preceding advRClick.
X*/
X
Xstatic Boolean Advisory (mesg, b)
Xint			mesg;
XBlobHandle	b;
X{
Xstatic int	h, v;	/* static to save board position of click on piece */
Xint			h2, v2;
XBoolean		result;
X
X	switch (mesg)
X	{
X
X	case advRClick:	/* first click on piece */
X		if (BGlob (b) != current && !(b == lastXferPiece && lastWasJump))
X			return (false);
X		BoardPos (b, &h, &v);	/* find where it is */
X		return (CanMove (h, v));
X
X	case advXfer:	/* Mouse released after dragging piece */
X
X		BoardPos (b, &h2, &v2);
X		result =   (h2 == h - 1 || h2 == h + 1)
X				&& (v2 == v - 1 || v2 == v + 1);
X		if (result == true)
X			lastWasJump = false;
X		else
X		{
X			result =   (h2 == h - 2 || h2 == h + 2)
X					&& (v2 == v - 2 || v2 == v + 2)
X					&& PosFilled ((h + h2) / 2, (v + v2) / 2);
X			if (result == true)
X				lastWasJump = true;
X		}
X		if (result == true)
X			lastXferPiece = b;
X		return (result);
X	}
X}
X
X
X
X/*
X	Activate the window:  Set the advisory function and set the permissions
X	to transfer-only.  Clear, replace, duplication and swap are off.
X	Since replace is off, the advisory doesn't have to check whether
X	the dragged piece was dragged onto a blob that already has a glob.
X
X	Deactivate the window:  Clear the advisory.
X*/
X
Xstatic Activate (active)
XBoolean	active;
X{
X
X	if (active)
X	{
X		SetDragRects (pyrPort);
X		SetBCPermissions (false, true, false, false, false);	/* xfer only */
X		SetBCAdvisory (Advisory);
X	}
X	else
X	{
X		SetBCAdvisory (nil);
X	}
X}
X
X
Xstatic Update (resized)
XBoolean	resized;
X{
X	DrawBlobSet (boardBlobs);
X	DrawBlobSet (misc);
X	StatusMesg (statusStr);
X}
X
X
XPyrInit	()
X{
XRect	r;
X
X	SkelWindow (pyrPort = GetDemoWind (pyrWindRes),
X				Mouse,			/* mouse clicks */
X				nil,			/* key clicks */
X				Update,			/* updates */
X				Activate,		/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	hMid = pyrPort->portRect.right / 2;
X
X	MakeDonors ();
X	InitBoard ();
X	misc = NewBlobSet ();
X	SetRect (&r, 0, 0, 20, 90);
X	OffsetRect (&r, pyrPort->portRect.right - 25,
X					(pyrPort->portRect.bottom - 90 - 25) / 2);
X	resetBlob = NewVButtonBlob (misc, &r, "\pReset", true, 0L);
X	Reset ();
X	ValidRect (&pyrPort->portRect);
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > BlobDemo/DemoRadix.c
X
X/*
X	Blob Manager Demonstration:  Arithmetic problem module
X
X	Displays arithmetic problems to solve.  Allows radix to be set
X	anywhere from base 2 to base 10.
X
X	This module demonstrate a simple use of the BlobClick advisory
X	function.
X
X	26 July 1986		Paul DuBois
X*/
X
X
X# include	"BlobDemo.h"
X# include	<MenuMgr.h>
X# include	<ControlMgr.h>
X
X
X/*
X	Blobs types, used to distinguish different parts of problem
X*/
X
Xenum
X{
X	donorBlob,		/* for donor digits */
X	carryBlob,		/* for carry digits */
X	addendBlob,		/* for addend digits */
X	sumBlob,		/* for sum digits */
X	operBlob		/* for the plus sign */
X};
X
X
X# define	carryPos	10	/* horizontal position of carry digits */
X							/* addend and sum pos's are relative to this */
X# define	digitPos	110	/* position of donor digits */
X# define	digitSize	18	/* size of digit blobs */
X# define	digitGap	2	/* gap between blobs */
X# define	absMaxLen	10	/* absolute max number of digits in addends */
X
X
Xstatic GrafPtr			radixPort;
Xstatic MenuHandle		radixMenu;
Xstatic ControlHandle	checkAns;
Xstatic ControlHandle	giveUp;
Xstatic ControlHandle	nextProb;
X
Xstatic int				radix = 10;		/* initially decimal */
X
Xstatic BlobSetHandle	digits = nil;	/* donor blobs */
Xstatic BlobSetHandle	problem = nil;	/* receptor blobs */
Xstatic BlobHandle		firstAddendBlob;
Xstatic BlobHandle		firstSumBlob;
Xstatic BlobHandle		plusSignBlob;
Xstatic int				maxLen = 6;		/* current max digits in addends */
Xstatic int				rightEdge;		/* right edge of problem display */
Xstatic int				lineX;			/* pos and length of line */
Xstatic int				lineY;			/* between lower addend and sum */
Xstatic int				lineLen;
X
Xstatic Boolean			paused;
X
X
X/*
X	Pick a problem digit.  Don't pick zero if canBeZero is false.
X*/
X
Xstatic PickDigit (canBeZero)
XBoolean	canBeZero;
X{
Xint		n;
X
X	do {
X		n = BlobRand (radix-1);		/* use Blob Manager rand function */
X	} while (!canBeZero && n == 0);
X	return (n);
X}
X
X
X/*
X	Make a blob.  Pass the x and y coordinates, the character drawn in the
X	blob, whether it needs an explicit match or not.  Carry digits don't
X	need an explicit match, digits of the sum do - except that the leftmost
X	sum blob might be unnecessary - so it can be zero or nothing.
X*/
X
Xstatic BlobHandle MakeBlob (bSet, x, y, c, needMatch, type)
XBlobSetHandle	bSet;
Xint				x, y;
Xint				c;
XBoolean			needMatch;
Xint				type;
X{
XBlobHandle		b;
X
X	b = MakeCharBlob (bSet, false, infiniteGlue, needMatch,
X				x, y, c);
X	SetBRefCon (b, c + ((long) type << 16));
X	return (b);
X}
X
X
X/*
X	Return blob type, encoded as the high word of the reference
X	constant.
X*/
X
Xstatic GetBlobType (b)
XBlobHandle	b;
X{
X	return (HiWord (GetBRefCon (b)));
X}
X
X
X/*
X	Generate a new problem to solve.  The addends are at least two digits
X	long.  The max number of digits in the sum will be 1 greater than the
X	longest addend.  The max number of carry digits is the same as the length
X	of the longest addend.
X
X	Digits in the addend and sum arrays are numbered from right to left.
X*/
X
Xstatic GenerateProblem ()
X{
Xint			addend1[absMaxLen];
Xint			addend2[absMaxLen];
Xint			carry[absMaxLen];
Xint			sum[absMaxLen+1];
Xint			add1Len;			/* number of digits in addend 1 */
Xint			add2Len;			/* number of digits in addend 2 */
Xint			sumLen;				/* number of digits in sum */
Xint			carryLen;			/* number of carry digits */
XBlobHandle	b;
Xint			i;
Xint			x, y;
X
X	add1Len = BlobRand (maxLen - 2) + 2;	/* addends are at least 2 digits */
X	add2Len = BlobRand (maxLen - 2) + 2;
X	carryLen = (add1Len > add2Len ? add1Len : add2Len);
X	sumLen = carryLen + 1;
X
X	/* zero the addends and the carry digits */
X
X	for (i = 0; i < absMaxLen; ++i)
X	{
X		addend1[i] = 0;
X		addend2[i] = 0;
X		carry[i] = 0;
X	}
X
X	/* generate digits for the addends and determine the sum. */
X	/* the leftmost digit of the addends should not be zero */
X
X	for (i = 0; i < add1Len; ++i)
X		addend1[i] = PickDigit (i != add1Len - 1);
X	for (i = 0; i < add2Len; ++i)
X		addend2[i] = PickDigit (i != add2Len - 1);
X	for (i = 0; i < sumLen; ++i)
X	{
X		sum[i] = addend1[i] + addend2[i];
X		if (i > 0 && sum[i-1] >= radix)
X		{
X			sum[i-1] -= radix;
X			++sum[i];
X			++carry[i-1];
X		}
X	}
X
X	/* get rid of any old problem blob set */
X
X	if (problem != nil)
X	{
X		HideBlobSet (problem);
X		DisposeBlobSet (problem);
X	}
X	problem = NewBlobSet ();
X
X	/* generate blobs for carry digits - they don't need an explicit */
X	/* match.  carry digits are placed over every addend digit */
X	/* except the rightmost */
X
X	x = rightEdge - digitSize;
X	y = carryPos;
X	for (i = 0; i < carryLen; ++i)
X	{
X		x -= digitSize + digitGap;
X		b = MakeBlob (problem, x, y, ' ', false, carryBlob);
X		NewBlobMatch (GetBlobHandle (digits, carry[i]), b);
X	}
X
X	/* generate blobs for addends.  addend blobs are given a non-zero */
X	/* reference value so the advisory function can distinguish them */
X	/* from carry and sum blobs easily. */
X
X	x = rightEdge - digitSize;
X	y += digitSize + digitGap;
X	for (i = 0; i < add1Len; ++i)
X	{
X		b = MakeBlob (problem, x, y, ' ', false, addendBlob);
X		GlueGlob (GetBlobHandle (digits, addend1[i]), b);
X		if (i == 0)
X			firstAddendBlob = b;
X		x -= digitSize + digitGap;
X	}
X
X	x = rightEdge - digitSize;
X	y += digitSize + digitGap;
X	for (i = 0; i < add2Len; ++i)
X	{
X		b = MakeBlob (problem, x, y, ' ', false, addendBlob);
X		GlueGlob (GetBlobHandle (digits, addend2[i]), b);
X		x -= digitSize + digitGap;
X	}
X	
X	/* add the plus sign */
X
X	plusSignBlob = MakeBlob (problem, x, y, '+', false, operBlob);
X	FreezeBlob (plusSignBlob);
X
X	/* figure out length and position of line 'tween addend2 and sum */
X
X	y += digitSize + digitGap;
X	lineLen = sumLen * (digitSize + digitGap) - digitGap;
X	lineX = rightEdge - lineLen;
X	lineY = y;
X
X	/* make sum digits.  these must be matched explicitly - except possibly */
X	/* the leftmost one. */
X
X	x = rightEdge - digitSize;
X	y += digitGap + 2;
X	for (i = 0; i < sumLen; ++i)
X	{
X		b = MakeBlob (problem, x, y, ' ', true, sumBlob);
X		if (i == sumLen - 1 && sum[i] == 0)
X			ClearBlobFlags (b, bNeedGlobMask);	/* explicit match unneeded */
X		NewBlobMatch (GetBlobHandle (digits, sum[i]), b);
X		if (i == 0)
X			firstSumBlob = b;
X		x -= digitSize + digitGap;
X	}
X}
X
X
Xstatic DrawLine ()
X{
X	MoveTo (lineX, lineY);
X	LineTo (rightEdge, lineY);
X}
X
X
Xstatic NextProblem ()
X{
X	InvalRect (&radixPort->portRect);
X	PenMode (patBic);
X	DrawLine ();					/* erase line */
X	PenNormal ();
X	SetCTitle (checkAns, "\pCheck");
X	HiliteControl (checkAns, 0);	/* make sure these buttons are on */
X	HiliteControl (giveUp, 0);
X	paused = false;
X	/* choose numbers */
X	GenerateProblem ();
X	ShowBlobSet (problem);
X	DrawLine ();
X	ValidRect (&radixPort->portRect);
X}
X
X
X/*
X	Make digit set - creates only the digits are legal for the
X	current radix.
X*/
X
Xstatic MakeDigits ()
X{
Xint			i, x;
X
X	if (digits != nil)
X	{
X		HideBlobSet (digits);
X		DisposeBlobSet (digits);
X	}
X	digits = NewBlobSet ();
X	x = rightEdge - radix * (digitSize + digitGap) + digitGap;
X	for (i = 0; i < radix; ++i)
X	{
X		(void) MakeBlob (digits, x, digitPos, i + '0', false, donorBlob);
X		x += digitSize + digitGap;
X	}
X	ShowBlobSet (digits);
X}
X
X
X/*
X	Radix menu handler
X
X	Change the radix and choose a new problem.
X*/
X
Xstatic ChooseRadix (item)
Xint		item;
X{
X	CheckItem (radixMenu, radix-1, false);
X	radix = item + 1;	/* menu items start with Base 2 */
X	CheckItem (radixMenu, radix-1, true);
X	MakeDigits ();
X	NextProblem ();
X}
X
X
Xstatic Mouse (pt, t, mods)
XPoint	pt;
Xlong	t;
Xint		mods;
X{
XBlobHandle		b;
Xint				type;
XControlHandle	ctl;
X
X	if (FindControl	(pt, radixPort, &ctl))
X	{
X		if (TrackControl (ctl, pt, nil))	/* any button hit? */
X		{
X			if (ctl == nextProb)
X				NextProblem ();
X			else if (ctl == checkAns)
X			{
X				if (!paused)
X				{
X					/* give feedback (this freezes the receptors) */
X					BlobFeedback (problem, normalDraw, dimDraw);
X					SetCTitle (checkAns, "\pResume");
X					paused = true;
X				}
X				else
X				{
X					/* allow user to continue working */
X					SetCTitle (checkAns, "\pCheck");
X					paused = false;
X					ThawBlobSet (problem);		/* thaw entire problem */
X					FreezeBlob (plusSignBlob);	/* except plus sign */
X				}
X			}
X			else if (ctl == giveUp)
X			{
X			/*
X				Show answer.  Show only the non-zero carry digits,
X				and the sum digits.  Show the leftmost sum digit only
X				if it's non-zero.  Have to that the problem first,
X				because some of it may have been dimmed by Check.
X			*/
X				ThawBlobSet (problem);	/* might be frozen from Check */
X				for (b = FirstBlob (problem); NextBlob (b) != nil; b = NextBlob (b))
X				{
X					type = GetBlobType (b);
X					if (type == carryBlob)
X					{
X						UnglueGlob (b);	/* clear any glob it might have */
X						if (FirstBMatch (b) == GetBlobHandle (digits, 1))
X							ZGlueGlob (FirstBMatch (b), b);
X					}
X					else if (type == sumBlob)
X						ZGlueGlob (FirstBMatch (b), b);
X				}
X				/*
X					for loop leaves b pointing at last blob in problem set,
X					i.e., the leftmost sum digit.
X				*/
X				if (FirstBMatch (b) != GetBlobHandle (digits, 0))
X					ZGlueGlob (FirstBMatch (b), b);
X
X				HiliteControl (checkAns, 255);	/* make inactive */
X				HiliteControl (giveUp, 255);
X				paused = true;
X			}
X		}
X	}
X	else if (!paused)
X	{
X		BlobClick (pt, t, digits, problem);
X		if (BlobSetQuiet (problem))		/* done yet? */
X		{
X			HiliteControl (checkAns, 255);
X			HiliteControl (giveUp, 255);
X			paused = true;
X		}
X	}
X}
X
X
X/*
X	The purpose of the advisory is to allow digits from the addends
X	to be duplicated onto carry or sum digits, but to prevent addend
X	blobs from being cleared or duplicated onto, and to prevent donors
X	from being glued to them.  So, whenever a clear, glue, or duplicate
X	message if received, return false if the blob involved is an addend
X	to make BlobClick abort.  Addends are easily distinguished since
X	they have non-zero reference values.
X	
X	Transfer and swap messages will never be received since the
X	permissions are set to disallow those transaction types.
X	For any other message, return true to continue normal processing.
X*/
X
Xstatic Boolean Advisory (mesg, b)
Xint			mesg;
XBlobHandle	b;
X{
X	switch (mesg)
X	{
X		case advGlue:
X		case advUnglue:
X		case advDup:
X			if (GetBlobType (b) == addendBlob)
X				return (false);
X	}
X	return (true);
X}
X
X
Xstatic Activate (active)
XBoolean	active;
X{
X	if (active)
X	{
X		SetDragRects (radixPort);
X		SetBCPermissions (true, true, true, false, true);
X		SetBCAdvisory (Advisory);
X		radixMenu = GetMenu (radixMenuRes);
X		SkelMenu (radixMenu, ChooseRadix, DoMClobber);
X		CheckItem (radixMenu, radix-1, true);
X	}
X	else
X	{
X		SkelRmveMenu (radixMenu);	/* destroy handler */
X		SetBCAdvisory (nil);
X	}
X}
X
X
Xstatic Update ()
X{
X	DrawControls (radixPort);
X	DrawBlobSet (problem);
X	DrawLine ();
X	DrawBlobSet (digits);
X}
X
X
XRadixInit ()
X{
XRect	r;
X
X	SkelWindow (radixPort = GetDemoWind (radixWindRes),
X				Mouse,			/* mouse clicks */
X				nil,			/* key clicks */
X				Update,			/* updates */
X				Activate,		/* activate/deactivate events */
X				nil,			/* close window */
X				DoWClobber,		/* dispose of window */
X				nil,			/* idle proc */
X				false);			/* irrelevant, since no idle proc */
X
X	SetRect (&r, 0, 25, 70, 45);
X	OffsetRect (&r, radixPort->portRect.right - 80, 0);
X	checkAns = NewControl (radixPort, &r, "\pCheck", true, 0, 0, 0,
X							pushButProc, nil);
X	OffsetRect (&r, 0, 30);
X	giveUp = NewControl (radixPort, &r, "\pUncle", true, 0, 0, 0,
X							pushButProc, nil);
X	OffsetRect (&r, 0, 30);
X	nextProb = NewControl (radixPort, &r, "\pNext", true, 0, 0, 0,
X							pushButProc, nil);
X
X	rightEdge = radixPort->portRect.right - 100;
X	SetCharBlobSize (digitSize);
X	MakeDigits ();
X	NextProblem ();
X}
SHAR_EOF
exit
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads