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