macintosh@felix.UUCP (01/31/87)
#!/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