maci...@felix.uucp
unread,Jan 31, 1987, 2:00:05 AM1/31/87You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to
#!/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