/* -*- Mode: C; tab-width: 4 -*- */ /* ant3d --- Extension to Langton's generalized turing machine ants */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)ant3d.c 5.13 2004/07/23 xlockmore"; #endif /*- * Copyright (c) 1999 by David Bagley. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 28-Jun-2004: A few more features added, need better "eyes" on leading * cube (instead of a small black face) * 08-Jun-2004: Resurrected code after seeing Heiko Hamann's "Definition * Behavior of Langton's Ant in Three Dimensions", Complex * Systems Volume 14, Issue 3, (2003) pp 264-268 * heiko.hamann AT gmx.de * 12-Aug-1999: Coded from life3d.c and ant.c probably will not work but... */ #ifdef STANDALONE #define PROGCLASS "Ant3D" #define HACK_INIT init_ant3d #define HACK_DRAW draw_ant3d #define ant3d_opts xlockmore_opts #define DEFAULTS "*delay: 5000 \n" \ "*count: -3 \n" \ "*cycles: 100000 \n" \ "*ncolors: 200 \n" \ "*wireframe: False \n" \ "*fullrandom: False \n" \ "*verbose: False \n" #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #include "automata.h" #ifdef MODE_ant3d #define DEF_LABEL "True" #define FONT_HEIGHT 19 #define FONT_WIDTH 15 #define DEF_RULE "A" /* All rules */ #define DEF_EYES "False" static char *rule; static Bool eyes; static Bool label; static XrmOptionDescRec opts[] = { {(char *) "-label", (char *) ".ant3d.label", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+label", (char *) ".ant3d.label", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-rule", (char *) ".ant3d.rule", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-eyes", (char *) ".ant3d.eyes", XrmoptionNoArg, (caddr_t) "on"} , {(char *) "+eyes", (char *) ".ant3d.eyes", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & label, (char *) "label", (char *) "Label", (char *) DEF_LABEL, t_Bool}, {(void *) & rule, (char *) "rule", (char *) "Rule", (char *) DEF_RULE, t_String}, {(void *) & eyes, (char *) "eyes", (char *) "Eyes", (char *) DEF_EYES, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+label", (char *) "turn on/off rule labeling"}, {(char *) "-rule string", (char *) "base 4 string for Turk's Ant"}, {(char *) "-/+eyes", (char *) "turn on/off eyes"} }; ModeSpecOpt ant3d_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct ant3d_description = {"ant3d", "init_ant3d", "draw_ant3d", "release_ant3d", "refresh_ant3d", "init_ant3d", NULL, &ant3d_opts, 5000, -3, 100000, 1, 64, 1.0, "", "Shows 3D ants", 0, NULL}; #endif #define ANT3DBITS(n,w,h)\ if ((ap->pixmaps[ap->init_bits]=\ XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\ free_ant3d(display,ap); return;} else {ap->init_bits++;} /* If you change the table you may have to change the following 2 constants */ #define STATES 2 #define MINANTS 1 #define REDRAWSTEP 2000 /* How much tape to draw per cycle */ #define ANGLES 360 /* Don't change these numbers without changing the offset() macro below! */ #define MAXSTACKS 64 #define MAXROWS 128 #define MAXCOLUMNS 128 #define BASESIZE ((MAXCOLUMNS*MAXROWS*MAXSTACKS)>>6) #define RT_ANGLE 90 #define HALFRT_ANGLE 45 /* Store state of cell in top bit. Reserve low bits for count of living nbrs */ #define Set3D(x,y,z,c) SetMem(ap,(unsigned int)x,(unsigned int)y,(unsigned int)z,c) #define Reset3D(x,y,z) SetMem(ap,(unsigned int)x,(unsigned int)y,(unsigned int)z,0) #define SetList3D(x,y,z,c) if (!SetMem(ap,(unsigned int)x,(unsigned int)y,(unsigned int)z,c)) return False; \ if (!AddToList(ap,(unsigned int)x,(unsigned int)y,(unsigned int)z,c)) return False #define EyeToScreen 72.0 /* distance from eye to screen */ #define HalfScreenD 20.0 /* 1/2 the diameter of screen */ #define BUCKETSIZE 10 #define NBUCKETS ((MAXCOLUMNS+MAXROWS+MAXSTACKS)*BUCKETSIZE) #define Distance(x1,y1,z1,x2,y2,z2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)) #define IP (M_PI / 180.0) #define NCOLORS 14 typedef struct { unsigned char color; short turn; unsigned char next; } statestruct; typedef struct _antstruct { int column, row, stack; unsigned char color; char visible; short direction; /* Direction facing */ short gravity; /* Need to know which way is down */ unsigned char state; short dist; /* dist from cell to eye */ struct _antstruct *next; /* pointer to next entry on linked list */ struct _antstruct *prev; struct _antstruct *priority; } antstruct; typedef struct { Bool painted; int ox, oy, oz; /* origin */ double vx, vy, vz; /* viewpoint */ int generation; int ncolumns, nrows, nstacks; int memstart; unsigned char *base[BASESIZE]; double A, B, C, F; int width, height; unsigned char ncolors, nstates; int n; double azm; double metaAlt, metaAzm, metaDist; antstruct *ants; antstruct *ptrhead, *ptrend, eraserhead, eraserend; antstruct *buckethead[NBUCKETS], *bucketend[NBUCKETS]; Bool wireframe, eyes; Pixmap dbuf; statestruct machine[NCOLORS * STATES]; unsigned char *tape; int init_bits; unsigned long colors[NCOLORS - 1]; GC stippledGC; Pixmap pixmaps[NCOLORS - 1]; int nInvisible; int labelOffsetX, labelOffsetY; char ruleString[40]; } antfarm3dstruct; /* Coordinate ant moves */ #define NU -1 /* Not used */ #define XN 0 #define XP 1 #define YN 2 #define YP 3 #define ZN 4 #define ZP 5 /* Relative ant moves */ #define TRS 0 /* Turn right, then step */ #define TLS 1 /* Turn left, then step */ #define TUS 2 /* Turn up, then step */ #define TDS 3 /* Turn down, then step */ #define FS 4 /* Step */ #define TBS 5 /* Turn back, then step */ /* Uhhh for completeness I guess there are some yaw moves and a 180 TU */ #define STR 6 /* Step then turn right */ #define STL 7 /* Step then turn left */ #define STU 8 /* Step then turn up */ #define STD 9 /* Step then turn down */ #define SF 10 /* Step */ #define STB 11 /* Step then turn back */ /* turnDirection, oldGravity, oldDirection */ static int newDirectionMap[4][6][6] = { { /* TRS */ {NU, NU, ZP, ZN, YN, YP}, /* oldGravity = XN */ {NU, NU, ZN, ZP, YP, YN}, {ZN, ZP, NU, NU, XP, XN}, /* oldGravity = YN */ {ZP, ZN, NU, NU, XN, XP}, {YP, YN, XN, XP, NU, NU}, /* oldGravity = ZN */ {YN, YP, XP, XN, NU, NU} }, { /* TLS */ {NU, NU, ZN, ZP, YP, YN}, /* oldGravity = XN */ {NU, NU, ZP, ZN, YN, YP}, {ZP, ZN, NU, NU, XN, XP}, /* oldGravity = YN */ {ZN, ZP, NU, NU, XP, XN}, {YN, YP, XP, XN, NU, NU}, /* oldGravity = ZN */ {YP, YN, XN, XP, NU, NU} }, { /* TUS */ {NU, NU, XP, XP, XP, XP}, /* oldGravity = XN */ {NU, NU, XN, XN, XN, XN}, {YP, YP, NU, NU, YP, YP}, /* oldGravity = YN */ {YN, YN, NU, NU, YN, YN}, {ZP, ZP, ZP, ZP, NU, NU}, /* oldGravity = ZN */ {ZN, ZN, ZN, ZN, NU, NU} }, { /* TDS */ {NU, NU, XN, XN, XN, XN}, /* oldGravity = XN */ {NU, NU, XP, XP, XP, XP}, {YN, YN, NU, NU, YN, YN}, /* oldGravity = YN */ {YP, YP, NU, NU, YP, YP}, {ZN, ZN, ZN, ZN, NU, NU}, /* oldGravity = ZN */ {ZP, ZP, ZP, ZP, NU, NU} } }; /* turnDirection, oldGravity, oldDirection */ static int newGravityMap[4][6][6] = { { /* TRS */ {NU, NU, XN, XN, XN, XN}, /* oldGravity = XN */ {NU, NU, XP, XP, XP, XP}, {YN, YN, NU, NU, YN, YN}, /* oldGravity = YN */ {YP, YP, NU, NU, YP, YP}, {ZN, ZN, ZN, ZN, NU, NU}, /* oldGravity = ZN */ {ZP, ZP, ZP, ZP, NU, NU} }, { /* TLS */ {NU, NU, XN, XN, XN, XN}, /* oldGravity = XN */ {NU, NU, XP, XP, XP, XP}, {YN, YN, NU, NU, YN, YN}, /* oldGravity = YN */ {YP, YP, NU, NU, YP, YP}, {ZN, ZN, ZN, ZN, NU, NU}, /* oldGravity = ZN */ {ZP, ZP, ZP, ZP, NU, NU} }, { /* TUS */ {NU, NU, YN, YP, ZN, ZP}, /* oldGravity = XN */ {NU, NU, YN, YP, ZN, ZP}, {XN, XP, NU, NU, ZN, ZP}, /* oldGravity = YN */ {XN, XP, NU, NU, ZN, ZP}, {XN, XP, YN, YP, NU, NU}, /* oldGravity = ZN */ {XN, XP, YN, YP, NU, NU} }, { /* TDS */ {NU, NU, YP, YN, ZP, ZN}, /* oldGravity = XN */ {NU, NU, YP, YN, ZP, ZN}, {XP, XN, NU, NU, ZP, ZN}, /* oldGravity = YN */ {XP, XN, NU, NU, ZP, ZN}, {XP, XN, YP, YN, NU, NU}, /* oldGravity = ZN */ {XP, XN, YP, YN, NU, NU} } }; static antfarm3dstruct *antfarm3ds = NULL; static unsigned char tables[][3 * NCOLORS * STATES + 2] = { #if 0 /* Generated */ { /* 0: RLU */ 3, 1, 1, TRS, 0, 2, TLS, 0, 0, TUS, 0 }, { /* 1: RUL */ 3, 1, 1, TRS, 0, 2, TUS, 0, 0, TLS, 0 }, { /* 2: RUD */ 3, 1, 1, TRS, 0, 2, TUS, 0, 0, TDS, 0 }, { /* 3: RLUU */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TUS, 0, 0, TUS, 0 }, { /* 4: RRLU 32 */ 4, 1, 1, TRS, 0, 2, TRS, 0, 3, TLS, 0, 0, TUS, 0 }, { /* 5: RRUL ? */ 4, 1, 1, TRS, 0, 2, TRS, 0, 3, TUS, 0, 0, TLS, 0 }, { /* 6: RRUD 22 */ 4, 1, 1, TRS, 0, 2, TRS, 0, 3, TUS, 0, 0, TDS, 0 }, { /* 7: RLRU 188 */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TRS, 0, 0, TUS, 0 }, { /* 8: RLLU ? */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TLS, 0, 0, TUS, 0 }, { /* 9: RLUR 46 ! */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TUS, 0, 0, TRS, 0 }, { /* 10: RLUL 32 */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TUS, 0, 0, TLS, 0 }, { /* 11: RLUU 102 */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TUS, 0, 0, TUS, 0 }, { /* 12: RLUD 28 */ 4, 1, 1, TRS, 0, 2, TLS, 0, 3, TUS, 0, 0, TDS, 0 }, { /* 13: RURL 30 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TRS, 0, 0, TLS, 0 }, { /* 14: RURD ? */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TRS, 0, 0, TDS, 0 }, { /* 15: RULL 22 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TLS, 0, 0, TLS, 0 }, { /* 16: RULR 22 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TLS, 0, 0, TRS, 0 }, { /* 17: RULU 22 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TLS, 0, 0, TUS, 0 }, { /* 18: RUUL 26 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TUS, 0, 0, TLS, 0 }, { /* 19: RULD 22 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TLS, 0, 0, TDS, 0 }, { /* 20: RUUD 22 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TUS, 0, 0, TDS, 0 }, { /* 21: RUDR 14 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TDS, 0, 0, TRS, 0 }, { /* 22: RUDL ? */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TDS, 0, 0, TLS, 0 }, { /* 23: RUDU 114 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TDS, 0, 0, TUS, 0 }, { /* 24: RUDD 40 */ 4, 1, 1, TRS, 0, 2, TUS, 0, 3, TDS, 0, 0, TDS, 0 }, { /* 25: RRRULL ? */ 6, 1, 1, TRS, 0, 2, TRS, 0, 3, TRS, 0, 4, TUS, 0, 5, TLS, 0, 0, TLS, 0, } { /* 26: RLRUUUL 25436 */ 7, 1, 1, TRS, 0, 2, TLS, 0, 3, TRS, 0, 4, TUS, 0, 5, TUS, 0, 6, TUS, 0, 0, TLS, 0, }, #endif { /* 27: RRLDDDULRRLLLL * */ 14, 1, 1, TRS, 0, 2, TRS, 0, 3, TLS, 0, 4, TDS, 0, 5, TDS, 0, 6, TDS, 0, 7, TUS, 0, 8, TLS, 0, 9, TRS, 0, 10, TRS, 0, 11, TLS, 0, 12, TLS, 0, 13, TLS, 0, 0, TLS, 0, } }; #define NTABLES (sizeof tables / sizeof tables[0]) #ifdef DEBUG static char * printDir(int d) { switch (d) { case XN: return (char *) "XN"; case XP: return (char *) "XP"; case YN: return (char *) "YN"; case YP: return (char *) "YP"; case ZN: return (char *) "ZN"; case ZP: return (char *) "ZP"; default: return (char *) "NU"; } } static char * printTurn(int d) { switch (d) { case TRS: return (char *) "TRS"; case TLS: return (char *) "TLS"; case TUS: return (char *) "TUS"; case TDS: return (char *) "TDS"; default: return (char *) "TXX"; } } #endif static void heading(short * direction, short * gravity, short turn) { short oldDirection = *direction; short oldGravity = *gravity; if (turn < 6) { if (turn == TBS) { if (oldDirection & 1) *direction = oldDirection - 1; else *direction = oldDirection + 1; } else if (turn < 4) { *gravity = newGravityMap [turn][oldGravity][oldDirection]; *direction = newDirectionMap [turn][oldGravity][oldDirection]; } } if (*gravity == NU) (void) fprintf(stderr, "gravity corruption\n"); if (*direction == NU) (void) fprintf(stderr, "direction corruption\n"); #ifdef DEBUG (void) printf( "odirection %s, ogravity %s, turn %s, direction %s, gravity %s\n", printDir(oldDirection), printDir(oldGravity), printTurn(turn), printDir(*direction), printDir(*gravity)); #endif } static void position_of_neighbor(antfarm3dstruct * ap, int dir, int *pcolumn, int *prow, int *pstack) { int column = *pcolumn, row = *prow, stack = *pstack; switch (dir) { case XN: column = (!column) ? ap->ncolumns - 1 : column - 1; break; case XP: column = (column + 1 == ap->ncolumns) ? 0 : column + 1; break; case YN: row = (!row) ? ap->nrows - 1 : row - 1; break; case YP: row = (row + 1 == ap->nrows) ? 0 : row + 1; break; case ZN: stack = (!stack) ? ap->nstacks - 1 : stack - 1; break; case ZP: stack = (stack + 1 == ap->nstacks) ? 0 : stack + 1; break; default: (void) fprintf(stderr, "wrong direction %d\n", dir); } *pcolumn = column; *prow = row; *pstack = stack; } /*--- list ---*/ /* initialise the state of all cells to OFF */ static void Init3D(antfarm3dstruct * ap) { ap->ptrhead = ap->ptrend = NULL; ap->eraserhead.next = &ap->eraserend; ap->eraserend.prev = &ap->eraserhead; } /*- * Function that adds the cell (assumed live) at (x,y,z) onto the search * list so that it is scanned in future generations */ static Bool AddToList(antfarm3dstruct * ap, unsigned int x, unsigned int y, unsigned int z, unsigned int c) { antstruct *tmp; if ((tmp = (antstruct *) malloc(sizeof (antstruct))) == NULL) return False; tmp->column = x; tmp->row = y; tmp->stack = z; tmp->color = c; if (ap->ptrhead == NULL) { ap->ptrhead = ap->ptrend = tmp; tmp->prev = NULL; } else { ap->ptrend->next = tmp; tmp->prev = ap->ptrend; ap->ptrend = tmp; } ap->ptrend->next = NULL; return True; } static void AddToEraseList(antfarm3dstruct * ap, antstruct * cell) { cell->next = &ap->eraserend; cell->prev = ap->eraserend.prev; ap->eraserend.prev->next = cell; ap->eraserend.prev = cell; } static void DelFromList(antfarm3dstruct * ap, antstruct * cell) { if (cell != ap->ptrhead) { cell->prev->next = cell->next; } else { ap->ptrhead = cell->next; if (ap->ptrhead != NULL) ap->ptrhead->prev = NULL; } if (cell != ap->ptrend) { cell->next->prev = cell->prev; } else { ap->ptrend = cell->prev; if (ap->ptrend != NULL) ap->ptrend->next = NULL; } AddToEraseList(ap, cell); } static void DelFromEraseList(antstruct * cell) { cell->next->prev = cell->prev; cell->prev->next = cell->next; free(cell); } /*--- memory ---*/ /*- * Simulate a large array by dynamically allocating 4x4x4 size cells when * needed. */ static void MemInit(antfarm3dstruct * ap) { int i; for (i = 0; i < BASESIZE; ++i) { if (ap->base[i] != NULL) { free(ap->base[i]); ap->base[i] = (unsigned char *) NULL; } } ap->memstart = 0; } #define BASE_OFFSET(x,y,z,b,o) \ b = ((x & 0x7c) << 7) + ((y & 0x7c) << 2) + ((z & 0x7c) >> 2); \ o = (x & 3) + ((y & 3) << 2) + ((z & 3) << 4); \ if (ap->base[b] == NULL) {\ if ((ap->base[b] = (unsigned char *) calloc(64, sizeof (unsigned char))) == NULL) {return False;}} #if 0 static Bool GetMem(antfarm3dstruct * ap, unsigned int x, unsigned int y, unsigned int z, int *m) { int b, o; if (ap->memstart) { MemInit(ap); } BASE_OFFSET(x, y, z, b, o); *m = ap->base[b][o]; return True; } #endif static Bool SetMem(antfarm3dstruct * ap, unsigned int x, unsigned int y, unsigned int z, unsigned int val) { int b, o; if (ap->memstart) { MemInit(ap); } BASE_OFFSET(x, y, z, b, o); ap->base[b][o] = val; return True; } #if 0 static Bool ChangeMem(antfarm3dstruct * ap, unsigned int x, unsigned int y, unsigned int z, unsigned int val) { int b, o; if (ap->memstart) { MemInit(ap); } BASE_OFFSET(x, y, z, b, o); ap->base[b][o] += val; return True; } #endif static void End3D(antfarm3dstruct * ap) { antstruct *ptr; while (ap->ptrhead != NULL) { /* Reset3D(ap->ptrhead->column, ap->ptrhead->row, ap->ptrhead->stack); */ DelFromList(ap, ap->ptrhead); } ptr = ap->eraserhead.next; while (ptr != &ap->eraserend) { DelFromEraseList(ptr); ptr = ap->eraserhead.next; } MemInit(ap); } static Bool RunAnt3D(antfarm3dstruct * ap) { antstruct *anant; statestruct *status; int i, state_pos, tape_pos; unsigned char color; if (ap->ants == NULL) return False; ap->painted = True; for (i = 0; i < ap->n; i++) { anant = &ap->ants[i]; tape_pos = anant->column + anant->row * ap->ncolumns + anant->stack * ap->nrows * ap->ncolumns; color = ap->tape[tape_pos]; /* read tape */ state_pos = color + anant->state * ap->ncolors; status = &(ap->machine[state_pos]); #ifdef DEBUG (void) printf("status: color %d, turn %s, state %d, colors %d\n", status->color, printTurn(status->turn), anant->state, ap->ncolors); #endif SetList3D(anant->column, anant->row, anant->stack, status->color); ap->tape[tape_pos] = status->color; /* write on tape */ #ifdef DEBUG (void) printf("gen %d: ", ap->generation); #endif heading(&(anant->direction), &(anant->gravity), status->turn); /* Find direction of Bees or Ants. */ /* Translate relative direction to actual direction */ /*chg_dir = (2 * ANGLES - status->direction) % ANGLES; anant->direction = (chg_dir + old_dir) % ANGLES; */ anant->state = status->next; /* Allow step first then turn */ /*old_dir = ((status->direction < ANGLES) ? anant->direction : old_dir);*/ #ifdef DEBUG (void) printf("state %d, column %d, row %d, stack %d", anant->state, anant->column, anant->row, anant->stack); #endif position_of_neighbor(ap, anant->direction, &(anant->column), &(anant->row), &(anant->stack)); SetList3D(anant->column, anant->row, anant->stack, NCOLORS); /*draw_anant(mi, anant->direction, anant->column, anant->row, anant->stack);*/ } return True; } #if 0 static int CountCells3D(antfarm3dstruct * ap) { antstruct *ptr; int count = 0; ptr = ap->ptrhead; while (ptr != NULL) { ++count; ptr = ptr->next; } return count; } void DisplayList(antfarm3dstruct * ap) { antstruct *ptr; int count = 0; ptr = ap->ptrhead; while (ptr != NULL) { (void) printf("(%x)=[%d,%d,%d] ", (int) ptr, ptr->x, ptr->y, ptr->z); ptr = ptr->next; ++count; } (void) printf("Living cells = %d\n", count); } #endif static void getTable(ModeInfo * mi, int i) { antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; int j, total; unsigned char *patptr; statestruct *status; patptr = &tables[i][0]; ap->ncolors = *patptr++; ap->nstates = *patptr++; total = ap->ncolors * ap->nstates; if (MI_IS_VERBOSE(mi)) (void) fprintf(stdout, "ants %d, table number %d, colors %d, states %d\n", ap->n, i, ap->ncolors, ap->nstates); if (label) (void) sprintf(ap->ruleString, "table number %d", i); for (j = 0; j < total; j++) { status = &(ap->machine[j]); status->color = *patptr++; status->turn = *patptr++; status->next = *patptr++; #ifdef DEBUG (void) printf("Status: color %d, turn %d, colors %d\n", status->color, status->turn, ap->ncolors); #endif } } static void getTurk(ModeInfo * mi, int logClass, int number) { antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; int power4, j, total; power4 = 1 << (2 * logClass); ap->ncolors = logClass; ap->nstates = 1; total = ap->ncolors * ap->nstates; for (j = 0; j < total; j++) { power4 >>= 2; ap->machine[j].color = (j + 1) % total; ap->machine[j].turn = ((power4 * 3) & number) / power4; #ifdef DEBUG (void) printf("turn %d\n", ap->machine[j].turn); #endif ap->machine[j].next = 0; } if (MI_IS_VERBOSE(mi)) (void) fprintf(stdout, "ants %d, Turk's number 0x%x, colors %d\n", ap->n, number, ap->ncolors); if (label) (void) sprintf(ap->ruleString, "Turk's number 0x%x", number); } static void parseRule(ModeInfo * mi) { if (rule) { int n = 0, l, number = 0, logClass = 0, power4; int bucket[4]; bucket[0] = bucket[1] = bucket[2] = bucket[3] = 0; while (rule[n]) { if (rule[n] == 'T' || rule[n] == 't') { n++; while (rule[n]) { number = number * 10 + (int) (rule[n] - '0'); n++; } getTable(mi, number); return; } if (rule[n] == 'A' || rule[n] == 'a') { if (!NRAND(NUMSTIPPLES)) { getTable(mi, (int) (NRAND(NTABLES))); return; } else { int mask = 3, shiftNumber, j; logClass = (int) (NRAND(NUMSTIPPLES - 4)) + 4; /* has 4 quadranary digits */ power4 = 1 << (2 * (logClass - 1)); do { bucket[0] = bucket[1] = bucket[2] = bucket[3] = 0; number = NRAND(power4); #ifdef DEBUG (void) printf("number 0x%x, power4 0x%x, logClass %d\n", number, power4, logClass); #endif /* Want to make sure 3 of 4 quadranary digits are here */ shiftNumber = number; for (j = 0; j < logClass - 1; j++) { bucket[mask & shiftNumber]++; shiftNumber = shiftNumber >> 2; } #ifdef DEBUG (void) printf("Buckets: %d %d %d %d\n", bucket[0], bucket[1], bucket[2], bucket[3]); #endif } while ((!bucket[0] && !bucket[1]) || (!bucket[0] && !bucket[2]) || (!bucket[0] && !bucket[3]) || (!bucket[1] && !bucket[2]) || (!bucket[1] && !bucket[3]) || (!bucket[2] && !bucket[3])); getTurk(mi, logClass, number); return; } } else { l = rule[n] - '0'; if (l >= 0 && l <= 3) { number = (number << 2) + l; if (logClass > 0 || l != 0) logClass++; bucket[l]++; } } n++; } #ifdef DEBUG (void) printf("Buckets: %d %d %d %d\n", bucket[0], bucket[1], bucket[2], bucket[3]); #endif if ((!bucket[0] && !bucket[1]) || (!bucket[0] && !bucket[2]) || (!bucket[0] && !bucket[3]) || (!bucket[1] && !bucket[2]) || (!bucket[1] && !bucket[3]) || (!bucket[2] && !bucket[3])) { switch (NRAND(3)) { case 0: getTurk(mi, 3, 0x21); break; case 1: getTurk(mi, 3, 0x27); break; default: getTurk(mi, 3, 0x2d); break; } } else { getTurk(mi, logClass, number); } } } static void NewViewpoint(antfarm3dstruct * ap, double x, double y, double z) { double k, l, d1, d2; k = x * x + y * y; l = sqrt(k + z * z); k = sqrt(k); d1 = (EyeToScreen / HalfScreenD); d2 = EyeToScreen / (HalfScreenD * ap->height / ap->width); ap->A = d1 * l * (ap->width / 2) / k; ap->B = l * l; ap->C = d2 * (ap->height / 2) / k; ap->F = k * k; } static void lissajous(antfarm3dstruct * ap) { double alt, azm, dist; alt = 44.0 * sin(ap->metaAlt * IP) + 45.0; ap->metaAlt += 1.123; if (ap->metaAlt >= 360.0) ap->metaAlt -= 360.0; if (ap->metaAlt < 0.0) ap->metaAlt += 360.0; azm = 44.0 * sin(ap->metaAzm * IP) + 45.0; ap->metaAzm += 0.987; if (ap->metaAzm >= 360.0) ap->metaAzm -= 360.0; if (ap->metaAzm < 0.0) ap->metaAzm += 360.0; dist = 10.0 * sin(ap->metaDist * IP) + 50.0; ap->metaDist += 1.0; if (ap->metaDist >= 360.0) ap->metaDist -= 360.0; if (ap->metaDist < 0.0) ap->metaDist += 360.0; #if 0 if (alt >= 90.0) alt = 90.0; else if (alt < -90.0) alt = -90.0; #endif ap->azm = azm; #ifdef DEBUG (void) printf("dist %g, alt %g, azm %g\n", dist, alt, azm); #endif ap->vx = (sin(azm * IP) * cos(alt * IP) * dist); ap->vy = (cos(azm * IP) * cos(alt * IP) * dist); ap->vz = (sin(alt * IP) * dist); NewViewpoint(ap, ap->vx, ap->vy, ap->vz); } static void NewPoint(antfarm3dstruct * ap, double x, double y, double z, register XPoint * cubepts) { double p1, E; p1 = x * ap->vx + y * ap->vy; E = ap->B - p1 - z * ap->vz; cubepts->x = (int) (ap->width / 2 - ap->A * (ap->vx * y - ap->vy * x) / E); cubepts->y = (int) (ap->height / 2 - ap->C * (z * ap->F - ap->vz * p1) / E); } /* Chain together all cells that are at the same distance. * These cannot mutually overlap. */ static void SortList(antfarm3dstruct * ap) { short dist; double d, column, row, stack, rsize; int i, r; XPoint point; antstruct *ptr; for (i = 0; i < NBUCKETS; ++i) ap->buckethead[i] = ap->bucketend[i] = NULL; /* Calculate distances and re-arrange pointers to chain off buckets */ ptr = ap->ptrhead; while (ptr != NULL) { column = (double) ptr->column - ap->ox; row = (double) ptr->row - ap->oy; stack = (double) ptr->stack - ap->oz; d = Distance(ap->vx, ap->vy, ap->vz, column, row, stack); if (ap->vx * (ap->vx - column) + ap->vy * (ap->vy - row) + ap->vz * (ap->vz - stack) > 0 && d > 1.5) ptr->visible = 1; else ptr->visible = 0; ptr->dist = (short) d; dist = (short) (d * BUCKETSIZE); if (dist > NBUCKETS - 1) dist = NBUCKETS - 1; if (ap->buckethead[dist] == NULL) { ap->buckethead[dist] = ap->bucketend[dist] = ptr; ptr->priority = NULL; } else { ap->bucketend[dist]->priority = ptr; ap->bucketend[dist] = ptr; ap->bucketend[dist]->priority = NULL; } ptr = ptr->next; } /* Check for invisibility */ rsize = 0.47 * ap->width / ((double) HalfScreenD * 2); i = (int) ap->azm; if (i < 0) i = -i; i = i % RT_ANGLE; if (i > HALFRT_ANGLE) i = RT_ANGLE - i; rsize /= cos(i * IP); for (i = 0; i < NBUCKETS; ++i) if (ap->buckethead[i] != NULL) { ptr = ap->buckethead[i]; while (ptr != NULL) { if (ptr->visible) { column = (double) ptr->column - ap->ox; row = (double) ptr->row - ap->oy; stack = (double) ptr->stack - ap->oz; NewPoint(ap, column, row, stack, &point); r = (int) (rsize * (double) EyeToScreen / (double) ptr->dist); if (point.x + r < 0 || point.y + r < 0 || point.x - r >= ap->width || point.y - r >= ap->height) { ap->nInvisible++; } else if (ap->nInvisible) { ap->nInvisible = 0; } } ptr = ptr->priority; } } } static void DrawFace(ModeInfo * mi, int color, XPoint * cubepts, int p1, int p2, int p3, int p4) { Display *display = MI_DISPLAY(mi); GC gc; antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; XPoint facepts[5]; facepts[0] = cubepts[p1]; facepts[1] = cubepts[p2]; facepts[2] = cubepts[p3]; facepts[3] = cubepts[p4]; facepts[4] = cubepts[p1]; if (color == NCOLORS) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); gc = MI_GC(mi); } else if (!color) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); gc = MI_GC(mi); } else if (MI_NPIXELS(mi) > 2) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, ap->colors[color - 1])); gc = MI_GC(mi); } else { XGCValues gcv; gcv.stipple = ap->pixmaps[(color - 1) % NUMSTIPPLES]; gcv.foreground = MI_WHITE_PIXEL(mi); gcv.background = MI_BLACK_PIXEL(mi); XChangeGC(MI_DISPLAY(mi), ap->stippledGC, GCStipple | GCForeground | GCBackground, &gcv); gc = ap->stippledGC; } if (!ap->wireframe) XFillPolygon(display, (Drawable) ap->dbuf, gc, facepts, 4, Convex, CoordModeOrigin); gc = MI_GC(mi); if (!ap->wireframe) { if (color == NCOLORS) { XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); } else { XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); } } XDrawLines(display, (Drawable) ap->dbuf, gc, facepts, 5, CoordModeOrigin); } static void DrawEyes(ModeInfo * mi, int color, XPoint * cubepts, int p1, int p2, int p3, int p4) { Display *display = MI_DISPLAY(mi); GC gc; antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; XPoint facepts[5]; XPoint av; av.x = (cubepts[p1].x + cubepts[p2].x + cubepts[p3].x + cubepts[p4].x) / 4; av.y = (cubepts[p1].y + cubepts[p2].y + cubepts[p3].y + cubepts[p4].y) / 4; facepts[0] = cubepts[p1]; facepts[1] = cubepts[p2]; facepts[2] = cubepts[p3]; facepts[3] = cubepts[p4]; facepts[4] = cubepts[p1]; facepts[0].x = (av.x + facepts[0].x) / 2; facepts[0].y = (av.y + facepts[0].y) / 2; facepts[1].x = (av.x + facepts[1].x) / 2; facepts[1].y = (av.y + facepts[1].y) / 2; facepts[2].x = (av.x + facepts[2].x) / 2; facepts[2].y = (av.y + facepts[2].y) / 2; facepts[3].x = (av.x + facepts[3].x) / 2; facepts[3].y = (av.y + facepts[3].y) / 2; facepts[4].x = (av.x + facepts[4].x) / 2; facepts[4].y = (av.y + facepts[4].y) / 2; if (color == NCOLORS) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); gc = MI_GC(mi); } else if (!color) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); gc = MI_GC(mi); } else if (MI_NPIXELS(mi) > 2) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, ap->colors[color - 1])); gc = MI_GC(mi); } else { XGCValues gcv; gcv.stipple = ap->pixmaps[(color - 1) % NUMSTIPPLES]; gcv.foreground = MI_WHITE_PIXEL(mi); gcv.background = MI_BLACK_PIXEL(mi); XChangeGC(MI_DISPLAY(mi), ap->stippledGC, GCStipple | GCForeground | GCBackground, &gcv); gc = ap->stippledGC; } if (!ap->wireframe) XFillPolygon(display, (Drawable) ap->dbuf, gc, facepts, 4, Convex, CoordModeOrigin); gc = MI_GC(mi); if (!ap->wireframe) { if (color == NCOLORS) { XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); } else { XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); } } XDrawLines(display, (Drawable) ap->dbuf, gc, facepts, 5, CoordModeOrigin); } #define LEN 0.45 #define LEN2 0.9 static int DrawCube(ModeInfo * mi, antstruct * cell) { antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; XPoint cubepts[8]; /* screen coords for point */ int i, out; unsigned int mask; double column, row, stack; double dx, dy, dz; int color; column = (double) cell->column - ap->ox; row = (double) cell->row - ap->oy; stack = (double) cell->stack - ap->oz; color = cell->color; out = 0; i = 0; for (dz = stack - LEN; dz <= stack + LEN2; dz += LEN2) for (dy = row - LEN; dy <= row + LEN2; dy += LEN2) for (dx = column - LEN; dx <= column + LEN2; dx += LEN2) { NewPoint(ap, dx, dy, dz, &cubepts[i]); if (cubepts[i].x < 0 || cubepts[i].x >= ap->width || cubepts[i].y < 0 || cubepts[i].y >= ap->height) ++out; ++i; } if (out == 8) return (0); if (cell->visible) mask = 0xFFFF; else mask = 0x0; /* Only draw those faces that are visible */ dx = ap->vx - column; dy = ap->vy - row; dz = ap->vz - stack; if (ap->wireframe) { if (dz <= LEN) DrawFace(mi, (int) (color & mask), cubepts, 4, 5, 7, 6); else if (dz >= -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 1, 3, 2); if (dx <= LEN) DrawFace(mi, (int) (color & mask), cubepts, 1, 3, 7, 5); else if (dx >= -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 2, 6, 4); if (dy <= LEN) DrawFace(mi, (int) (color & mask), cubepts, 2, 3, 7, 6); else if (dy >= -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 1, 5, 4); } if (dz > LEN) DrawFace(mi, (int) (color & mask), cubepts, 4, 5, 7, 6); else if (dz < -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 1, 3, 2); if (dx > LEN) DrawFace(mi, (int) (color & mask), cubepts, 1, 3, 7, 5); else if (dx < -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 2, 6, 4); if (dy > LEN) DrawFace(mi, (int) (color & mask), cubepts, 2, 3, 7, 6); else if (dy < -LEN) DrawFace(mi, (int) (color & mask), cubepts, 0, 1, 5, 4); if (ap->eyes && color == NCOLORS) { antstruct *anant; for (i = 0; i < ap->n; i++) { anant = &ap->ants[i]; if (anant->column == cell->column && anant->row == cell->row && anant->stack == cell->stack) { #if 0 (void) printf("%d: direction %d, gravity %d\n", i, anant->direction, anant->gravity); #endif /* TODO: use gravity to position better eyes */ if (dz > LEN && anant->direction == ZP) DrawEyes(mi, 0, cubepts, 4, 5, 7, 6); else if (dz < -LEN && anant->direction == ZN) DrawEyes(mi, 0, cubepts, 0, 1, 3, 2); if (dx > LEN && anant->direction == XP) DrawEyes(mi, 0, cubepts, 1, 3, 7, 5); else if (dx < -LEN && anant->direction == XN) DrawEyes(mi, 0, cubepts, 0, 2, 6, 4); if (dy > LEN && anant->direction == YP) DrawEyes(mi, 0, cubepts, 2, 3, 7, 6); else if (dy < -LEN && anant->direction == YN) DrawEyes(mi, 0, cubepts, 0, 1, 5, 4); } } } return (1); } static void DrawScreen(ModeInfo * mi) { antfarm3dstruct *ap = &antfarm3ds[MI_SCREEN(mi)]; Display *display = MI_DISPLAY(mi); GC gc = MI_GC(mi); antstruct *ptr; antstruct *eraserptr; int i; SortList(ap); XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); XFillRectangle(display, (Drawable) ap->dbuf, gc, 0, 0, ap->width, ap->height); /* Erase dead cubes */ eraserptr = ap->eraserhead.next; while (eraserptr != &ap->eraserend) { eraserptr->visible = 0; (void) DrawCube(mi, eraserptr); DelFromEraseList(eraserptr); eraserptr = ap->eraserhead.next; } /* draw furthest cubes first */ for (i = NBUCKETS - 1; i >= 0; --i) { ptr = ap->buckethead[i]; while (ptr != NULL) { /*if (ptr->visible) */ /* v += */ (void) DrawCube(mi, ptr); ptr = ptr->priority; /* ++count; */ } } if (label) { int size = MAX(MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) - 1, 1); if (size >= 10 * FONT_WIDTH) { /* hard code these to corners */ XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); /*XDrawString(display, ap->dbuf, gc, 16 + ap->labelOffsetX, 16 + ap->labelOffsetY + FONT_HEIGHT, ap->ruleString, strlen(ap->ruleString)); */ XDrawString(display, (Drawable) ap->dbuf, gc, 16 + ap->labelOffsetX, MI_HEIGHT(mi) - 16 - ap->labelOffsetY - FONT_HEIGHT / 2, ap->ruleString, strlen(ap->ruleString)); } } XFlush(display); XCopyArea(display, (Drawable) ap->dbuf, MI_WINDOW(mi), gc, 0, 0, ap->width, ap->height, 0, 0); #if 0 { int count = 0, v = 0; (void) printf("Pop=%-4d Viewpoint (%3d,%3d,%3d) Origin (%3d,%3d,%3d) Mode %dx%d\ (%d,%d) %d\n", count, (int) (ap->vx + ap->ox), (int) (ap->vy + ap->oy), (int) (ap->vz + ap->oz), (int) ap->ox, (int) ap->oy, (int) ap->oz, ap->width, ap->height, ap->alt, ap->azm, v); } #endif } static void free_ant3d(Display *display, antfarm3dstruct *ap) { if (ap->eraserhead.next != NULL) { End3D(ap); } if (ap->dbuf != None) { XFreePixmap(display, ap->dbuf); ap->dbuf = None; } if (ap->tape != NULL) { free(ap->tape); ap->tape = (unsigned char *) NULL; } if (ap->ants != NULL) { free(ap->ants); ap->ants = (antstruct *) NULL; } } void init_ant3d(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); antfarm3dstruct *ap; int i; if (antfarm3ds == NULL) { if ((antfarm3ds = (antfarm3dstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (antfarm3dstruct))) == NULL) return; } ap = &antfarm3ds[MI_SCREEN(mi)]; ap->nInvisible = 0; if (MI_NPIXELS(mi) <= 2) { if (ap->stippledGC == None) { XGCValues gcv; gcv.fill_style = FillOpaqueStippled; if ((ap->stippledGC = XCreateGC(display, window, GCFillStyle, &gcv)) == None) { free_ant3d(display, ap); return; } } if (ap->init_bits == 0) { for (i = 1; i < NUMSTIPPLES; i++) { ANT3DBITS(stipples[i], STIPPLESIZE, STIPPLESIZE); } } } ap->generation = 0; ap->n = MI_COUNT(mi); if (ap->n < -MINANTS) { /* if ap->n is random ... the size can change */ if (ap->ants != NULL) { free(ap->ants); ap->ants = NULL; } ap->n = NRAND(-ap->n - MINANTS + 1) + MINANTS; } else if (ap->n < MINANTS) ap->n = MINANTS; if (!ap->eraserhead.next) { ap->metaDist = (double) NRAND(360); ap->metaAlt = (double) NRAND(360); ap->metaAzm = (double) NRAND(360); ap->ncolumns = MAXCOLUMNS; ap->nrows = MAXROWS; ap->nstacks = MAXSTACKS; ap->ox = ap->ncolumns / 2; ap->oy = ap->nrows / 2; ap->oz = ap->nstacks / 2; Init3D(ap); } else { End3D(ap); } ap->labelOffsetX = NRAND(8); ap->labelOffsetY = NRAND(8); parseRule(mi); if (MI_NPIXELS(mi) > 2) for (i = 0; i < (int) ap->ncolors - 1; i++) ap->colors[i] = (unsigned char) (NRAND(MI_NPIXELS(mi)) + i * MI_NPIXELS(mi)) / ((int) (ap->ncolors - 1)); if (ap->tape != NULL) free(ap->tape); if ((ap->tape = (unsigned char *) calloc( ap->ncolumns * ap->nrows * ap->nstacks, sizeof (unsigned char))) == NULL) { free_ant3d(display, ap); return; } ap->width = MI_WIDTH(mi); ap->height = MI_HEIGHT(mi); ap->memstart = 1; /*ap->tablesMade = 0; */ if (MI_IS_FULLRANDOM(mi)) { ap->wireframe = (NRAND(8) == 0); } else { ap->wireframe = MI_IS_WIREFRAME(mi); } MI_CLEARWINDOW(mi); ap->painted = False; lissajous(ap); if (ap->ants == NULL) { if ((ap->ants = (antstruct *) malloc(ap->n * sizeof (antstruct))) == NULL) { free_ant3d(display, ap); return; } } for (i = 0; i < ap->n; i++) { ap->ants[i].row = ap->nrows / 2; ap->ants[i].column = ap->ncolumns / 2; ap->ants[i].stack = ap->nstacks / 2; ap->ants[i].direction = NRAND(6); ap->ants[i].gravity = NRAND(6); while ((ap->ants[i].gravity / 2) % 3 == (ap->ants[i].direction / 2) % 3) ap->ants[i].gravity = NRAND(6); ap->ants[i].state = 0; } if (ap->dbuf != None) { XFreePixmap(display, ap->dbuf); ap->dbuf = None; } if ((ap->dbuf = XCreatePixmap(display, window, ap->width, ap->height, MI_DEPTH(mi))) == None) { free_ant3d(display, ap); return; } if (MI_IS_FULLRANDOM(mi)) { ap->eyes = (Bool) (LRAND() & 1); } else { ap->eyes = eyes; } DrawScreen(mi); } void draw_ant3d(ModeInfo * mi) { antfarm3dstruct *ap; if (antfarm3ds == NULL) return; ap = &antfarm3ds[MI_SCREEN(mi)]; if (ap->eraserhead.next == NULL) return; if (!RunAnt3D(ap)) { if (ap->eraserhead.next != NULL) End3D(ap); return; } lissajous(ap); DrawScreen(mi); MI_IS_DRAWN(mi) = True; if (++ap->generation > MI_CYCLES(mi) || ap->nInvisible > 32) { /*CountCells3D(ap) == 0) */ init_ant3d(mi); } else ap->painted = True; } void release_ant3d(ModeInfo * mi) { if (antfarm3ds != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_ant3d(MI_DISPLAY(mi), &antfarm3ds[screen]); free(antfarm3ds); antfarm3ds = (antfarm3dstruct *) NULL; } } void refresh_ant3d(ModeInfo * mi) { antfarm3dstruct *ap; if (antfarm3ds == NULL) return; ap = &antfarm3ds[MI_SCREEN(mi)]; if (ap->painted) { MI_CLEARWINDOW(mi); } } #endif /* MODE_ant3d */