jim@artecon.UUCP (Jim Wang) (01/03/86)
echo x - README sed 's/^X//' >README <<'*-*-END-of-README-*-*' X In September, Scientific American carried an article in the XMathematical Games section (or whatever they're calling it now) describing X'crabs', cutesy little critters that scamper all over your graphics screen Xeating chunks of your favorite bitmap and replacing them with their favorite Xpattern. And they do so with no regard for window or process boundaries, Xmuch to the window manager's consternation! The version described in SA Xran on Teletype Blit terminals at Bell Labs. X Then, Doug Gywn of BRL (gwyn@brl-tgr.ARPA) posted a version of 'crabs' Xfor the Blit terminal. Not having a Blit but a Sun, I proceeded to modify XDoug's original program using Sun pixrect commands to violate window Xboundaries. Just to get it working didn't take too much effort, but Xevolution took its course and now most of the routines have changed X(sorry, Doug) although they retain their original names. However, XDoug's excellent collision avoidance system (air traffic controllers Xshould take note) is untouched. X The evolution means that this implementation doesn't follow Xprecisely the description in SA. Among the differences: X X - There are six types of crabs (user selectable, of course) X all of which eat in the same manner, but create different X types of backgrounds. If you start up several different X types of crabs, they'll eventually cover the whole screen X and start battling each other for territory. Fascinating X to watch if you have lots of time. X X - Crabs can run in invisible mode. Great for playing nasty X tricks on unsuspecting console users. Looks like bitmap X rot. X X - Crabs accelerate (get hungry and desparate) for each step X they take in background pattern. They're much more X effective this way on sparse screens. X X - The crab image itself is different than the one in SA. X X - As far as the crabs are concerned, the screen wraps X around both vertically and horizontally. X X Sun crabs run on Sun 2 & 3's, Release 2.x and 3.0 alpha, Xon Models 50, 120, and 160. Compile it with: X X cc -O -o crabs crabs.c -lpixrect -DNOCOLLIDE X X Coming next: (or rather, I'm hoping someone will write it) XSunAnts, industrious insects who search for non-blank pieces of your Xscreen, pick them up, and neatly stack them in a corner. X XJim Wang X(seismo!jim, jim@seismo) X X X=============== *-*-END-of-README-*-* echo x - crabs.6 sed 's/^X//' >crabs.6 <<'*-*-END-of-crabs.6-*-*' X.TH CRABS 6 "15 October 1985" X.SH NAME Xcrabs \- digital creatures which feed on screen images X.SH SYNOPSIS X\f3crabs\fP [\f3-g\fP\f2n\fP] [\f3-i\fP] [\f3-p\fP\f2sec\fP] X.br X.SH DESCRIPTION X.LP X\f2crabs\fP is a rather loose implementation of the ``crabs'' Xdescribed in the September 1985 issue of Scientific American. XBasically, crabs are little digital creatures which scuttle around Xon the Sun screen, eating pieces of the bitmap and replacing them Xwith a pattern of their own choosing. X.PP XCrabs travel fast in areas of their own pattern; when they find Xan alien pattern, they eat it, replace it with their own pattern, Xthen wander off in a different direction in search of more food. X.PP XThere are different types of crabs, distinguished only by the Xpatterns they create after digesting bits. X.SH OPTIONS X.LP XThere are only three options: X.TP X\f3-g\fP\f2n\fP XSelect the type of background the crabs will create: 0=white, X1=Blit grey, 2=Suntools grey, 3=inverse Suntools grey, 4=vertical Xstripes, and 5=black. XWhite is useful for consoles that are not being used for graphics; Xthe crabs will wander among the letters, eating them. XSuntools grey is useful when Suntools is running. XThe default is Suntools grey. X.TP X\f3-i\fP XMake the crabs invisible; the only indication that they are running Xis the deterioration of the screen image. XThe default is for visible crabs. X.TP X\f3-p\fP\f2sec\fP XSpecify the sleep interval between crab motions, in seconds. XThe default time is 0.5 second; this is also the minimum, since Xotherwise \f2crabs\fP can be a CPU hog. X.SH "NOTES" X.LP X\f2crabs\fP runs at low priority to be nice to other users. X.LP X\f2crabs\fP may act differently on a Sun 160 screen because of the Xdiscrepancy in bitmap depths. X.SH AUTHORS X.LP XDoug Gywn (gwyn@brl-tgr.ARPA) Blit version. X.br XJim Wang (jim@seismo) Sun version. *-*-END-of-crabs.6-*-* echo x - crabs.c sed 's/^X//' >crabs.c <<'*-*-END-of-crabs.c-*-*' X#ifndef lint Xstatic char RCSid[] = "$Header: crabs.c,v 1.6 85/12/05 09:45:34 jim Exp $"; X#endif X X/* X * $Log: crabs.c,v $ X * Revision 1.6 85/12/05 09:45:34 jim X * Minor cleanup. X * X * Revision 1.5 85/10/01 09:27:15 jim X * 1) Added argument range and sensibility checking. X * 2) Comments cleaned up a little. X * 3) Changed -p option to accept floating point seconds instead of usec. X * X * Revision 1.4 85/09/30 16:56:12 jim X * Fixed XOR images and positioning using up to eight relative positions X * with respect to background. X * NOCOLLIDE option seems to work. X * X * Revision 1.3 85/09/26 13:17:32 jim X * 1) Collision avoidance reinstated. X * 2) Now partially works properly with run time xor'ing. X * X * Revision 1.2 85/09/26 11:32:02 jim X * Changes from original: X * 1) Ability to specify different backgrounds. X * 2) Changed rand() to random() with seeding based on time. X * 3) Added crab acceleration factor for hungry crabs. X * 4) Added xor'ing of crabs with bg at run time instead of at compile time. X * 5) Collision avoidance commented out; to be reinstated. X * 6) Added -p flag for sleep interval (usec) and -g flag for background. X * 7) Wraparound screen. X * X * Revision 1.1 85/09/26 11:31:16 jim X * Initial revision X * X * X */ X X/* for xc: X% cc -O -o crabs crabs.c -lpixrect -DNOCOLLIDE X%d cc -g -o crabs crabs.c -lpixrect -DNOCOLLIDE X%l lint -chb -DNOCOLLIDE crabs.c > lout X*/ X X/* X crabs -- see September 1985 Scientific American pages 18..23 X*/ X X#ifndef lint X /* for "what" utility */ Xstatic char SCCS_ID[] = "@(#)crabs.c 1.1 85/09/03"; X#endif X X#include <stdio.h> X#include <sys/time.h> X#include <sys/resource.h> X#include <pixrect/pixrect_hs.h> X#include <sunwindow/rect.h> /* for the rect structure */ X X#define ABS(x) ((x > 0) ? (x) : (0 - x)) Xtypedef int bool; /* Boolean data type */ X X#define NCRABS 8 /* total number of crabs (1..32) */ X#define CWIDTH 8 /* crab size */ X#define CHEIGHT 8 X X#define PIX_XOR (PIX_SRC^PIX_DST) X#define MAXVEL 8 /* bound absolute velocity component */ X#define MAX_SEARCH 8 /* no. of steps before new direction */ X#define PERIOD 0.50 /* default sleep time (sec) */ X#define MILLION 1000000.0 /* the number of usec in a sec */ X Xstatic bool visible; /* true if crabs are to be shown */ X Xtypedef struct { /* struct to describe a crab location */ X int x, y; X } Point; X X /* The following must be at least 16 bits wide */ Xstatic short white_image[] = /* white texture */ X{ 0x0000, /* 1 row */ X}; X Xstatic short blit_grey_image[] = /* grey as for Blit */ X{ 0x4444, 0x1111, /* 2 rows */ X}; X Xstatic short root_grey_image[] = /* Suntools root grey */ X{ 0x8888, 0x8888, 0x2222, 0x2222, /* 4 rows */ X}; X Xstatic short inv_grey_image[] = /* inverse root grey */ X{ 0x7777, 0x7777, 0xdddd, 0xdddd, /* 4 rows */ X}; X Xstatic short stripe_image[] = /* vertical stripes */ X{ 0x8888, /* 1 row */ X}; X Xstatic short black_image[] = /* solid black */ X{ 0xffff, /* 1 row */ X}; X Xmpr_static(white, 16, 1, 1, white_image); Xmpr_static(blit, 16, 2, 1, blit_grey_image); Xmpr_static(root_grey, 16, 4, 1, root_grey_image); Xmpr_static(inv_grey, 16, 4, 1, inv_grey_image); Xmpr_static(stripe, 16, 1, 1, stripe_image); Xmpr_static(black, 16, 1, 1, black_image); X Xstatic struct pixrect *backgrounds[] = X{ &white, &blit, &root_grey, X &inv_grey, &stripe, &black X}; X Xstatic struct pixrect *grey; /* the one that is finally chosen */ X Xstatic struct X{ Point ulc; /* upr left corner screen coordinates */ X Point vel; /* velocity (pixels/cycle) */ X int search_count; /* count of hungry steps for new dir */ X} crab[NCRABS]; /* keeps track of crabs' state */ X Xstatic struct pixrect *pr; /* the root pixrect */ X X /* Crab images XORed with grey texture at various offsets: */ X X /* X ** We need to define eight crab images in each of four orientations X ** relative to the background pattern. This makes for 32 X ** total images to create (eight orientations x four positions). X ** Create at init time rather than as static as original did, X ** since the type of background won't be known til then. X ** Some patterns really only require four orientations. X */ X X /* X ** The bitmaps for the four orientations, defined as X ** 16 bits wide. Only the left side is filled since the crabs X ** are only 8 bits wide, but mpr_static requires shorts, so X ** the right sides are all 0. X */ X Xstatic short up_data[] = /* facing up */ X{ 0x4200, 0xa500, 0xa500, 0x4200, 0x3c00, 0x7e00, 0xff00, 0x7e00, X}; X Xstatic short down_data[] = /* facing down */ X{ 0x7e00, 0xff00, 0x7e00, 0x3c00, 0x4200, 0xa500, 0xa500, 0x4200, X}; X Xstatic short right_data[] = /* facing right */ X{ 0x2600, 0x7900, 0xf600, 0xf000, 0xf000, 0xf600, 0x7900, 0x2600, X}; X Xstatic short left_data[] = /* facing left */ X{ 0x6200, 0x9700, 0x6f00, 0x0f00, 0x0f00, 0x6f00, 0x9700, 0x6200, X}; X Xstatic struct pixrect *upmap[4][2]; /* facing up */ Xstatic struct pixrect *downmap[4][2]; /* facing down */ Xstatic struct pixrect *rightmap[4][2]; /* facing right */ Xstatic struct pixrect *leftmap[4][2]; /* facing left */ X X /* X ** Crab "vicinities" are recorded in the following X ** global map; see Collide() and Draw() for details: X */ X X#define XMAX 1151 /* Sun screen size */ X#define YMAX 899 Xstatic int vicinity[(XMAX + 31) / 32 + 2][(YMAX + 31) / 32 + 2]; X /* includes margins all around */ X Xstatic void Cycle(), DrawCrab(), HideCrabs(), Init(), ModVel(), NewVel(); Xstatic int Collide(), RandInt(); X Xstatic struct pixrect *mem_pr; /* static memory pixrect for backgr */ Xstatic short mem_im[32]; /* pixrect image */ X Xstatic int sec, usec; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X void nap(); X X Init(argc, argv); /* set up initial grey crab layer */ X for (;;) /* no way out! */ X { nap(sec, usec); /* relinquish the processor */ X Cycle(); /* move the crabs */ X } X /*NOTREACHED*/ X} X X X/* ARGSUSED */ Xstatic void XInit(argc, argv) /* set up initial crab layer */ Xint argc; Xchar *argv[]; X{ X register int i, j, max_bg; X double nap_int; /* sleep interval, sec */ X struct timeval tv; /* for random() seed */ X X char *myname; X struct pixrect *up_image, *down_image; X struct pixrect *right_image, *left_image; X X struct pixrect *pr_open(); X double atof(); X X visible = 1; /* defaults */ X nap_int = PERIOD; X grey = &root_grey; X X max_bg = sizeof(backgrounds)/sizeof(struct pixrect *); X X myname = *argv; X while(*++argv) X { if(**argv != '-') X break; X switch(*(*argv + 1)) X { case 'g': /* background ? */ X if(atoi(*argv + 2) < max_bg) X grey = backgrounds[atoi(*argv + 2)]; X else X fprintf(stderr, "%s: Max -g is %0d.\n", X myname, max_bg); X break; X case 'i': /* invisible ? */ X visible = 0; X break; X case 'p': /* sleep interval */ X nap_int = atof(*argv + 2); X break; X default: X fprintf(stderr, "%s: Unknown option: %c.\n", X myname, *(*argv + 1)); X break; X } X } X X if(nap_int < PERIOD) /* prevent from becoming CPU hog */ X { fprintf(stderr, "%s: Bad period %0.2f, reset to %0.2f sec.\n", X myname, nap_int, PERIOD); X nap_int = PERIOD; X } X sec = nap_int; X usec = MILLION * (nap_int - (double)sec); X X if( ! (pr = pr_open("/dev/fb"))) X exit(-1); X X /* get time for random(3) seed */ X (void) gettimeofday(&tv, NULL); X srandom(tv.tv_sec); X X /* set up memory pixrect for background */ X mem_pr = mem_point(CWIDTH, CHEIGHT, pr->pr_depth, (short *)mem_im); X X /* set up memory pixrect for the crab images (temporary) */ X /* mininum width is 16 for these guys */ X up_image = mem_point(16, CHEIGHT, 1, (short *)up_data); X down_image = mem_point(16, CHEIGHT, 1, (short *)down_data); X left_image = mem_point(16, CHEIGHT, 1, (short *)left_data); X right_image = mem_point(16, CHEIGHT, 1, (short *)right_data); X X /* set up bitmap images for the crabs XOR'ed with background */ X for(i = 0; i < 4; i++) X { for(j = 0; j < 2; j++) X { upmap[i][j] = mem_create(CWIDTH, CHEIGHT, 1); X pr_rop(upmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_SRC, up_image, 0, 0); X pr_replrop(upmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_XOR, grey, i, j); X X downmap[i][j] = mem_create(CWIDTH, CHEIGHT, 1); X pr_rop(downmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_SRC, down_image, 0, 0); X pr_replrop(downmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_XOR, grey, i, j); X X leftmap[i][j] = mem_create(CWIDTH, CHEIGHT, 1); X pr_rop(leftmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_SRC, left_image, 0, 0); X pr_replrop(leftmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_XOR, grey, i, j); X X rightmap[i][j] = mem_create(CWIDTH, CHEIGHT, 1); X pr_rop(rightmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_SRC, right_image, 0, 0); X pr_replrop(rightmap[i][j], 0, 0, CWIDTH, CHEIGHT, X PIX_XOR, grey, i, j); X } X } X X /* Create initial set of crabs: */ X for ( i = 0; i < NCRABS; ++i ) X { X /* Assign random position within "crabs" layer: */ X crab[i].ulc.x = RandInt(0, pr->pr_size.x - 8); X crab[i].ulc.y = 0; X X /* Assign random velocity: */ X NewVel(i); X X /* Draw crab at initial position (within "crabs" layer): */ X if (visible) X DrawCrab(i); X } X X pr_destroy(up_image); X pr_destroy(down_image); X pr_destroy(right_image); X pr_destroy(left_image); X X /* be nice - lower priority to lowest */ X (void) setpriority(PRIO_PROCESS, getpid(), 20); X} X Xstatic void XCycle() /* one motion cycle for all crabs */ X{ X Point p; /* new crab upper left corner */ X register int syndrome; /* crab collision mask */ X register int i; /* crab # */ X register struct pixrect *oldmap; X register short *sp, *end; X X oldmap = mem_pr; X X for (i = 0; i < NCRABS; ++i) X { X DrawCrab(i); /* erase crab from previous position */ X X p.x = crab[i].ulc.x + crab[i].vel.x; /* motion */ X p.y = crab[i].ulc.y + crab[i].vel.y; X X if(p.x < 0) X p.x += pr->pr_size.x; X else if(p.x > pr->pr_size.x) X p.x -= pr->pr_size.x; X X if(p.y < 0) X p.y += pr->pr_size.y; X else if(p.y > pr->pr_size.y) X p.y -= pr->pr_size.y; X X /* Check for collision with other crabs; X if you don't worry about this, you get X crud left behind from crab collisions X (visible in Scientific American article). X (Note that crab # i has been removed.) */ X X /* The strategy is: only undraw possibly colliding crabs. X The obvious alternative, not showing any crabs until X all locations have been painted, would probably cause X the set of crabs to flicker or to appear too faint. */ X X#ifdef NOCOLLIDE X if ( (syndrome = Collide( p )) != 0 ) X HideCrabs( syndrome );/* save from the following code */ X#endif X X /* Save old contents of new crab location: */ X pr_rop(oldmap, 0, 0, CWIDTH, CHEIGHT, PIX_SRC, pr, p.x, p.y); X X /* Paint the new location grey: */ X pr_replrop(pr, p.x, p.y, CWIDTH, CHEIGHT, X PIX_SRC, grey, p.x, p.y); X X /* Determine if new location used to be grey: */ X pr_rop(oldmap, 0, 0, CWIDTH, CHEIGHT, PIX_XOR, pr, p.x, p.y); X X end = ((short *)mem_im) + 4 * pr->pr_depth; X for (sp = (short *)mem_im; sp < end; sp++ ) X if(*sp != 0) break; X X if(sp < end) /* location wasn't gray before, bounce */ X { p = crab[i].ulc; X crab[i].search_count = 0; X NewVel( i ); /* randomly alter crab velocity */ X } X else X { crab[i].search_count++; X if(ABS(crab[i].vel.x) < MAXVEL) X crab[i].vel.x += (crab[i].vel.x < 0) ? -1 : 1; X if(ABS(crab[i].vel.y) < MAXVEL) X crab[i].vel.y += (crab[i].vel.y < 0) ? -1 : 1; X } X X#ifdef NOCOLLIDE X if (syndrome != 0) X HideCrabs(syndrome); /* bring them back */ X#endif X X /* Draw the crab in its new position: */ X crab[i].ulc = p; X X if(crab[i].search_count > MAX_SEARCH) X { crab[i].search_count = 0; X ModVel( i ); /* randomly alter crab velocity */ X } X X DrawCrab( i ); X } X} X X#ifdef NOCOLLIDE X Xstatic int XCollide( p ) /* return syndrome for crab collision */ XPoint p; /* crab upper left corner */ X{ X int syndrome; /* accumulate syndrome here */ X bool right = p.x % 32 > 32 - 8, X down = p.y % 32 > 32 - 8; /* more than one vicinity? */ X int x32 = p.x / 32, X y32 = p.y / 32; /* vicinity array indices */ X X /* "Or" in crabs from overlapping vicinities: */ X X syndrome = vicinity[x32 + 1][y32 + 1]; X if ( right ) X syndrome |= vicinity[x32 + 1 + 1][y32 + 1]; X if ( down ) X syndrome |= vicinity[x32 + 1][y32 + 1 + 1]; X if ( right && down ) X syndrome |= vicinity[x32 + 1 + 1][y32 + 1 + 1]; X X return syndrome; X} X X Xstatic void XHideCrabs( syndrome ) /* draw crabs contained in syndrome */ Xint syndrome; /* syndrome (crab bit flags) */ X{ X int i; /* indexes crab[.] */ X int m; /* bit mask for crab # i */ X X for ( m = 1, i = 0; i < NCRABS; m <<= 1, ++i ) X if ( (m & syndrome) != 0 ) /* crab contained in syndrome */ X DrawCrab( i ); /* toggle crab */ X} X X#endif X Xstatic void XDrawCrab(index) /* draw specified crab */ Xint index; /* crab # (0..NCRABS-1) */ X{ X Point p; /* upper left corner for crab image */ X Point v; /* crab velocity */ X register int i, j; /* selects 1/8 crab offsets wrt grey */ X int x32, y32; /* vicinity array indices */ X bool right, down; /* more than one vicinity? */ X int syn_bit; /* crab possible-occupancy bit */ X X if (visible) X { X p = crab[index].ulc; X v = crab[index].vel; X X if(grey == &blit || grey == &stripe) X { i = p.x % 4; X j = p.y % 2; X } X else if(grey == &root_grey || grey == &inv_grey) X { i = ((((p.y % 4) >> 1) << 1 ) + p.x) % 4; X j = p.y % 2; X } X else /* white and black */ X { i = 0; X j = 0; X } X X if ( ABS( v.x ) >= ABS( v.y ) ) X { if ( v.x < 0 ) X pr_rop(pr, p.x, p.y, CWIDTH, CHEIGHT, PIX_XOR, X upmap[i][j], 0, 0); X else X pr_rop(pr, p.x, p.y, CWIDTH, CHEIGHT, PIX_XOR, X downmap[i][j], 0, 0); X } X else X { if ( v.y < 0 ) X pr_rop(pr, p.x, p.y, CWIDTH, CHEIGHT, PIX_XOR, X rightmap[i][j], 0, 0); X else X pr_rop(pr, p.x, p.y, CWIDTH, CHEIGHT, PIX_XOR, X leftmap[i][j], 0, 0); X } X X#ifdef NOCOLLIDE X /* A crab's vicinities are the disjoint 32x32 regions X that contain any piece of the crab's 8x8 square. X On the average, 9 out of 16 crabs occupy just 1 X vicinity; 6 out of 16 crabs occupy 2 vicinities, X and 1 out of every 16 crabs occupies 4 vicinities. */ X X x32 = p.x >> 5; X y32 = p.y >> 5; /* coords for upper left vicinity */ X X right = p.x % 32 > 32 - 8; /* also next vicinity right? */ X down = p.y % 32 > 32 - 8; /* also next vicinty down? */ X X /* Toggle crab's occupancy bit in all occupied vicinities: */ X X syn_bit = 1 << index; X X vicinity[x32 + 1][y32 + 1] ^= syn_bit; X if(right) X vicinity[x32 + 1 + 1][y32 + 1] ^= syn_bit; X if(down) X vicinity[x32 + 1][y32 + 1 + 1] ^= syn_bit; X if(right && down) X vicinity[x32 + 1 + 1][y32 + 1 + 1] ^= syn_bit; X#endif X } X /* else nibble away but don't show crabs */ X} X Xstatic void XNewVel(i) /* assign new velocity to crab */ Xint i; /* crab index */ X{ X crab[i].vel.x = RandInt( 0 - MAXVEL, MAXVEL ); X crab[i].vel.y = RandInt( 0 - MAXVEL, MAXVEL ); X X /* Velocity (0,0) is okay since we repeatedly modify all velocities. */ X} X Xstatic void XModVel( i ) /* randomly modify crab velocity */ Xint i; /* crab # */ X{ X int d; /* increment */ X X if ( crab[i].vel.x >= MAXVEL - 2 ) X d = RandInt( -4, 2 ); X else if ( crab[i].vel.x <= 2 - MAXVEL ) X d = RandInt( -2, 4 ); X else X d = RandInt( -4, 4 ); X X crab[i].vel.x += d; X X if ( crab[i].vel.y >= MAXVEL - 2 ) X d = RandInt( -4, 2 ); X else if ( crab[i].vel.y <= 2 - MAXVEL ) X d = RandInt( -2, 4 ); X else X d = RandInt( -4, 4 ); X X crab[i].vel.y += d; X} X Xstatic int XRandInt(lo, hi) /* generate random integer in range */ Xint lo, hi; /* range lo..hi-1 */ X{ X register long rn; X long random(); X X rn = random() >> 16; X return(lo + ((1 << 14) + (hi - lo) * rn)/(1<<15)); X} X Xstatic Xvoid Xnap(seconds, microseconds) Xint seconds, microseconds; X{ X struct timeval timeout; X X timeout.tv_sec = seconds; X timeout.tv_usec = microseconds; X X /* a cheap sub-second delay */ X (void) select(1, 0, 0, 0, &timeout); X} *-*-END-of-crabs.c-*-* exit