/* * Xburn, modified by JAR from * * Xfire, a forest fire simulator for X windows. * * Michael Creutz creutz@wind.phy.bnl.gov * :!cc -O % -lm -lX11 */ # include # include # include # include # include # include # include # include /* lattice dimensions (plus two for boundaries): */ # define NROWS 160 # define NCOLS 192 # define VOLUME (NROWS*NCOLS) int block = 1; /* default to small blocks */ char field[2][VOLUME]; /* to store the system */ int old = 0, new = 1; # define barren 0 # define tree 1 # define fire 2 # define burnt 3 long translate[256]; /* for converting colors */ int paused = 0; int iteration = 0; int iter_tot = 0; double p_init = 0.0; double p_prop = 1.0; int ntrees_init = 0; int ntrees = 0; int nfires = 0; int mask; static char *progname; char stringbuffer[100]; /* for fast but crude random number generation */ # define RSIZE 127 int births[RSIZE]; int randomizer = 57, birthindex = 0; /* dimensions for placing things */ #define PLAYTOP 60 /* various window stuff */ Display *display; int screen; Window window, quitbutton, pausebutton, playground; GC gc, gcr, gcxor, gccolor; XImage *spinimage = NULL; XFontStruct *font = NULL; int font_height; unsigned int windowwidth, windowheight; XSizeHints size_hints; int darkcolor, lightcolor, black, white; main(argc, argv) int argc; char **argv; { unsigned int width, height; int i, x, y; XEvent report; progname = argv[0]; if (argc < 3) exit(1); p_init = atof(argv[1]); /* bring in (required) fraction filled */ if ((p_init < 0.0) || (p_init > 1.0)) exit(1); ntrees_init = (int) ((double) (VOLUME) * p_init); p_prop = atof(argv[2]); /* bring in propagation probability */ if ((p_prop < 0.0) || (p_prop > 1.0)) exit(1); if (argc > 3) /* show image with bigger blocks */ block = atoi(argv[3]); if (block > 4) block = 4; if (block < 1) block = 1; openwindow(argc, argv); /* mask used for random site selection */ mask = 1; while (mask < VOLUME) mask = 1 | (mask << 1); /* set initial state random filled up to p_init */ srand48( (long) time( (time_t *)0 ) ); for (i = 0; i < VOLUME; i++) field[old][i] = field[new][i] = barren; ntrees = 0; while (ntrees < ntrees_init) { i = (int) (VOLUME * drand48()); if (field[new][i] == barren) { field[old][i] = field[new][i] = tree; ntrees++; } } /* start fires along east boundary and its reflection west */ for (i = 0; i < VOLUME; i += NCOLS) { field[old][i] = field[old][i + NCOLS - 2] = fire; nfires += 1; } /* set up array for fast random number generation */ for (i = 0; i < RSIZE; i++) births[i] = (lrand48() >> 2) & mask; /* loop forever, looking for events */ while (1) { if (paused | XPending(display)) { XNextEvent(display, &report); switch (report.type) { case Expose: if (report.xexpose.count != 0) break; /* more in queue, wait for * them */ repaint(); break; case ConfigureNotify: width = report.xconfigure.width; height = report.xconfigure.height; if ((width < size_hints.min_width) || (height < size_hints.min_height)) { fprintf(stderr, "%s: window too small to proceed.\n", progname); XUnloadFont(display, font->fid); XFreeGC(display, gc); XCloseDisplay(display); exit(1); } break; case ButtonPress: if (report.xbutton.window == quitbutton) { XUnloadFont(display, font->fid); XFreeGC(display, gc); XCloseDisplay(display); exit(1); } else if (report.xbutton.window == pausebutton) { paused = 1 - paused; mypause(); } else if (report.xbutton.window == playground) { /* start a fire */ x = report.xbutton.x / block; y = report.xbutton.y / block; field[old][x + NCOLS * y] = fire; showpic(); } else /* update for clicks outside buttons */ update(); break; default: break; } } else update(); } } update() { int i, newtrees = VOLUME / 32; fixboundary(); # if 0 /* grow new trees */ while (newtrees) { newtrees--; i = VOLUME; while (i >= VOLUME) { i = births[birthindex]; births[birthindex] ^= births[randomizer]; if ((++birthindex) >= RSIZE) birthindex = 0; if ((++randomizer) >= RSIZE) randomizer = 0; } if (field[old][i] != fire) field[new][i] = field[old][i] = tree; } # endif /* spread fires */ for (i = NCOLS; i < VOLUME - NCOLS; i++) { if (fire == field[old][i]) { if ((tree == field[new][i - 1]) && (drand48() <= p_prop)) field[new][i - 1] = fire; if ((tree == field[new][i + 1]) && (drand48() <= p_prop)) field[new][i + 1] = fire; if ((tree == field[new][i - NCOLS]) && (drand48() <= p_prop)) field[new][i - NCOLS] = fire; if ((tree == field[new][i + NCOLS]) && (drand48() <= p_prop)) field[new][i + NCOLS] = fire; /* field[old][i] = field[new][i] = barren; */ field[old][i] = field[new][i] = burnt; } } # if 1 /* fix top and bottom boundaries */ for (i = 0; i < NCOLS; i++) if (fire == field[old][i]) if (tree == field[new][i + NCOLS]) field[new][i + NCOLS] = fire; for (i = VOLUME - NCOLS; i < VOLUME; i++) if (fire == field[old][i]) if (tree == field[new][i - NCOLS]) field[new][i - NCOLS] = fire; # endif old = new; new = 1 - new; showpic(); /* if ((iter_tot % 25) == 0) { */ if (1) { ntrees = nfires = 0; for (i = NCOLS; i < VOLUME - NCOLS; i++) { ntrees += (field[old][i] == tree); nfires += (field[old][i] == fire); } iteration = 0; sprintf(stringbuffer, "%d iter, %d fires ", iter_tot, nfires); XDrawImageString(display, window, gccolor, 20, PLAYTOP + 25 + block * NROWS, stringbuffer, strlen(stringbuffer)); sprintf(stringbuffer, "%5.3f trees left, %5.3f start ", (double) ntrees/(double) ntrees_init, p_init); XDrawImageString(display, window, gccolor, 20, PLAYTOP + 39 + block * NROWS, stringbuffer, strlen(stringbuffer)); if (nfires == 0) { paused = 1 - paused; mypause(); } } iter_tot++; return; } showpic() { int row, col, i1, i2, color, j, j1, j2; char *picture = (*spinimage).data; if (8 == (*spinimage).depth) { if (block > 1) /* I wish I knew how to do this faster */ for (row = 0; row < NROWS; row++) for (col = 0; col < NCOLS; col++) { color = translate[field[old][row * NCOLS + col]]; j = block * (col + block * NCOLS * row); if (color != picture[j]) for (i1 = 0; i1 < block; i1++) { j1 = i1 * block * NCOLS + j; for (i2 = 0; i2 < block; i2++) picture[j1 + i2] = color; } } else for (j = 0; j < VOLUME; j++) picture[j] = translate[field[old][j]]; } else { /* depth is not 8, use xputpixel (this is * really ugly) */ if (block > 1) /* I wish I knew how to do this faster */ for (row = 0; row < NROWS; row++) for (col = 0; col < NCOLS; col++) { color = translate[field[old][row * NCOLS + col]]; if (color != XGetPixel(spinimage, j1 = block * col, j2 = block * row)) for (i2 = 0; i2 < block; i2++) for (i1 = 0; i1 < block; i1++) XPutPixel(spinimage, j1 + i1, j2 + i2, color); } else for (j = 0; j < VOLUME; j++) XPutPixel(spinimage, j, 0, translate[field[old][j]]); } XPutImage(display, playground, gc, spinimage, 0, 0, 0, 0, block * NCOLS, block * NROWS); return; } fixboundary() /* copies edges for periodicity */ { int i; for (i = 0; i < NCOLS; i++) { field[old][i] = field[old][VOLUME - 2 * NCOLS + i]; field[old][VOLUME - NCOLS + i] = field[old][NCOLS + i]; } for (i = 0; i < VOLUME; i += NCOLS) { field[old][i] = field[old][i + NCOLS - 2]; field[old][i + NCOLS - 1] = field[old][i + 1]; } return; } repaint() /* this fixes the window up whenever it is uncovered */ { XDrawString(display, quitbutton, gcr, 0, font_height, "quit", 4); mypause(); /* write various strings */ sprintf(stringbuffer, "%d by %d lattice", NCOLS - 2, NROWS - 2); XDrawString(display, window, gc, 5, 40, stringbuffer, strlen(stringbuffer)); XDrawString(display, window, gc, NCOLS * block + 50, PLAYTOP + 25 + block * NROWS ,"MJC", 3); showpic(); return; } /* fix up the pause button */ mypause() { if (0 == paused) XDrawImageString(display, pausebutton, gcr, 0, font_height, "pause", 5); else XDrawImageString(display, pausebutton, gcr, 0, font_height, " run ", 5); return; } openwindow(argc, argv) /* * a lot of this is taken from the basicwin program in the Xlib Programming * Manual */ int argc; char **argv; { char *window_name = "Forest fires"; char *icon_name = "fires"; Pixmap icon_pixmap; char *display_name = NULL; long event_mask; XColor xcolor, colorcell; Colormap cmap; int i; XEvent report; #define icon_bitmap_width 16 #define icon_bitmap_height 16 static char icon_bitmap_bits[] = { 0x1f, 0xf8, 0x1f, 0x88, 0x1f, 0x88, 0x1f, 0x88, 0x1f, 0x88, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /* open up the display */ if ((display = XOpenDisplay(display_name)) == NULL) { fprintf(stderr, "%s: cannot connect to X server %s\n", progname, XDisplayName(display_name)); exit(-1); } screen = DefaultScreen(display); cmap = DefaultColormap(display, screen); /* set the colors */ darkcolor = black = BlackPixel(display, screen); lightcolor = white = WhitePixel(display, screen); translate[3] = white; translate[1] = black; translate[2] = lightcolor; translate[0] = darkcolor; if (XAllocNamedColor(display, cmap, "firebrick", &colorcell, &xcolor)) darkcolor = colorcell.pixel; if (XAllocNamedColor(display, cmap, "wheat", &colorcell, &xcolor)) lightcolor = colorcell.pixel; if (XAllocNamedColor(display, cmap, "black", &colorcell, &xcolor)) translate[barren] = colorcell.pixel; if (XAllocNamedColor(display, cmap, "forest green", &colorcell, &xcolor)) translate[tree] = colorcell.pixel; if (XAllocNamedColor(display, cmap, "yellow", &colorcell, &xcolor)) translate[fire] = colorcell.pixel; /* purple means I screwed up somewhere */ if (XAllocNamedColor(display, cmap, "purple", &colorcell, &xcolor)) translate[3] = colorcell.pixel; /* fill out the color table for future uses */ for (i = 4; i < 256; i++) translate[i] = translate[i % 4]; /* make the main window */ windowwidth = block * NCOLS + 85; windowheight = PLAYTOP + block * NROWS + 40; window = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, windowwidth, windowheight, 4, black, lightcolor); /* make the icon */ icon_pixmap = XCreateBitmapFromData(display, window, icon_bitmap_bits, icon_bitmap_width, icon_bitmap_height); size_hints.flags = PPosition | PSize | PMinSize; size_hints.min_width = windowwidth; size_hints.min_height = windowheight; #ifdef X11R3 size_hints.x = x; size_hints.y = y; size_hints.width = windowwidth; size_hints.height = windowheight; XSetStandardProperties(display, window, window_name, icon_name, icon_pixmap, argv, argc, &size_hints); #else { XWMHints wm_hints; XClassHint class_hints; XTextProperty windowName, iconName; if (XStringListToTextProperty(&window_name, 1, &windowName) == 0) { fprintf(stderr, "%s: structure allocation for windowName failed.\n" ,progname); exit(-1); } if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0) { fprintf(stderr, "%s: structure allocation for iconName failed.\n" ,progname); exit(-1); } wm_hints.initial_state = NormalState; wm_hints.input = True; wm_hints.icon_pixmap = icon_pixmap; wm_hints.flags = StateHint | IconPixmapHint | InputHint; class_hints.res_name = progname; class_hints.res_class = "Basicwin"; XSetWMProperties(display, window, &windowName, &iconName, argv, argc, &size_hints, &wm_hints, &class_hints); } #endif /* make the buttons */ quitbutton = XCreateSimpleWindow(display, window, 6, 0, 40, 20, 2, black, darkcolor); pausebutton = XCreateSimpleWindow(display, window, 56, 0, 48, 20, 2, black, darkcolor); playground = XCreateSimpleWindow(display, window, 42, PLAYTOP, block * NCOLS, block * NROWS, 2, black, white); /* pick the events to look for */ event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask; XSelectInput(display, window, event_mask); event_mask = ButtonPressMask; /* * note that with this simple mask if one just covers a button it * will not get redrawn. I wonder if anyone will notice? If I put * the exposuremask in here, things flash irritatingly on being * uncovered. */ XSelectInput(display, quitbutton, event_mask); XSelectInput(display, pausebutton, event_mask); XSelectInput(display, playground, event_mask); /* pick font: 9x15 is supposed to almost always be there */ if ((font = XLoadQueryFont(display, "9x15")) == NULL) { fprintf(stderr, "%s: Cannot open 9x15 font\n", progname); exit(-1); } font_height = font->ascent + font->descent; /* * make graphics contexts: gc for black on white gccolor for * background and buttons gcr for reverse video gcxor for * highlighting */ gc = XCreateGC(display, window, 0, NULL); XSetFont(display, gc, font->fid); XSetForeground(display, gc, black); XSetBackground(display, gc, white); gcr = XCreateGC(display, window, 0, NULL); XSetFont(display, gcr, font->fid); XSetForeground(display, gcr, lightcolor); XSetBackground(display, gcr, darkcolor); gccolor = XCreateGC(display, window, 0, NULL); XSetFont(display, gccolor, font->fid); XSetForeground(display, gccolor, darkcolor); XSetBackground(display, gccolor, lightcolor); gcxor = XCreateGC(display, window, 0, NULL); XSetFont(display, gcxor, font->fid); XSetForeground(display, gcxor, darkcolor ^ lightcolor); XSetFunction(display, gcxor, GXxor); /* show the window and buttons */ XMapWindow(display, window); XMapWindow(display, quitbutton); XMapWindow(display, pausebutton); XMapWindow(display, playground); /* wait for playground to be displayed befor proceeding */ i = 1; /* a flag */ while (i) { XNextEvent(display, &report); switch (report.type) { case Expose: if (report.xexpose.window != playground) i = 0; default: break; } } /* make the image structure */ spinimage = XGetImage((Display *) display, (Drawable) playground, 0, 0, block * NCOLS, block * NROWS, AllPlanes, ZPixmap); if (NULL == spinimage) { fprintf(stderr, "trouble creating image structure\n"); exit(-1); } repaint(); return; }