/* -*- Mode: C; tab-width: 4 -*- */ /* bug --- Michael Palmiter's simulated evolution */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)bug.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1995 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: * 01-Nov-2000: Allocation checks * 10-May-1997: Compatible with xscreensaver * 24-Aug-1995: Coded from A.K. Dewdney's, "Computer Recreations", Scientific * American Magazine" May 1989 pp138-141 and Sept 1989 p 183. * also used wator.c as a guide. */ /*- * Bugs learn to hunt bacteria (or die) in the Garden of Eden and outside. * They start as jitterbugs and "evolve" to move straight (in the Garden * they may evolve to twirl around). */ #ifdef STANDALONE #define MODE_bug #define PROGCLASS "Bug" #define HACK_INIT init_bug #define HACK_DRAW draw_bug #define bug_opts xlockmore_opts #define DEFAULTS "*delay: 75000 \n" \ "*count: 10 \n" \ "*cycles: 32767 \n" \ "*size: -4 \n" \ "*ncolors: 64 \n" #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #include "automata.h" #ifdef MODE_bug #define DEF_NEIGHBORS "0" /* choose random value */ #define DEF_EYES "False" static int neighbors; static Bool eyes; static XrmOptionDescRec opts[] = { {(char *) "-neighbors", (char *) ".bug.neighbors", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-eyes", (char *) ".bug.eyes", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+eyes", (char *) ".bug.eyes", XrmoptionNoArg, (caddr_t) "off"}, }; static argtype vars[] = { {(void *) & neighbors, (char *) "neighbors", (char *) "Neighbors", (char *) DEF_NEIGHBORS, t_Int}, {(void *) & eyes, (char *) "eyes", (char *) "Eyes", (char *) DEF_EYES, t_Bool}, }; static OptionStruct desc[] = { {(char *) "-neighbors num", (char *) "squares 4 or 8, hexagons 6"}, {(char *) "-/+eyes", (char *) "turn on/off eyes"} }; ModeSpecOpt bug_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct bug_description = {"bug", "init_bug", "draw_bug", "release_bug", "refresh_bug", "init_bug", (char *) NULL, &bug_opts, 75000, 10, 32767, -4, 64, 1.0, "", "Shows Palmiter's bug evolution and garden of Eden", 0, NULL}; #endif #ifdef DEBUG #include #endif #define BUGBITS(n,w,h)\ if ((bp->pixmaps[bp->init_bits]=\ XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\ free_bug(display,bp); return;} else {bp->init_bits++;} #define BACTERIA 0 #define BUG 1 #define MAXENERGY 1500 /* Max energy storage */ #define INITENERGY 400 /* Initial energy */ #define FOODPERCYCLE 1 #define BACTERIAENERGY 40 #define STRONG 1000 /* Min energy to breed */ #define MATURE 800 /* Breeding time of bugs */ #define MAXGENE 6 #define MINGENE (-MAXGENE) #define REDRAWSTEP 2000 /* How many bacteria to draw per cycle */ #define MINGRIDSIZE 24 #define MINSIZE 1 #define ANGLES 360 #define MAXNEIGHBORS 12 /* Relative hex bug moves */ /* Forward, Right, Hard Right, Reverse, Hard Left, Left */ /* Bug data */ typedef struct { char direction; int age, energy; unsigned int color; int col, row; int gene[MAXNEIGHBORS]; double gene_prob[MAXNEIGHBORS]; #if EXTRA_GENES int lgene[MAXNEIGHBORS]; double lgene_prob[MAXNEIGHBORS]; #endif } bugstruct; /* Doublely linked list */ typedef struct _BugList { bugstruct info; struct _BugList *previous, *next; } BugList; typedef struct { Bool painted; int neighbors; int nbugs; /* Number of bugs */ int eden; /* Does the garden exist? */ int generation; int ncols, nrows; int width, height; int edenwidth, edenheight; int edenstartx, edenstarty; int xs, ys, xb, yb; int redrawing, redrawpos; int eyes; BugList *currbug, *babybug, *lastbug, *firstbug, *lasttemp, *firsttemp; BugList **arr; /* 0=empty or pts to a bug */ char *bacteria; /* 0=empty or food */ int init_bits; GC stippledGC; Pixmap pixmaps[NUMSTIPPLES - 1]; union { XPoint hexagon[6]; XPoint triangle[2][3]; } shape; } bugfarmstruct; static double genexp[MAXGENE - MINGENE + 1]; static char plots[] = {3, 4, 6, 8, #ifdef NUMBER_9 9, #endif 12}; /* Neighborhoods */ #define NEIGHBORKINDS (long) (sizeof plots / sizeof *plots) static bugfarmstruct *bugfarms = (bugfarmstruct *) NULL; #if 0 static void position_of_neighbor(bugfarmstruct * bp, int dir, int *pcol, int *prow) { int col = *pcol, row = *prow; /* NO WRAPING */ if (bp->neighbors == 4 || bp->neighbors == 6 || bp->neighbors == 8) { switch (dir) { case 0: col = col + 1; break; case 45: col = col + 1; row = row - 1; break; case 60: if (!(row & 1)) col = col + 1; row = row - 1; break; case 90: row = row - 1; break; case 120: if (row & 1) col = col - 1; row = row - 1; break; case 135: col = col - 1; row = row - 1; break; case 180: col = col - 1; break; case 225: col = col - 1; row = row + 1; break; case 240: if (row & 1) col = col - 1; row = row + 1; break; case 270: row = row + 1; break; case 300: if (!(row & 1)) col = col + 1; row = row + 1; break; case 315: col = col + 1; row = row + 1; break; default: (void) fprintf(stderr, "wrong direction %d\n", dir); } } else { /* TRI */ switch (dir) { case 0: col = col + 1; break; case 20: case 30: case 40: col = col + 1; row = row - 1; break; case 60: if ((col + row) % 2) { /* right */ row = row - 1; } else { /* left */ col = col + 1; row = row - 2; } break; case 80: case 90: case 100: row = row - 2; break; case 120: if ((col + row) % 2) { /* right */ col = col - 1; row = row - 2; } else { /* left */ row = row - 1; } break; case 140: case 150: case 160: col = col - 1; row = row - 1; break; case 180: col = col - 1; break; case 200: case 210: case 220: col = col - 1; row = row + 1; break; case 240: if ((col + row) % 2) { /* right */ col = col - 1; row = row + 2; } else { /* left */ row = row + 1; } break; case 260: case 270: case 280: row = row + 2; break; case 300: if ((col + row) % 2) { /* right */ row = row + 1; } else { /* left */ col = col + 1; row = row + 2; } break; case 320: case 330: case 340: col = col + 1; row = row + 1; break; default: (void) fprintf(stderr, "wrong direction %d\n", dir); } } *pcol = col; *prow = row; } #endif #if 0 static void screen2grid(bugfarmstruct *bp, int sx, int sy, int x, int y) { *y = sy / 2; if (bp->neighbors == 6) { *x = (sx + 1) / 2 - 1; } else if (bp->neighbors == 4 || bp->neighbors == 8) { *x = sx / 2; } else { /* TRI */ /* Wrong but... */ *x = sx / 3; } } #endif static void grid2screen(bugfarmstruct *bp, int x, int y, int *sx, int *sy) { *sy = y * 2 + 1; if (bp->neighbors == 6) { *sx = 2 * x + 1 + !(y & 1); } else if (bp->neighbors == 4 || bp->neighbors == 8) { *sx = x * 2 + 1; } else { /* TRI */ *sx = x * 3 + 1 + ((y & 1) ? (x & 1) : !(x & 1)); } } static char dirmove(bugfarmstruct * bp, int x, int y, int dir, int *nx, int *ny) { if (bp->neighbors == 6) { switch (dir) { case 0: *ny = y; *nx = x + 1; return (*nx < bp->ncols - !(*ny & 1)); case 60: *ny = y - 1; *nx = x + (int) !(y & 1); return (*ny >= 0 && *nx < bp->ncols - !(*ny & 1)); case 120: *ny = y - 1; *nx = x - (int) (y & 1); return (*ny >= 0 && *nx >= 0); case 180: *ny = y; *nx = x - 1; return (*nx >= 0); case 240: *ny = y + 1; *nx = x - (int) (y & 1); return (*ny < bp->nrows && *nx >= 0); case 300: *ny = y + 1; *nx = x + (int) !(y & 1); return (*ny < bp->nrows && *nx < bp->ncols - !(*ny & 1)); default: (void) fprintf(stderr, "wrong direction %d\n", dir); } } else if (bp->neighbors == 4 || bp->neighbors == 8) { switch (dir) { case 0: *ny = y; *nx = x + 1; return (*nx < bp->ncols); case 45: *ny = y - 1; *nx = x + 1; return (*ny >= 0 && *nx < bp->ncols); case 90: *ny = y - 1; *nx = x; return (*ny >= 0); case 135: *ny = y - 1; *nx = x - 1; return (*ny >= 0 && *nx >= 0); case 180: *ny = y; *nx = x - 1; return (*nx >= 0); case 225: *ny = y + 1; *nx = x - 1; return (*ny < bp->nrows && *nx >= 0); case 270: *ny = y + 1; *nx = x; return (*ny < bp->nrows); case 315: *ny = y + 1; *nx = x + 1; return (*ny < bp->nrows && *nx < bp->ncols); default: (void) fprintf(stderr, "wrong direction %d\n", dir); } } else { /* TRI */ switch (dir) { case 0: *nx = x + 1; *ny = y; return (*nx < bp->ncols); case 20: case 30: case 40: *nx = x + 1; *ny = y - 1; return (*nx < bp->ncols && *ny >= 0); case 60: if ((x + y) % 2) { /* right */ *nx = x; *ny = y - 1; return (*ny >= 0); } else { /* left */ *nx = x + 1; *ny = y - 2; return (*nx < bp->ncols && *ny >= 0); } case 80: case 90: case 100: *nx = x; *ny = y - 2; return (*ny >= 0); case 120: if ((x + y) % 2) { /* right */ *nx = x - 1; *ny = y - 2; return (*nx >= 0 && *ny >= 0); } else { /* left */ *nx = x; *ny = y - 1; return (*ny >= 0); } case 140: case 150: case 160: *nx = x - 1; *ny = y - 1; return (*nx >= 0 && *ny >= 0); case 180: *nx = x - 1; *ny = y; return (*nx >= 0); case 200: case 210: case 220: *nx = x - 1; *ny = y + 1; return (*nx >= 0 && *ny < bp->nrows); case 240: if ((x + y) % 2) { /* right */ *nx = x - 1; *ny = y + 2; return (*nx >= 0 && *ny < bp->nrows); } else { /* left */ *nx = x; *ny = y + 1; return (*ny < bp->nrows); } case 260: case 270: case 280: *nx = x; *ny = y + 2; return (*ny < bp->nrows); case 300: if ((x + y) % 2) { /* right */ *nx = x; *ny = y + 1; return (*ny < bp->nrows); } else { /* left */ *nx = x + 1; *ny = y + 2; return (*nx < bp->ncols && *ny < bp->nrows); } case 320: case 330: case 340: *nx = x + 1; *ny = y + 1; return (*nx < bp->ncols && *ny < bp->nrows); default: (void) fprintf(stderr, "wrong direction %d\n", dir); } } *nx = -1; *ny = -1; return 0; } /* This keeps bugs from walking over each other and escaping off the screen */ static int has_neighbor(bugfarmstruct * bp, int col, int row) { int colrow = col + row * bp->ncols; if (bp->neighbors == 6) { if ((row > 0) && bp->arr[colrow - bp->ncols]) return True; if ((row < bp->nrows - 1) && bp->arr[colrow + bp->ncols]) return True; if (col > 0) { if (bp->arr[colrow - 1]) return True; if (row & 1) { if ((row > 0) && bp->arr[colrow - 1 - bp->ncols]) return True; if ((row < bp->nrows - 1) && bp->arr[colrow - 1 + bp->ncols]) return True; } } if (col < bp->ncols - 1) { if (bp->arr[colrow + 1]) return True; if (!(row & 1)) { if ((row > 0) && bp->arr[colrow + 1 - bp->ncols]) return True; if ((row < bp->nrows - 1) && bp->arr[colrow + 1 + bp->ncols]) return True; } } } else if (bp->neighbors == 4 || bp->neighbors == 8) { /* Same for both because the bug square takes up the space regardless */ if ((row > 0) && bp->arr[colrow - bp->ncols]) return True; if ((row < bp->nrows - 1) && bp->arr[colrow + bp->ncols]) return True; if ((col > 0) && bp->arr[colrow - 1]) return True; if ((col < bp->ncols - 1) && bp->arr[colrow + 1]) return True; if (row > 0 && col > 0 && bp->arr[colrow - bp->ncols - 1]) return True; if (row > 0 && col < bp->ncols - 1 && bp->arr[colrow - bp->ncols + 1]) return True; if (row < bp->nrows - 1 && col > 0 && bp->arr[colrow + bp->ncols - 1]) return True; if (row < bp->nrows - 1 && col < bp->ncols - 1 && bp->arr[colrow + bp->ncols + 1]) return True; } else { /* TRI */ /* Same for all three neighbor kinds. Only care about 3 adjacent spots because that is all the squares will overlap.*/ if (((row & 1) && (col & 1)) || ((!(row & 1)) && (!(col & 1)))) { if ((col < bp->ncols - 1) && bp->arr[colrow + 1]) return True; } else { if ((col > 0) && bp->arr[colrow - 1]) return True; } if ((row > 0) && bp->arr[colrow - bp->ncols]) return True; if ((row < bp->nrows - 1) && bp->arr[colrow + bp->ncols]) return True; } return False; } static void drawabacterium(ModeInfo * mi, int col, int row, unsigned char color) { bugfarmstruct *bp = &bugfarms[MI_SCREEN(mi)]; int ccol, crow; if (color) XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); else XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); grid2screen(bp, col, row, &ccol, &crow); XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), bp->xb + bp->xs * ccol, bp->yb + bp->ys * crow, bp->xs, bp->ys); } static void drawabug(ModeInfo * mi, int col, int row, unsigned int color, int direction) { bugfarmstruct *bp = &bugfarms[MI_SCREEN(mi)]; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int ccol, crow; GC gc; if (MI_NPIXELS(mi) > 3) { XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, color)); gc = MI_GC(mi); } else { XGCValues gcv; gcv.stipple = bp->pixmaps[color]; gcv.foreground = MI_WHITE_PIXEL(mi); gcv.background = MI_BLACK_PIXEL(mi); XChangeGC(MI_DISPLAY(mi), bp->stippledGC, GCStipple | GCForeground | GCBackground, &gcv); gc = bp->stippledGC; } grid2screen(bp, col, row, &ccol, &crow); ccol = bp->xb + bp->xs * (ccol - 1); crow = bp->yb + bp->ys * (crow - 1); XFillRectangle(display, window, gc, ccol, crow, 3 * bp->xs, 3 * bp->ys); if (bp->eyes && bp->xs >= 1 && bp->ys >= 1) { /* Draw Eyes */ XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi)); switch (direction) { case 0: XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 2, crow + 3 * bp->ys / 2 - 1); XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 2, crow + 3 * bp->ys / 2 + 1); break; case 20: case 30: case 40: case 45: case 60: XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 3, crow + 1); XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 2, crow + 2); break; case 80: case 90: case 100: XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs / 2 - 1, crow + 1); XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs / 2 + 1, crow + 1); break; case 120: case 135: case 140: case 150: case 160: XDrawPoint(display, window, MI_GC(mi), ccol + 2, crow + 1); XDrawPoint(display, window, MI_GC(mi), ccol + 1, crow + 2); break; case 180: XDrawPoint(display, window, MI_GC(mi), ccol + 1, crow + 3 * bp->ys / 2 - 1); XDrawPoint(display, window, MI_GC(mi), ccol + 1, crow + 3 * bp->ys / 2 + 1); break; case 200: case 210: case 220: case 225: case 240: XDrawPoint(display, window, MI_GC(mi), ccol + 2, crow + 3 * bp->ys - 2); XDrawPoint(display, window, MI_GC(mi), ccol + 1, crow + 3 * bp->ys - 3); break; case 260: case 270: case 280: XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs / 2 - 1, crow + 3 * bp->ys - 2); XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs / 2 + 1, crow + 3 * bp->ys - 2); break; case 300: case 315: case 320: case 330: case 340: XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 3, crow + 3 * bp->ys - 2); XDrawPoint(display, window, MI_GC(mi), ccol + 3 * bp->xs - 2, crow + 3 * bp->ys - 3); break; default: (void) fprintf(stderr, "wrong direction %d\n", direction); } } } static void eraseabug(ModeInfo * mi, int col, int row) { bugfarmstruct *bp = &bugfarms[MI_SCREEN(mi)]; int ccol, crow; XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); grid2screen(bp, col, row, &ccol, &crow); XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), bp->xb + bp->xs * (ccol - 1), bp->yb + bp->ys * (crow - 1), 3 * bp->xs, 3 * bp->ys); } static void flush_buglist(bugfarmstruct * bp) { while (bp->lastbug->previous != bp->firstbug) { bp->currbug = bp->lastbug->previous; bp->currbug->previous->next = bp->lastbug; bp->lastbug->previous = bp->currbug->previous; /*bp->arr[bp->currbug->info.col + bp->currbug->info.row * bp->ncols] = 0; */ free(bp->currbug); } while (bp->lasttemp->previous != bp->firsttemp) { bp->currbug = bp->lasttemp->previous; bp->currbug->previous->next = bp->lasttemp; bp->lasttemp->previous = bp->currbug->previous; /*bp->arr[bp->currbug->info.col + bp->currbug->info.row * bp->ncols] = 0; */ free(bp->currbug); } } static void free_buglist(bugfarmstruct *bp) { if (bp->firstbug != NULL) { flush_buglist(bp); free(bp->firstbug); bp->firstbug = (BugList *) NULL; } if (bp->lastbug != NULL) { free(bp->lastbug); bp->lastbug = (BugList *) NULL; } if (bp->lasttemp != NULL) { free(bp->lasttemp); bp->lasttemp = (BugList *) NULL; } if (bp->firsttemp != NULL) { free(bp->firsttemp); bp->firsttemp = (BugList *) NULL; } } static Bool init_buglist(bugfarmstruct * bp) { /* Waste some space at the beginning and end of list so we do not have to complicated checks against falling off the ends. */ if ((bp->lastbug = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return False; } if ((bp->firstbug = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return False; } bp->firstbug->previous = bp->lastbug->next = (struct _BugList *) NULL; bp->firstbug->next = bp->lastbug->previous = (struct _BugList *) NULL; bp->firstbug->next = bp->lastbug; bp->lastbug->previous = bp->firstbug; if ((bp->lasttemp = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return False; } if ((bp->firsttemp = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return False; } bp->firsttemp->previous = bp->lasttemp->next = (struct _BugList *) NULL; bp->firsttemp->next = bp->lasttemp->previous = (struct _BugList *) NULL; bp->firsttemp->next = bp->lasttemp; bp->lasttemp->previous = bp->firsttemp; return True; } static void addto_buglist(bugfarmstruct * bp, bugstruct info) { if ((bp->currbug = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return; } bp->lastbug->previous->next = bp->currbug; bp->currbug->previous = bp->lastbug->previous; bp->currbug->next = bp->lastbug; bp->lastbug->previous = bp->currbug; bp->currbug->info = info; } static void removefrom_buglist(bugfarmstruct * bp, BugList * ptr) { ptr->previous->next = ptr->next; ptr->next->previous = ptr->previous; bp->arr[ptr->info.col + ptr->info.row * bp->ncols] = 0; free(ptr); } static void dupin_buglist(bugfarmstruct * bp) { BugList *temp; if ((temp = (BugList *) malloc(sizeof (BugList))) == NULL) { free_buglist(bp); return; } temp->previous = bp->babybug; temp->next = bp->babybug->next; bp->babybug->next = temp; temp->next->previous = temp; temp->info = bp->babybug->info; bp->babybug = temp; } /*- * new bug at end of list, this rotates who goes first, young bugs go last * this most likely will not change the feel to any real degree */ static void cutfrom_buglist(bugfarmstruct * bp) { bp->babybug = bp->currbug; bp->currbug = bp->currbug->previous; bp->currbug->next = bp->babybug->next; bp->babybug->next->previous = bp->currbug; bp->babybug->next = bp->lasttemp; bp->babybug->previous = bp->lasttemp->previous; bp->babybug->previous->next = bp->babybug; bp->babybug->next->previous = bp->babybug; } static void reattach_buglist(bugfarmstruct * bp) { bp->currbug = bp->lastbug->previous; bp->currbug->next = bp->firsttemp->next; bp->currbug->next->previous = bp->currbug; bp->lastbug->previous = bp->lasttemp->previous; bp->lasttemp->previous->next = bp->lastbug; bp->lasttemp->previous = bp->firsttemp; bp->firsttemp->next = bp->lasttemp; } static int dirbug(bugstruct * info, int local_neighbors) { double sum = 0.0, prob; int i; prob = (double) LRAND() / MAXRAND; #if EXTRA_GENES if ((local_neighbors % 2) && !((info->col + info->row) & 1)) { for (i = 0; i < local_neighbors; i++) { sum += info->lgene_prob[i]; if (prob < sum) return i; } } else #endif { for (i = 0; i < local_neighbors; i++) { sum += info->gene_prob[i]; if (prob < sum) return i; } } return local_neighbors - 1; /* Could miss due to rounding */ } static void mutatebug(bugstruct * info, int local_neighbors) { double sum = 0.0; int gene; gene = NRAND(local_neighbors); if (LRAND() & 1) { if (info->gene[gene] == MAXGENE) return; info->gene[gene]++; } else { if (info->gene[gene] == MINGENE) return; info->gene[gene]--; } for (gene = 0; gene < local_neighbors; gene++) sum += genexp[info->gene[gene] + MAXGENE]; for (gene = 0; gene < local_neighbors; gene++) info->gene_prob[gene] = genexp[info->gene[gene] + MAXGENE] / sum; #if EXTRA_GENES if (local_neighbors % 2) { sum = 0.0; gene = NRAND(local_neighbors); if (LRAND() & 1) { if (info->lgene[gene] == MAXGENE) return; info->lgene[gene]++; } else { if (info->lgene[gene] == MINGENE) return; info->lgene[gene]--; } for (gene = 0; gene < local_neighbors; gene++) sum += genexp[info->lgene[gene] + MAXGENE]; for (gene = 0; gene < local_neighbors; gene++) info->lgene_prob[gene] = genexp[info->lgene[gene] + MAXGENE] / sum; } #endif } static void makebacteria(ModeInfo * mi, int n, int startx, int starty, int width, int height, Bool draw) { int nbacteria = 0, ntries = 0, col, row, colrow; bugfarmstruct *bp = &bugfarms[MI_SCREEN(mi)]; /* Make bacteria but if can not, exit */ while (nbacteria < n && ntries < 2 * n) { row = NRAND(height) + starty; if (bp->neighbors == 6) { if (width - (!(row & 1)) > 0) col = NRAND(width - (!(row & 1))) + startx; else col = startx; } else { /* RECT or TRI */ col = NRAND(width) + startx; } colrow = col + row * bp->ncols; ntries++; if (!bp->arr[colrow] && !bp->bacteria[colrow]) { nbacteria++; bp->bacteria[colrow] = True; drawabacterium(mi, col, row, draw); } } } static void redrawbacteria(ModeInfo * mi, int colrow) { int col, row; bugfarmstruct *bp = &bugfarms[MI_SCREEN(mi)]; if (!bp->bacteria[colrow]) return; row = colrow / bp->ncols; col = colrow % bp->ncols; drawabacterium(mi, col, row, True); } static void free_bug(Display *display, bugfarmstruct *bp) { int shade; if (bp->stippledGC != None) { XFreeGC(display, bp->stippledGC); bp->stippledGC = None; } for (shade = 0; shade < bp->init_bits; shade++) { if (bp->pixmaps[shade] != None) { XFreePixmap(display, bp->pixmaps[shade]); bp->pixmaps[shade] = None; } } bp->init_bits = 0; if (bp->firstbug != NULL) { flush_buglist(bp); free(bp->firstbug); bp->firstbug = (BugList *) NULL; } if (bp->lastbug != NULL) { free(bp->lastbug); bp->lastbug = (BugList *) NULL; } if (bp->lasttemp != NULL) { free(bp->lasttemp); bp->lasttemp = (BugList *) NULL; } if (bp->firsttemp != NULL) { free(bp->firsttemp); bp->firsttemp = (BugList *) NULL; } if (bp->arr != NULL) { free(bp->arr); bp->arr = (BugList **) NULL; } if (bp->bacteria != NULL) { free(bp->bacteria); bp->bacteria = (char *) NULL; } } void init_bug(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int size = MI_SIZE(mi); XGCValues gcv; int nbugs = 0, ntries = 0, col = 0, row, gene, colrow, i; int nccols, ncrows; double sum; bugstruct info; bugfarmstruct *bp; if (bugfarms == NULL) { if ((bugfarms = (bugfarmstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (bugfarmstruct))) == NULL) return; } bp = &bugfarms[MI_SCREEN(mi)]; if (MI_NPIXELS(mi) <= 3) { if (bp->stippledGC == None) { gcv.fill_style = FillOpaqueStippled; if ((bp->stippledGC = XCreateGC(display, window, GCFillStyle, &gcv)) == None) { free_bug(display, bp); return; } } if (bp->init_bits == 0) { for (i = 1; i < NUMSTIPPLES; i++) { BUGBITS(stipples[i], STIPPLESIZE, STIPPLESIZE); } } } bp->generation = 0; if (bp->firstbug == NULL) { /* Genesis */ /* Set up what will be a 'triply' linked list. doubly linked list, doubly linked to an array */ if (!init_buglist(bp)) { free_bug(display, bp); return; } genexp[MAXGENE] = 1; for (i = 1; i <= MAXGENE; i++) { genexp[MAXGENE + i] = genexp[MAXGENE + i - 1] * M_E; genexp[MAXGENE - i] = 1.0 / genexp[MAXGENE + i]; } } else /* Apocolypse now */ flush_buglist(bp); for (i = 0; i < NEIGHBORKINDS; i++) { if (neighbors == plots[i]) { bp->neighbors = plots[i]; break; } if (i == NEIGHBORKINDS - 1) { bp->neighbors = plots[NRAND(NEIGHBORKINDS)]; break; } } bp->width = MI_WIDTH(mi); bp->height = MI_HEIGHT(mi); if (bp->width < 8) bp->width = 8; if (bp->height < 8) bp->height = 8; if (size < -MINSIZE) bp->ys = NRAND(MIN(-size, MAX(MINSIZE, MIN(bp->width, bp->height) / MINGRIDSIZE)) - MINSIZE + 1) + MINSIZE; else if (size < MINSIZE) { if (!size) bp->ys = MAX(MINSIZE, MIN(bp->width, bp->height) / MINGRIDSIZE); else bp->ys = MINSIZE; } else bp->ys = MIN(size, MAX(MINSIZE, MIN(bp->width, bp->height) / MINGRIDSIZE)); bp->xs = bp->ys; nccols = MAX(bp->width / bp->xs - 2, 2); ncrows = MAX(bp->height / bp->ys - 1, 2); bp->nrows = ncrows / 2; if ((bp->neighbors % 2) || bp->neighbors == 12) { bp->ncols = nccols / 3; if (bp->ncols & 1) bp->ncols--; /* Must be odd for no wrap */ if (bp->ncols < 2) bp->ncols = 2; if (!(bp->nrows & 1)) bp->nrows--; /* Must be even for no wrap */ } else bp->ncols = nccols / 2; if (bp->neighbors == 6 && !(bp->nrows & 1)) bp->nrows--; /* Must be odd for no wrap */ bp->xb = (bp->width - bp->xs * nccols) / 2; bp->yb = (bp->height - bp->ys * ncrows) / 2; if (bp->arr != NULL) free(bp->arr); if ((bp->arr = (BugList **) calloc(bp->ncols * bp->nrows, sizeof (BugList *))) == NULL) { free_bug(display, bp); return; } if (bp->bacteria != NULL) free(bp->bacteria); if ((bp->bacteria = (char *) calloc(bp->ncols * bp->nrows, sizeof (char))) == NULL) { free_bug(display, bp); return; } bp->edenheight = bp->nrows / 4; bp->edenwidth = bp->ncols / 4; if ((bp->neighbors % 2) || bp->neighbors == 12) { if (bp->edenwidth & 1) bp->edenwidth--; if (!(bp->edenheight & 1)) bp->edenheight--; } else if (bp->neighbors == 6 && bp->edenheight && !(bp->edenheight & 1)) bp->edenheight--; /* Make sure its odd */ bp->eden = (bp->edenwidth > 1 && bp->edenheight > 1); if (bp->eden) { bp->edenstartx = NRAND(bp->ncols - bp->edenwidth); bp->edenstarty = NRAND(bp->nrows - bp->edenheight); if ((bp->neighbors % 2) || bp->neighbors == 12) { if (bp->edenstartx & 1) { bp->edenstartx--; bp->eden = (bp->edenwidth > 1); } if (bp->edenstarty & 1) { bp->edenstarty--; bp->eden = (bp->edenheight > 1); } } else if (bp->neighbors == 6 && (bp->edenstarty & 1)) { bp->edenstarty--; bp->eden = (bp->edenheight > 1); } } /* Play G-d with these numbers... the genes */ nbugs = MI_COUNT(mi); if (nbugs < 0) nbugs = NRAND(-nbugs) + 1; bp->nbugs = 0; MI_CLEARWINDOW(mi); bp->painted = False; if (MI_IS_FULLRANDOM(mi)) { bp->eyes = (Bool) (LRAND() & 1); } else { bp->eyes = eyes; } bp->eyes = (bp->eyes && bp->xs > 1 && bp->ys > 1); /* Try to make bugs, but if can not, exit */ while (bp->nbugs < nbugs && ntries < 2 * nbugs) { row = NRAND(bp->nrows); if (bp->neighbors == 6) { if (bp->ncols - (!(row & 1)) > 0) col = NRAND(bp->ncols - (!(row & 1))); else col = 0; } else { /* RECT or TRI */ col = NRAND(bp->ncols); } colrow = col + row * bp->ncols; ntries++; if (!bp->arr[colrow] && !has_neighbor(bp, col, row)) { bp->nbugs++; info.age = 0; info.energy = INITENERGY; info.direction = NRAND(bp->neighbors); for (gene = 0; gene < bp->neighbors; gene++) #if 0 /* Some creationalism, may create gliders or twirlers */ do { double temp = (double) LRAND() / MAXRAND; info.gene[gene] = ((int) (1.0 / (1.0 - temp * temp)) - 1) * ((LRAND() & 1) ? -1 : 1); } while (info.gene[gene] > MAXGENE / 2 || info.gene[gene] < MINGENE / 2); #else /* Jitterbugs, evolve or die */ info.gene[gene] = 0; #endif sum = 0; for (gene = 0; gene < bp->neighbors; gene++) sum += genexp[info.gene[gene] + MAXGENE]; for (gene = 0; gene < bp->neighbors; gene++) info.gene_prob[gene] = genexp[info.gene[gene] + MAXGENE] / sum; #if EXTRA_GENES if (bp->neighbors % 2) { for (gene = 0; gene < bp->neighbors; gene++) #if 0 /* Some creationalism, may create gliders or twirlers */ do { double temp = (double) LRAND() / MAXRAND; info.lgene[gene] = ((int) (1.0 / (1.0 - temp * temp)) - 1) * ((LRAND() & 1) ? -1 : 1); } while (info.lgene[gene] > MAXGENE / 2 || info.lgene[gene] < MINGENE / 2); #else /* Jitterbugs, evolve or die */ info.lgene[gene] = 0; #endif sum = 0; for (gene = 0; gene < bp->neighbors; gene++) sum += genexp[info.lgene[gene] + MAXGENE]; for (gene = 0; gene < bp->neighbors; gene++) info.lgene_prob[gene] = genexp[info.lgene[gene] + MAXGENE] / sum; } #endif if (MI_NPIXELS(mi) > 3) info.color = NRAND(MI_NPIXELS(mi)); else info.color = NRAND(NUMSTIPPLES - 1); info.col = col; info.row = row; addto_buglist(bp, info); if (bp->firstbug == NULL) { free_bug(display, bp); return; } bp->arr[colrow] = bp->currbug; drawabug(mi, col, row, bp->currbug->info.color, (int) bp->currbug->info.direction * ANGLES / bp->neighbors); } } makebacteria(mi, bp->ncols * bp->nrows / 2, 0, 0, bp->ncols, bp->nrows, False); if (bp->eden) makebacteria(mi, bp->edenwidth * bp->edenheight / 2, bp->edenstartx, bp->edenstarty, bp->edenwidth, bp->edenheight, False); bp->redrawing = 1; bp->redrawpos = 0; } #define ENOUGH 16 void draw_bug(ModeInfo * mi) { int col, row, ncol = -1, nrow = -1, colrow, ncolrow; int absdir = 0, tryit, dir; bugfarmstruct *bp; if (bugfarms == NULL) return; bp = &bugfarms[MI_SCREEN(mi)]; if (bp->firstbug == NULL) return; MI_IS_DRAWN(mi) = True; bp->painted = True; bp->currbug = bp->firstbug->next; while (bp->currbug != bp->lastbug) { col = bp->currbug->info.col; row = bp->currbug->info.row; colrow = col + row * bp->ncols; if (bp->currbug->info.energy-- < 0) { /* Time to die, Bug */ #ifdef DEBUG assert(bp->currbug == bp->arr[colrow]); #endif /* back up one or else in void */ bp->currbug = bp->currbug->previous; removefrom_buglist(bp, bp->arr[colrow]); #ifdef DEBUG assert(bp->arr[colrow] == 0); #endif bp->arr[colrow] = 0; eraseabug(mi, col, row); bp->nbugs--; } else { /* try to move */ bp->arr[colrow] = 0; /* Don't want neighbor to detect itself */ tryit = 0; do { if (tryit++ > ENOUGH) { break; } absdir = (bp->currbug->info.direction + dirbug(&bp->currbug->info, bp->neighbors)) % bp->neighbors; dir = absdir * ANGLES / bp->neighbors; if (bp->neighbors % 2) { if ((col + row) & 1) dir = (3 * ANGLES / 2 - dir) % ANGLES; else dir = (ANGLES - dir) % ANGLES; } } while (!dirmove(bp, col, row, dir, &ncol, &nrow) || has_neighbor(bp, ncol, nrow) || bp->arr[ncol + nrow * bp->ncols]); bp->currbug->info.age++; bp->currbug->info.energy--; if (tryit <= ENOUGH) { ncolrow = ncol + nrow * bp->ncols; if (bp->bacteria[ncolrow]) { bp->currbug->info.energy += BACTERIAENERGY; bp->bacteria[ncolrow] = 0; if (bp->currbug->info.energy > MAXENERGY) bp->currbug->info.energy = MAXENERGY; } bp->currbug->info.col = ncol; bp->currbug->info.row = nrow; bp->currbug->info.direction = absdir; #ifdef DEBUG assert(bp->arr[ncolrow] == 0); #endif bp->arr[ncolrow] = bp->currbug; if (bp->currbug->info.energy > STRONG && bp->currbug->info.age > MATURE) { /* breed */ drawabug(mi, ncol, nrow, bp->currbug->info.color, (int) bp->currbug->info.direction * ANGLES / bp->neighbors); cutfrom_buglist(bp); /* This rotates out who goes first */ bp->babybug->info.age = 0; bp->babybug->info.energy = INITENERGY; dupin_buglist(bp); if (bp->firstbug == NULL) { free_bug(MI_DISPLAY(mi), bp); return; } mutatebug(&bp->babybug->previous->info, bp->neighbors); mutatebug(&bp->babybug->info, bp->neighbors); bp->arr[colrow] = bp->babybug; bp->babybug->info.col = col; bp->babybug->info.row = row; bp->nbugs++; } else { eraseabug(mi, col, row); drawabug(mi, ncol, nrow, bp->currbug->info.color, (int) bp->currbug->info.direction * ANGLES / bp->neighbors); } } else bp->arr[colrow] = bp->currbug; } bp->currbug = bp->currbug->next; } reattach_buglist(bp); makebacteria(mi, FOODPERCYCLE, 0, 0, bp->ncols, bp->nrows, True); if (bp->eden) makebacteria(mi, FOODPERCYCLE, bp->edenstartx, bp->edenstarty, bp->edenwidth, bp->edenheight, True); if (!bp->nbugs || bp->generation >= MI_CYCLES(mi)) init_bug(mi); bp->generation++; if (bp->redrawing) { int i; for (i = 0; i < REDRAWSTEP; i++) { if (bp->bacteria[bp->redrawpos]) redrawbacteria(mi, bp->redrawpos); if (++(bp->redrawpos) >= bp->ncols * bp->nrows) { bp->redrawing = 0; break; } } } } void release_bug(ModeInfo * mi) { if (bugfarms != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_bug(MI_DISPLAY(mi), &bugfarms[screen]); free(bugfarms); bugfarms = (bugfarmstruct *) NULL; } } void refresh_bug(ModeInfo * mi) { bugfarmstruct *bp; if (bugfarms == NULL) return; bp = &bugfarms[MI_SCREEN(mi)]; if (bp->painted) { MI_CLEARWINDOW(mi); bp->painted = False; bp->redrawing = 1; bp->redrawpos = 0; } } #endif /* MODE_bug */