TweetFollow Us on Twitter

Sep 93 Challenge
Volume Number:9
Issue Number:9
Column Tag:Programmers’ Challenge

Programmers’ Challenge

By Mike Scanlin, MacTech Magazine Regular Contributing Author

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

BLOCKMOVEBITS

Because I’ve had several requests for it and because I’ve wanted to do it for a while and because this column is primarily about about efficient coding and because the lazy days of summer are over and it’s time to get serious, I’m happy to say that September will from now on be the annual MacTech Assembly Language Programmer’s Challenge. When submitting entries to September challenges you may use as much in-line 68040 assembly source inside of a Think C function as you like. In fact, a C wrapper around a 100% assembly language solution is allowed (and preferred). You should optimize for the 040 cause that’s what I’ll run the time tests on (and your code can require an 020, 030 and/or 040 if it wants to). You must preserve all registers except A0-A1/D0-D2. OK. Let’s get to it.

This first assembly language challenge is to move a block of contiguous bits when given the number of bits to move along with source and destination byte and bit offsets (the source and destination bit ranges do not overlap). This could be used, for example, inside a bit-blitting graphics routine that’s working with bitMaps.

The prototype of the function you write is:

void BlockMoveBits(srcBytePtr, 
 destBytePtr, srcBitOffset, 
 destBitOffset, bitCount)
char    *srcBytePtr;
char    *destBytePtr;
unsigned char    srcBitOffset;
unsigned char    destBitOffset;
unsigned short bitCount;

The srcBytePtr and destBytePtr point to the bytes containing the first source bit and first destination bit. The actual bits are at bit offsets srcBitOffset and destBitOffset (both of which will be less than 8). The most number of bits you can move at one time with this routine is 2^16 - 1 or 65535 (it’s possible that bitCount will be zero, which means you should move zero bits, not 64K bits). You should not disturb any of the bits in the destination bytes that aren’t part of the move.

Here’s an example. Say srcBitOffset is 2, destBitOffset is 5 and bitCount is 10. You have the following before the move (‘s’ is a source bit, ‘d’ is a destination bit and the ‘S’ bits (capital S) are going to be copied:

bit position : 76543210 76543210
srcBytePtr  -> ssSSSSSS SSSSssss
destBytePtr -> dddddddd dddddddd

and you have this after the move:

bit position : 76543210 76543210
srcBytePtr  -> ssSSSSSS SSSSssss
destBytePtr -> dddddSSS SSSSSSSd

It’s very likely that your implementation will have this shell:

void BlockMoveBits(srcBytePtr, 
 destBytePtr, srcBitOffset, 
 destBitOffset, bitCount)
char    *srcBytePtr;
char    *destBytePtr;
unsigned char    srcBitOffset;
unsigned char    destBitOffset;
unsigned short bitCount;
{
 asm {
 ;get parameters into registers
 Move.L srcBytePtr,A0
 Move.L destBytePtr,A1
 ... and so on
 }
}

Please note that because of the increased level of difficulty of reading assembly compared to C, it is very important that your code contains at least a few comments explaining what’s going on. I don’t want to see 200 lines of blazingly fast completely undocumented code. That won’t be of much use to anyone. And use meaningful labels where you can (rather than randomly ordered numeric ones from @258 to @431 or whatever).

TWO MONTHS AGO WINNER

I guess I scared away too many people with my “don’t use MoveWindow or SizeWindow” suggestion in the Tile Windows challenge because I only received three entries this month. However, all three entrants managed to find ways around using those traps and their times were very close to each other (and all three were at least an order of magnitude faster than the MoveWindow/SizeWindow equivalent). Congrats to Raffi Kasparian (Baltimore, MD) for being somewhat faster than the other guys. Raffi’s performance gain comes from manipulating some of the window’s fields directly, which is less than ideal in terms of future compatibility but it works on today’s machines so I’ll let it stand.

Here are the times (for tiling 80 windows) and sizes:

Name bytes ticks

Raffi Kasparian 716 81

Jordan Zimmerman 948 123

Ken Franklin/Michael Staw 806 126

Note that tiling 80 windows in 2 seconds (120 ticks) or less is quite fast. MPW, for comparison, took 10 seconds to tile 20 windows (and I found out that the “New” menu item becomes disabled after you create 45 Untitled windows).

The key to winning this challenge was to make use of the routines MovePortTo, PortSize, PaintBehind and CalcVisBehind. If you do it right then you can have the entire desktop (spread over multiple devices) updated with all the new windows blinking into place with a single call to PaintBehind. It’s really interesting to trap on PaintBehind in a debugger and then to step over that one trap and watch the whole desktop get rearranged (after all ports have been positioned and sized correctly, of course).

Since the winning solution does things in a less than future compatible way (it doesn’t call the window’s defProc to calculate the new regions, for instance) and since my goal with this challenge was to release a generic, efficient window tiling and stacking mechanism into the world, I’ve included some of my own code after Raffi’s.

If you think about minimal screen updating, which parts of the desktop need to be invalidated by rearranging the windows? The answer is the sum of the strucRgns in the windows’ old positions and the sum of the strucRgns in the windows’ new positions. No other area on the screen needs to be invalidated or redrawn. Therefore, my routine keeps track of the sum of these two sets of regions as it works. The resulting region, sumOfStructRgns, is then used as the clobberedRgn parameter to PaintBehind and CalcVisBehind. Any desktop pixel or background app pixel not in this region is not redrawn (i.e. there is no unnecessary flicker).

My routine, TileStackWindows, takes a function pointer parameter to a proc you write that does something with a list of windows inside a given rectangle (the function callback is similar to the challenge’s TileWindows function). This callback is called once per device, with the device’s gdRect given as the rect (inset a few pixels) and the list of windows set to all windows that are more on that device than on any other device. Thus, TileStackWindows is a generic, per-device window rearranging routine. All you have to do is come up with interesting tiling algorithms and bake them into the proc you pass to TileStackWindows. The efficient updating of the desktop and region maintenance is taken care of for you.

There are a couple of restrictions for the callback. First, it must resize a window before it repositions a window. Second, it must call MySizeWindow and MyMoveWindow instead of SizeWindow and MoveWindow. The callback should return TRUE if it moved or sized at least one window and FALSE if it did nothing. The reason for this is because if you have a Tile Windows menu item in your application and the user chooses it twice in a row, it is nice if the second time it is chosen the code is smart enough to see that things are already as they should be and does not cause any flickering or redrawing to take place.

An example callback function, StackWindows, is given here. It takes advantage of the fact that the list of windows it is passed is in order from the frontmost window to the backmost window (for that device). The last window in the list (the backmost) ends up being the largest window after the set has been stacked and the frontmost window (first in the list) ends up being the smallest window after the set has been stacked. It is done this way so that you can read all window titles after they’ve been stacked.

If you want to do tiling rather than stacking, all you have to do is replace StackWindows with your own TileWindows function and come up with a way to calculate each window’s new size and position. Hopefully application writers who have tiling and stacking options within their programs (including MPW) will implement this type of a scheme so that we no longer have to wait so long while we watch windows being moved and sized one at a time with maximum intermediate screen updating.

Here’s Raffi’s winning solution followed by my own version of a generic window tiling/stacking routine and a piece of test code so you can try it out:

TileWindows.c Listing
/*----------------------------------------------------
TileWindows
by Raffi Kasparian
----------------------------------------------------*/
#define StackingOffset 10
 
void TileWindows(enclosingRectPtr, windowPtrArray,
                 windowCount, pixelsBetween,
                 minHorzSize, minVertSize)
 
Rect            *enclosingRectPtr;
WindowPtr       windowPtrArray[];
unsigned short  windowCount;
unsigned short  pixelsBetween;
unsigned short  minHorzSize;
unsigned short  minVertSize;
 
#define mx minHorzSize
#define my minVertSize
#define d  pixelsBetween
 
#define struc  ((WindowPeek)(windowPtrArray[num]))->strucRgn
#define strucL (**struc).rgnBBox.left
#define strucT (**struc).rgnBBox.top
#define strucR (**struc).rgnBBox.right
#define strucB (**struc).rgnBBox.bottom
 
#define cont  ((WindowPeek)(windowPtrArray[num]))->contRgn
#define contL (**cont).rgnBBox.left
#define contT (**cont).rgnBBox.top
#define contR (**cont).rgnBBox.right
#define contB (**cont).rgnBBox.bottom
 
#define pBits  windowPtrArray[num]->portBits.bounds
#define pBitsL pBits.left
#define pBitsT pBits.top
#define pBitsR pBits.right
#define pBitsB pBits.bottom
 
#define pRect  windowPtrArray[num]->portRect
#define pRectL pRect.left
#define pRectT pRect.top
#define pRectR pRect.right
#define pRectB pRect.bottom
 
{
 register short dL, dT, dR, dB, BR, x, y, maxx, maxy,
                num, rL, rT, rR, rB;
 Rect           er = *enclosingRectPtr;
 
#define erL er.left
#define erT er.top
#define erR er.right
#define erB er.bottom
 
 num = 0;
 
 while (true) {
 
  maxy = (erB - erT + d)/(my + d);
  maxx = (erR - erL + d)/(mx + d);
  if ((BR = maxx * maxy) > windowCount) {
   maxx = (windowCount - 1) / maxy + 1;
   maxy = (windowCount - 1) / maxx + 1;
   BR = windowCount;
  }
  BR--;
 
  if (num <= BR) {
   mx = ((erR - erL) - ((maxx - 1) * d)) / maxx;
   my = ((erB - erT) - ((maxy - 1) * d)) / maxy;
  }
 
  rT = erT - (my + d);
  for (y = 1; y <= maxy; y++) {
   rL = erL - (mx + d);
   rT += my + d;
   rB = (y == maxy) && (num <= BR) ? erB : rT + my;
   for (x = 1; x <= maxx; x++, num++) {
 
    rL += mx + d;
    rR = ((x == maxx) && (num <= BR)) ||
      (num == BR) ? erR : rL + mx;
 
    dL = rL - strucL;
    dT = rT - strucT;
    dR = rR - strucR;
    dB = rB - strucB;
 
    SetRectRgn(struc, rL, rT, rR, rB);
    SetRectRgn(cont, contL + dL, contT + dT,
      contR + dR,  contB + dB);
      
    pBitsL -= dL;
    pBitsT -= dT;
    pBitsR -= dL;
    pBitsB -= dT;
 
    pRectR = contR - contL + pRectL;
    pRectB = contB - contT + pRectT;
 
    if (num == windowCount - 1) {
     PaintBehind((WindowPeek)FrontWindow(),
       GrayRgn);
     CalcVisBehind((WindowPeek)FrontWindow(),
       GrayRgn);
     return;
    }
   }
  }
  if (erR - erL - StackingOffset >= mx)
   erL += StackingOffset;
  if (erB - erT - StackingOffset >= my)
   erT += StackingOffset;
 }
}
TileStackWindows.h Listing
/*****************************************************
 * TileStackWindowsWindows.h
 ****************************************************/

/*****************************************************
 * typedefs
 ****************************************************/

typedef struct WindowElement {
 WindowPeek theWindowPtr;
 GDHandle theDevHndl;
} WindowElement, *WindowElementPtr;

typedef Boolean (*TileStackWindowsProc)
 (Rect *enclosingRectPtr,
 WindowElementPtr p, int wCount);
 
 
/*****************************************************
 * prototypes
 ****************************************************/
 
void  TileStackWindows(TileStackWindowsProc
 theTileStackProc);
Boolean MyMoveWindow(WindowPtr w, int leftGlobal,
 int topGlobal, Boolean sizeChanged);
Boolean MySizeWindow(WindowPtr w, int width,
 int height);
TileStackWindows.c Listing
/*****************************************************
 * TileStackWindows.c
 *
 * Set of routines to quickly rearrange windows
 * on multiple devices.
 *
 * Mike Scanlin  10 July 1993
 ****************************************************/

#include <GestaltEqu.h>
#include <Traps.h>
#include "TileStackWindows.h"

/*****************************************************
 * defines
 ****************************************************/

#define BAD_DEVICE ((GDHandle) -1)
#define TOPLEFT_SLOP 2
#define BOTRIGHT_SLOP3
#define NIL 0L

/* MAX_WINDOWS is not a real limit but stack space
 * requirements for TileStackWindows are equal to
 * (sizeof(WindowElement) * 2 * MAX_WINDOWS) so
 * don't make it too big. It's the max number of
 * windows that TileStackWindows can deal with.
 */
#define MAX_WINDOWS100

/*****************************************************
 * typedefs
 ****************************************************/

typedef pascal long(**WDefProcHndl)(int var,
 WindowPtr w, int message, long param);

/*****************************************************
 * prototypes
 ****************************************************/
 
static GDHandle DominantDevice(Rect *theRect);

/*****************************************************
 * TileStackWindows
 *
 * Calls theTileStackProc on a per-device basis to
 * clean up (stack, tile or whatever else you can
 * think of) all the windows on that device. Once
 * all devices have been taken care of the part of
 * the screen that needs to be updated is updated.
 ****************************************************/
void TileStackWindows(TileStackWindowsProc
 theTileStackProc)
{
 WindowElementPtrp, dp;
 WindowPeek w;
 GDHandle deviceHndl;
 RgnHandle  sumOfStructRgns;
 WindowElement   theWindows[MAX_WINDOWS],
 theDeviceWindows[MAX_WINDOWS];
 long   theQDVers;
 Rect   enclosingRect;
 int    i, totalWindows,
 windowsToClean,
 deviceWindows;
 BooleanneedToRedraw, hasColorQD;
 
 /* check for color QuickDraw */
 hasColorQD = FALSE;
 if (TrapIsAvailable(_Gestalt)) {
 Gestalt(gestaltQuickdrawVersion, &theQDVers);
 hasColorQD = theQDVers >= 0x0100;
 }

 sumOfStructRgns = NewRgn();
 needToRedraw = FALSE;
 
 /* find the dominant device for each window
  * as we add it to our list of windows
  */
 p = theWindows;
 totalWindows = 0;
 w = (WindowPeek) FrontWindow();
 while (w != NIL && totalWindows < MAX_WINDOWS) {
 p->theWindowPtr = w;
 p->theDevHndl = NIL;
 if (hasColorQD)
 p->theDevHndl =
   DominantDevice(&(*w->strucRgn)->rgnBBox);
 p++;
 totalWindows++;
 /* or the old struct region into the
  * update region
  */
 UnionRgn(sumOfStructRgns, w->strucRgn,
 sumOfStructRgns);
 w = w->nextWindow;
 }
 
 if (totalWindows == 0)
 goto Exit;
 
 /* set up enclosingRect here in case we don't
  * have color QD; if we do have colorQD then
  * enclosingRect is set again in the loop below
  * on a per-device basis
  */
 enclosingRect = (*GetGrayRgn())->rgnBBox;
 enclosingRect.top += TOPLEFT_SLOP;
 enclosingRect.left += TOPLEFT_SLOP;
 enclosingRect.bottom -= BOTRIGHT_SLOP;
 enclosingRect.right -= BOTRIGHT_SLOP;

 windowsToClean = totalWindows;
 do {
 /* find the first device in the list that we
  * haven't already done and copy all elements
  * from that device into a new list
  */
 p = theWindows;
 dp = theDeviceWindows;
 deviceWindows = 0;
 deviceHndl = BAD_DEVICE;
 i = totalWindows;
 do {
 if (p->theDevHndl != BAD_DEVICE) {
 if (deviceHndl == BAD_DEVICE) {
 /* this is the first time we've
  * seen this device so we do a
  * little set up first
  */
 deviceHndl = p->theDevHndl;
 /* if we have colorQD, use the
  * device's rect
  */
 if (deviceHndl != NIL) {
 enclosingRect = (*deviceHndl)->gdRect;
 enclosingRect.top += TOPLEFT_SLOP;
 enclosingRect.left += TOPLEFT_SLOP;
 enclosingRect.bottom -= BOTRIGHT_SLOP;
 enclosingRect.right -= BOTRIGHT_SLOP;
 if (deviceHndl == GetMainDevice())
 enclosingRect.top += GetMBarHeight();
 }
 }
 if (deviceHndl == p->theDevHndl) {
 /* it's on the current device,
  * add it to the list
  */
 *dp++ = *p;
 deviceWindows++;
 /* we don't want to see this
  * one again
  */
 p->theDevHndl = BAD_DEVICE;
 }
 }
 p++;
 } while (--i);
 
 if (deviceWindows > 0) {
 /* do something to the windows on
  * this device
  */
 needToRedraw |= (*theTileStackProc)
   (&enclosingRect, theDeviceWindows,
   deviceWindows);
 windowsToClean -= deviceWindows;
 }
 
 } while (windowsToClean > 0);
 
 if (needToRedraw) {
 
 /* add all of the new struct regions to the
  * update region
  */
 p = theWindows;
 do {
 UnionRgn(sumOfStructRgns,
   p->theWindowPtr->strucRgn,
   sumOfStructRgns);
 p++;
 } while (--totalWindows);
 
 /* To see a cool effect, trap on the next
  * instruction and watch the screen as you
  * step over it. All window frames are drawn
  * with this one call to the ROMs.
  */
 PaintBehind(theWindows[0].theWindowPtr,
   sumOfStructRgns);
 
 /* Need to reset the visRgns since
  * MySizeWindow nuked 'em.
  */
 CalcVisBehind(theWindows[0].theWindowPtr,
   sumOfStructRgns);
 }

Exit:

 DisposeRgn(sumOfStructRgns);
}


/*****************************************************
 * DominantDevice
 *
 * This returns a device hndl to the device that
 * owns most of the given Rect (which is in global
 * coordinates). This routine requires ColorQD.
 ****************************************************/
static GDHandle DominantDevice(Rect *theRect)
{
 GDHandle nthDevice, theDevice;
 long   greatestArea, sectArea;
 Rect   theSect;
 
 nthDevice = theDevice = GetDeviceList();
 greatestArea = 0;
 do {
 if (TestDeviceAttribute(nthDevice, screenDevice) &&
   TestDeviceAttribute(nthDevice, screenActive)) {
 SectRect(theRect, &(*nthDevice)->gdRect,
   &theSect);
 sectArea =
   ((long) (theSect.bottom - theSect.top)) *
   ((long) (theSect.right - theSect.left));
 if (sectArea > greatestArea) {
 greatestArea = sectArea;
 theDevice = nthDevice;
 }
 }
 nthDevice = GetNextDevice(nthDevice);
 } while (nthDevice != NIL);
 
 return (theDevice);
}


/*****************************************************
 * MyMoveWindow
 *
 * Quickly move the window. No screen updating
 * will take place.
 ****************************************************/
Boolean MyMoveWindow(WindowPtr w, int leftGlobal,
 int topGlobal, Boolean sizeChanged)
{
 Handle theDefProc;
 GrafPtroldPort;
 Point  upperLeft;
 char   oldState;
 BooleanitMoved;
 
 itMoved = FALSE;

 oldPort = thePort;
 SetPort(w);
 
 /* Don't move it if it's already there, unless
  * it was just resized (in which case we don't
  * really need to call MovePortTo but we do
  * need to call the windowDefProc below to fix
  * up the regions).
  */
 if (!sizeChanged) {
 upperLeft = topLeft(w->portRect);
 LocalToGlobal(&upperLeft);
 if (upperLeft.h == leftGlobal &&
   upperLeft.v == topGlobal)
 goto Exit;
 }
 
 itMoved = TRUE;
 
 MovePortTo(leftGlobal, topGlobal);
 
 theDefProc = ((WindowPeek) w)->windowDefProc;
 oldState = HGetState(theDefProc);
 HLock(theDefProc);
 
 /* call the WDEF to update the regions */
 (*(WDefProcHndl) theDefProc)(zoomDocProc, w,
   wCalcRgns, 0);

 HSetState(theDefProc, oldState);

Exit:

 SetPort(oldPort);
 
 return (itMoved);
}


/*****************************************************
 * MySizeWindow
 *
 * Quickly set the size of a window. Set the
 * visRgn to NIL so that no screen updating takes
 * place. The visRgn will be reset when we call
 * CalcVisBehind later.
 ****************************************************/
Boolean MySizeWindow(WindowPtr w, int width,
 int height)
{
 GrafPtroldPort;
 
 /* don't size it if it's already the right size */
 if (w->portRect.right - w->portRect.left == width &&
   w->portRect.bottom - w->portRect.top == height)
   return(FALSE);
   
 oldPort = thePort;
 SetPort(w);
 
 PortSize(width, height);
 
 /* nuke the visRgn so that moving this port
  * (in MyMoveWindow) won't cause any screen
  * drawing
  */
 SetEmptyRgn(((GrafPtr) w)->visRgn);
 
 SetPort(oldPort);
 
 return(TRUE);
}
test.c Listing
/*****************************************************
 * test.c
 *
 * Driver function and example TileStackWindows
 * function to test out TileStackWindows.
 ****************************************************/

#include "TileStackWindows.h"

/*****************************************************
 * defines
 ****************************************************/

#define NUM_TEST_WINDOWS  20

/*****************************************************
 * prototypes
 ****************************************************/
 
static Boolean StackWindows(Rect *enclosingRectPtr,
 WindowElementPtr p, int wCount);

/*****************************************************
 * main
 ****************************************************/
void main()
{
 WindowPtr*p, windowPtrArray[NUM_TEST_WINDOWS];
 Rect   theBounds;
 int    i;
 
 theBounds.top = 50;
 theBounds.left = 50;
 theBounds.bottom = 200;
 theBounds.right = 200;
 
 p = windowPtrArray;
 i = NUM_TEST_WINDOWS;
 do {
 *p++ = NewWindow(0L, &theBounds, "\pTest",
 TRUE, 0, (WindowPtr) -1, TRUE, 0);
 OffsetRect(&theBounds, 3, 2);
 } while (--i);
 
 TileStackWindows(StackWindows);
}


/* window stacking variables used by StackWindows */
#define WTitleHeight 18
#define StaggerH 7
#define StaggerV (WTitleHeight - 2)
#define MinVertSize200
#define NextRowOffsetH    2
#define NextRowOffsetV    4

/*****************************************************
 * StackWindows -- example TileStackWindows proc
 *
 * Stack all the windows so you can see their
 * titles. Returns TRUE if we moved or sized at
 * least one window (if not then we don't need to
 * redraw the screen).
 ****************************************************/
static Boolean StackWindows(enclosingRectPtr, p, wCount)
Rect    *enclosingRectPtr;
WindowElementPtr p;
intwCount;
{
 WindowPtrw;
 Rect   theBounds;
 Point  upperLeft;
 int    width;
 BooleandidOne, sizeChanged;
 
 theBounds = *enclosingRectPtr;
 theBounds.top += WTitleHeight;
 upperLeft = topLeft(theBounds);
 width = theBounds.right - theBounds.left;
 
 didOne = FALSE;
 
 /* this particular routine starts at the back
  * of the list and works it's way forward
  * (to the frontmost window)
  */
 p += wCount;
 do {
 p--;
 w = (WindowPtr) p->theWindowPtr;
 sizeChanged = MySizeWindow(w,
 theBounds.right - theBounds.left,
 theBounds.bottom - theBounds.top);
 didOne |= sizeChanged;
 didOne |= MyMoveWindow(w, theBounds.left,
 theBounds.top, sizeChanged);
 theBounds.left += StaggerH;
 theBounds.right += StaggerH;
 theBounds.top += StaggerV;
 if (theBounds.top >
   enclosingRectPtr->bottom - MinVertSize) {
 upperLeft.h += NextRowOffsetH;
 upperLeft.v += NextRowOffsetV;
 topLeft(theBounds) = upperLeft;
 theBounds.right = theBounds.left + width;
 }
 if (theBounds.right > enclosingRectPtr->right)
 theBounds.right = enclosingRectPtr->right;
 } while (--wCount);
 
 return (didOne);
}

The Rules

Here’s how it works: Each month there will be a different programming challenge presented here. First, you must write some code that solves the challenge. Second, you must optimize your code (a lot). Then, submit your solution to MacTech Magazine (formerly MacTutor). A winner will be chosen based on code correctness, speed, size and elegance (in that order of importance) as well as the postmark of the answer. In the event of multiple equally desirable solutions, one winner will be chosen at random (with honorable mention, but no prize, given to the runners up). The prize for the best solution each month is $50 and a limited edition “The Winner! MacTech Magazine Programming Challenge” T-shirt (not to be found in stores).

In order to make fair comparisons between solutions, all solutions must be in ANSI compatible C (i.e., don’t use Think’s Object extensions). Only pure C code can be used. Any entries with any assembly in them will be disqualified (except for those challenges specifically stated to be in assembly). However, you may call any routine in the Macintosh toolbox you want (i.e., it doesn’t matter if you use NewPtr instead of malloc). All entries will be tested with the FPU and 68020 flags turned off in THINK C. When timing routines, the latest version of THINK C will be used (with ANSI Settings plus “Honor ‘register’ first” and “Use Global Optimizer” turned on) so beware if you optimize for a different C compiler. All code should be limited to 60 characters wide. This will aid us in dealing with e-mail gateways and page layout.

The solution and winners for this month’s Programmers’ Challenge will be published in the issue two months later. All submissions must be received by the 10th day of the month printed on the front of this issue.

All solutions should be marked “Attn: Programmers’ Challenge Solution” and sent to Xplain Corporation (the publishers of MacTech Magazine) via “snail mail” or preferably, e-mail - AppleLink: MT.PROGCHAL, Internet: progchallenge@xplain.com, CompuServe: 71552,174 and America Online: MT PRGCHAL. If you send via snail mail, please include a disk with the solution and all related files (including contact information). See page 2 for information on “How to Contact Xplain Corporation.”

MacTech Magazine reserves the right to publish any solution entered in the Programming Challenge of the Month and all entries are the property of MacTech Magazine upon submission. The submission falls under all the same conventions of an article submission.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Apple GarageBand 10.1 - Complete recordi...
The new GarageBand is a whole music creation studio right inside your Mac -- complete with keyboard, synths, orchestral and percussion instruments, presets for guitar and voice, an entirely... Read more
Duplicate Annihilator 5.7.7 - Find and d...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator... Read more
OS X Server 4.1.3 - For OS X 10.10 Yosem...
Designed for OS X and iOS devices, OS X Server makes it easy to share files, schedule meetings, synchronize contacts, develop software, host your own website, publish wikis, configure Mac, iPhone,... Read more
pwSafe 4.1 - Secure password management...
pwSafe provides simple and secure password management across devices and computers. pwSafe uses iCloud to keep your password databases backed-up and synced between Macs and iOS devices. It is... Read more
Kodi 15.0.rc1 - Powerful media center to...
Kodi (was XBMC) is an award-winning free and open-source (GPL) software media player and entertainment hub that can be installed on Linux, OS X, Windows, iOS, and Android, featuring a 10-foot user... Read more
Coda 2.5.11 - One-window Web development...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few surprises... Read more
Bookends 12.5.7 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Access the power of Bookends directly from Mellel, Nisus Writer Pro, or MS Word (... Read more
Maya 2016 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
RapidWeaver 6.2.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
MacFamilyTree 7.5.2 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more

Rage of Bahamut is Giving Almost All of...
The App Store isn't what it used to be back in 2012, so it's not unexpected to see some games changing their structures with the times. Now we can add Rage of Bahamut to that list with the recent announcement that the game is severely cutting back... | Read more »
Adventures of Pip (Games)
Adventures of Pip 1.0 Device: iOS iPhone Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ** ONE WEEK ONLY — 66% OFF! *** “Adventures of Pip is a delightful little platformer full of charm, challenge and impeccable... | Read more »
Divide By Sheep - Tips, Tricks, and Stre...
Who would have thought splitting up sheep could be so involved? Anyone who’s played Divide by Sheep, that’s who! While we’re not about to give you complete solutions to everything (because that’s just cheating), we will happily give you some... | Read more »
NaturalMotion and Zynga Have Started Tea...
An official sequel to 2012's CSR Racing is officially on the way, with Zynga and NaturalMotion releasing a short teaser trailer to get everyone excited. Well, as excited as one can get from a trailer with no gameplay footage, anyway. [Read more] | Read more »
Grab a Friend and Pick up Overkill 3, Be...
Overkill 3 is a pretty enjoyable third-person shooter that was sort of begging for some online multiplayer. Fortunately the begging can stop, because its newest update has added an online co-op mode. [Read more] | Read more »
Scanner Pro's Newest Update Adds Au...
Scanner Pro is one of the most popular document scanning apps on iOS, thanks in no small part to its near-constant updates, I'm sure. Now we're up to update number six, and it adds some pretty handy new features. [Read more] | Read more »
Heroki (Games)
Heroki 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: CLEAR THE SKIES FOR A NEW HERO!The peaceful sky village of Levantia is in danger! The dastardly Dr. N. Forchin and his accomplice,... | Read more »
Wars of the Roses (Games)
Wars of the Roses 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: | Read more »
TapMon Battle (Games)
TapMon Battle 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: It's time to battle!Tap! Tap! Tap! Try tap a egg to hatch a Tapmon!Do a battle with another tapmons using your hatched tapmons! *... | Read more »
Alchemic Dungeons (Games)
Alchemic Dungeons 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: ### Release Event! ### 2.99$->0.99$ for limited time! ### Roguelike Role Playing Game! ### Alchemic Dungeons is roguelike... | Read more »

Price Scanner via MacPrices.net

13-inch 1.6GHz MacBook Air on sale for $849,...
Best Buy has the 2015 13″ 1.6GHz/128GB MacBook Air on sale for $849.99 on their online store this weekend. Choose free shipping or free local store pickup (if available). Sale price for online orders... Read more
Apple Refurbished iMacs available for up to $...
The Apple Store has Apple Certified Refurbished iMacs available for up to $380 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 27″ 3.5GHz 5K iMac – $1949 $... Read more
Apple refurbished 2014 13-inch Retina MacBook...
The Apple Store has Apple Certified Refurbished 2014 13″ Retina MacBook Pros available for up to $400 off original MSRP, starting at $979. An Apple one-year warranty is included with each model, and... Read more
Seagate Backup Plus Drives Feature 200GB of C...
Seagate Technology plc has announced that its Backup Plus family of external storage offerings will now include 200GB of OneDrive cloud storage, a major added value, and the addition of Lyve’s photo... Read more
Canon PIXMA MG3620 Wireless Inkjet All-in-One...
Canon U.S.A., Inc. has announced the PIXMA MG3620 Wireless (1) Inkjet All-in-One (AIO) printer for high-quality photo and document printing. Built with convenience in mind for the everyday home user... Read more
July 4th Holiday Weekend 13-inch MacBook Pro...
Save up to $150 on the purchase of a new 2015 13″ Retina MacBook Pro at the following resellers this weekend. Shipping is free with each model: 2.7GHz/128GB MSRP $1299 2.7GHz/... Read more
27-inch 3.5GHz 5K iMac on sale for $2149, sav...
Best Buy has the 27″ 3.5GHz 5K iMac on sale for $2149.99. Choose free shipping or free local store pickup (if available). Sale price for online orders only, in-store prices may vary. Their price is $... Read more
Apple now offering refurbished 2015 11-inch...
The Apple Store is now offering Apple Certified Refurbished 2015 11″ MacBook Airs as well as 13″ MacBook Airs (the latest models), available for up to $180 off the cost of new models. An Apple one-... Read more
15-inch 2.5GHz Retina MacBook Pro on sale for...
Amazon.com has the 15″ 2.5GHz Retina MacBook Pro on sale for $2274 including free shipping. Their price is $225 off MSRP, and it’s the lowest price available for this model. Read more
Finally Safe To Upgrade To Yosemite’?
The reason I’ve held back from upgrading my MacBook Air from OS X 10.9 Mavericks to 10.10 Yosemite for nearly a year isn’t just procrastination. Among other bugs reported, there have been persistent... Read more

Jobs Board

*Apple* Music Producer - Apple (United State...
**Job Summary** Apple Music seeks a Producer to help shepherd some of the most important content and editorial initiatives within the music app, with a particular focus Read more
Editor, *Apple* News - Apple (United States...
**Job Summary** Editor, Apple News The Apple News team is looking for passionate, knowledgeable editors to help identify and deliver the best in breaking national, Read more
*Apple* Watch SW Application Project Manager...
**Job Summary** The Apple Watch software team is looking for an Application Engineering Project Manager to work on new projects for Apple . The successful candidate Read more
Engineering Project Manager - *Apple* Searc...
**Job Summary** Apple 's new Spotlight Suggestions service provides fast, relevant search results from the Inte et in Spotlight and Safari on iOS and OS X. We are looking Read more
Business Development Manager - *Apple* Pay...
**Job Summary** Apple Pay is seeking an experienced relationship manager to support the ongoing management of partners for the Apple Pay platform. This position will Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.