// xhexpar2.c MR Dec 2000, Jan 2001 // #include #include #include #include #include #include #include "bool.h" #include "rand.h" #include "xgrp.h" #define BGCOLOR CLR_DGREY // -------------------------------------------------------------------------- // DATA TYPES // -------------------------------------------------------------------------- typedef enum { EMPTY, PARAS, HOST } cont_t; typedef struct { cont_t cur; // Current contents of cell cont_t tmp; // Used temporarily to determine next contents of cell } cell_t; // #define SIZEMAX 128 // #define SIZEMAX 200 #define SIZEMAX 512 typedef struct { cell_t cell[SIZEMAX][SIZEMAX]; } world_t; // -------------------------------------------------------------------------- // GENERAL CELL-ARRAY MANIPULATION FUNCTIONS // -------------------------------------------------------------------------- void w_clear( world_t * pW, int size ) { int i, j; for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { pW->cell[i][j].cur = EMPTY; pW->cell[i][j].tmp = EMPTY; } } } void w_insrandom( world_t * pW, int size, unsigned int seed, int density ) // Fill '.cur' with random contents { int i, j; srand( seed ); assert( density >= 1 ); for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { if ( n_rand( density ) > 0 ) { continue; } pW->cell[i][j].cur = ( n_rand(2) == 0 ? HOST : PARAS ); } } } cont_t getcur( const world_t * pW, int size, int i, int j ) { if ( ! ( 0 <= i && i < size && 0 <= j && j < size ) ) { return EMPTY; } else { return pW->cell[i][j].cur; } } cont_t gettmp( const world_t * pW, int size, int i, int j ) { if ( ! ( 0 <= i && i < size && 0 <= j && j < size ) ) { return EMPTY; } else { return pW->cell[i][j].tmp; } } void settmp( world_t * pW, int size, int i, int j, cont_t val ) { if ( ! ( 0 <= i && i < size && 0 <= j && j < size ) ) { return; } else { pW->cell[i][j].tmp = val; } } // -------------------------------------------------------------------------- // COMPUTATION OF NEXT STATE // -------------------------------------------------------------------------- void w_tmptocur( world_t * pW, int size ) // // Copy tmp to cur, and kill parasites not neighbouring any host. // { int i, j; for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { // Parasite dies if not next to any host if ( pW->cell[i][j].cur == PARAS ) { cont_t left = gettmp( pW, size, i-1, j ); cont_t right = gettmp( pW, size, i+1, j ); cont_t up = gettmp( pW, size, i, j-1 ); cont_t down = gettmp( pW, size, i, j+1 ); cont_t ur = gettmp( pW, size, i+1, j-1 ); cont_t dl = gettmp( pW, size, i-1, j+1 ); if ( ! ( left == HOST || right == HOST || up == HOST || down == HOST || ur == HOST || dl == HOST ) ) { pW->cell[i][j].cur = EMPTY; } } else { pW->cell[i][j].cur = pW->cell[i][j].tmp; } }//for(j) }//for(i) } typedef enum { INERT = 0, WRAP } border_t; #define N_BORDER 2 static const char * border_t_text[N_BORDER] = { "INERT", "WRAP" }; void w_curtotmp( world_t * pW, int size, border_t borderType, double probHost, // Host growing speed double probParas ) // Parasite growing speed // { int i, j; assert( 0.0 < probHost && probHost <= 1.0 ); assert( 0.0 < probParas && probParas <= 1.0 ); for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { pW->cell[i][j].tmp = pW->cell[i][j].cur; } } for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { cont_t target; cont_t own; double prob; cont_t left; cont_t right; cont_t up; cont_t down; cont_t ur; cont_t dl; if ( pW->cell[i][j].cur == EMPTY ) { continue; } switch ( pW->cell[i][j].cur ) { case HOST: target = EMPTY; own = HOST; prob = probHost; break; case PARAS: target = HOST; own = PARAS; prob = probParas; break; }//switch left = getcur( pW, size, i-1, j ); right = getcur( pW, size, i+1, j ); up = getcur( pW, size, i, j-1 ); down = getcur( pW, size, i, j+1 ); ur = getcur( pW, size, i+1, j-1 ); dl = getcur( pW, size, i-1, j+1 ); // Produce offspring in neighbouring cells if ( left == target && f_rand(1.0) <= prob ) { settmp( pW, size, i-1, j, own ); } if ( up == target && f_rand(1.0) <= prob ) { settmp( pW, size, i, j-1, own ); } if ( right == target && f_rand(1.0) <= prob ) { settmp( pW, size, i+1, j, own ); } if ( down == target && f_rand(1.0) <= prob ) { settmp( pW, size, i, j+1, own ); } if ( ur == target && f_rand(1.0) <= prob ) { settmp( pW, size, i+1, j-1, own ); } if ( dl == target && f_rand(1.0) <= prob ) { settmp( pW, size, i-1, j+1, own ); } }//for(j) }//for(i) } // -------------------------------------------------------------------------- // LOGICAL HEXAGONAL GRID AUXILIARY FUNCTIONS // -------------------------------------------------------------------------- // Direction vectors, in terms of cell indexes: #define NDRN 6 static const int GLOB_udrn_i[NDRN] = { 1, 0, -1, -1, 0, 1 }; static const int GLOB_udrn_j[NDRN] = { 0, 1, 1, 0, -1, -1 }; // (0,-1) (1,-1) // *-------* // / \ / \ // / \ / \ // / \ / \ // (-1,0*-------*(0,0)--*(1,0) 0---> x // \ / \ / \ // \ / \ / \ // \ / \ / \ // *-------* y // (-1,1) (0,1) // -------------------------------------------------------------------------- // SCREEN I/O FUNCTIONS // -------------------------------------------------------------------------- #define XHALFINCMAX 8 // xhalfinc = 0 1 2 3 4 5 6 7 static const GLOB_yhalfinc[XHALFINCMAX] = { 0, 1, 2, 2, 3, 4, 4, 5 }; // Needed square screen size in pixels is: // width = 3 * size * xhalfinc // height = 2 * size * yhalfinc // where yhalfinc = GLOB_yhalfinc[xhalfinc]. int need_screenwidth( int xhalfinc, int worldsize ) { return ( 3 * worldsize * xhalfinc ); } int need_screenheight( int xhalfinc, int worldsize ) { return ( 2 * worldsize * GLOB_yhalfinc[ xhalfinc ] ); } void screenpos( int i, int j, // Cell coordinates int * px, int * py, // Screen pixel positions int xhalfinc ) { *px = ( ( j + 1 ) + ( 2 * i ) ) * xhalfinc; *py = ( 1 + ( 2 * j ) ) * GLOB_yhalfinc[ xhalfinc ]; } void print_worldoutline( grp_t * pgrp, int xhalfinc, int worldsize ) { int x00, y00, x01, y01, x10, y10, x11, y11; grp_paintrect( pgrp, 0, 0, pgrp->ourScreenWidth, pgrp->ourScreenHeight, GLOB_colorpixel[ BGCOLOR ] ); // Clear window screenpos( -1, -1, &x00, &y00, xhalfinc ); screenpos( worldsize, -1, &x10, &y10, xhalfinc ); screenpos( -1, worldsize, &x01, &y01, xhalfinc ); screenpos( worldsize, worldsize, &x11, &y11, xhalfinc ); grp_paintline( pgrp, x00, y00, x01, y01, GLOB_colorpixel[ CLR_DGREY ] ); grp_paintline( pgrp, x10, y10, x11, y11, GLOB_colorpixel[ CLR_DGREY ] ); } void print_w( grp_t * pgrp, int xhalfinc, const world_t * pW, int worldsize ) // Print all atoms in whole ''world'' anew. { int i, j; int x, y; int xinc, yhalfinc, yinc, radius; assert ( 1 <= xhalfinc && xhalfinc < XHALFINCMAX ); xinc = 2 * xhalfinc; yhalfinc = GLOB_yhalfinc[xhalfinc]; yinc = 2 * yhalfinc; //radius = ( yhalfinc > 1 ? (yhalfinc-1) : yhalfinc ); radius = yhalfinc; for ( j = 0, y = yhalfinc; j < worldsize; j++, y += yinc ) { for ( i = 0, x = ((j+1)*xhalfinc); i < worldsize; i++, x += xinc ) { cell_t cc = pW->cell[i][j]; int iclr; switch ( pW->cell[i][j].cur ) { case EMPTY: iclr = CLR_BLACK; break; case HOST: iclr = CLR_GREEN; break; case PARAS: iclr = CLR_RED; break; } grp_paintcircle( pgrp, x, y, radius, GLOB_colorpixel[ iclr ] ); } } } void print_cursor( grp_t * pgrp, int xhalfinc, int cx, int cy ) // Cell indices { int yhalfinc = GLOB_yhalfinc[xhalfinc]; int radius; int screenx, screeny; screenpos( cx, cy, &screenx, &screeny, xhalfinc ); radius = 1 + ( yhalfinc > 1 ? (yhalfinc-1) : yhalfinc ); grp_paintbox( pgrp, screenx, screeny, radius, GLOB_colorpixel[ CLR_WHITE ] ); } // ------------------------------------------------------------------------- // Auxiliary X-win graphics function for ''back-buffer'' // ------------------------------------------------------------------------- void swap_bb_to_foregrond( Display * pD, Window w, XdbeBackBuffer bb ) // Swap the ''back buffer'' to the foreground { XdbeSwapInfo si; si.swap_window = w; //si.swap_action = XdbeUndefined; si.swap_action = XdbeBackground; //This immediately fills the // new back-buffer with the // background color. XdbeSwapBuffers( pD, &si, 1 ); } // ------------------------------------------------------------------------- // Auxiliary high-level user I/O functions // ------------------------------------------------------------------------- #define KEY_ONLY_EVT_MASK KeyPressMask _bool really( const char * pstrWhat, grp_t * pgrp, Display * pD, Window w, XdbeBackBuffer bb ) { XEvent evt; char cmd; grp_strprintf( pgrp, 20, pgrp->ourScreenHeight / 3, GLOB_colorpixel[ CLR_WHITE ], "$Really %s ? (y/n)", pstrWhat ); swap_bb_to_foregrond( pD, w, bb ); XWindowEvent( pD, w, KEY_ONLY_EVT_MASK, &evt ); XLookupString( &evt, &cmd, 1, NULL, NULL ); return ( cmd == 'y' ); } // ------------------------------------------------------------------------- // MAIN // ------------------------------------------------------------------------- // #define MY_EVT_MASK KeyPressMask | VisibilityChangeMask | \ // EnterWindowMask | LeaveWindowMask | \ // FocusChangeMask | ResizeRedirectMask | \ // ExposureMask #define MY_EVT_MASK KeyPressMask | VisibilityChangeMask void usage( void ) { fprintf( stderr, "Usage: xhexpar2 [options]\n" ); fprintf( stderr, "Options (r = real value in [0,1], n = integer > 0):\n" ); fprintf( stderr, " -Pr Parasite dispersion probability\n" ); fprintf( stderr, " -Hr Host dispersion probability\n" ); fprintf( stderr, " -dn Initialize by filling each nth cell " "with random a host or parasite\n" ); fprintf( stderr, " -sn World (cell array) size\n" ); fprintf( stderr, " -pn Diameter of entities ('pixels') as " "shown on screen, in [1,6]\n" ); fprintf( stderr, " -v Verbose messages to stdout\n" ); fprintf( stderr, " -l Messages to logfile\n" ); } int main( int argc, char ** argv ) { // X-Windows variables Display * pD; Window w; XdbeBackBuffer bb; int screenNr; KeySym * pKS; int kcMin, kcMax; int ksPerKc; grp_t grp; // Logical game variables int size = 50; world_t world; border_t borderType = WRAP; //INERT; int density = 2; double probHost = 0.8; double probParas = 0.5; int xhalfinc = 3; int cx = 0; int cy = 0; int ngeneration = 0; // Flags for control of flow in main() function int k; _bool stop = 0; FILE * pfl = NULL; // Verbose messages logging file enum { AUTO, SINGLESTEP } kmode = SINGLESTEP; // Flag determining single-step mode // Cmd line args for ( k = 1; k < argc; k++ ) { if ( argv[k][0] == '-' ) { switch ( argv[k][1] ) { case 'b': borderType = INERT; break; case 'B': borderType = WRAP; break; case 'P': if ( sscanf( argv[k]+2, "%le", &probParas ) != 1 || ! ( 0.0 < probParas && probParas <= 1.0 ) ) { usage(); return -1; } break; case 'H': if ( sscanf( argv[k]+2, "%le", &probHost ) != 1 || ! ( 0.0 < probHost && probHost <= 1.0 ) ) { usage(); return -1; } break; case 'd': if ( sscanf( argv[k]+2, "%d", &density ) != 1 || ! ( density > 0 ) ) { usage(); return -1; } break; case 's': if ( sscanf( argv[k]+2, "%d", &size ) != 1 || ! ( 5 <= size && size < SIZEMAX ) ) { usage(); return -1; } break; case 'p': if ( sscanf( argv[k]+2, "%d", &xhalfinc ) != 1 || ! ( 1 <= xhalfinc && xhalfinc <= 6 ) ) { usage(); return -1; } break; case 'v': pfl = stdout; break; case 'l': pfl = fopen( "XHEXPAR2.LOG", "w" ); if ( pfl == NULL ) { fprintf( stderr, "fopen(XHEXPAR2.LOG) failed\n" ); } break; default: usage(); return -1; break; }//switch } else { usage(); return -1; } }//for assert( size < SIZEMAX ); assert( 0 < xhalfinc && xhalfinc < XHALFINCMAX ); grp.ourScreenWidth = need_screenwidth( xhalfinc, size ); grp.ourScreenHeight = need_screenheight( xhalfinc, size ); // // Initialize logical game stuff // w_clear( &world, size ); w_insrandom( &world, size, time(NULL), density ); /* * Open X-server connection, * open a window, * initialize keyboard input settings, * initialize a ''grp_t'' struct (which contains a ''GC''). */ if ( ( pD = XOpenDisplay( NULL ) ) == NULL ) { fprintf( stderr, "XOpenDisplay() Failed\n" ); return -1; } screenNr = DefaultScreen(pD); // Initialize our global color array, used in the grp_...() drawing // functions initcolors( pD ); w = XCreateSimpleWindow( pD, DefaultRootWindow(pD), 0, 0, grp.ourScreenWidth, grp.ourScreenHeight, 2, 0L, GLOB_colorpixel[ BGCOLOR ] ); // Background color //BlackPixel(pD,screenNr) ); // Background color bb = XdbeAllocateBackBufferName( pD, w, XdbeBackground ); //XdbeUndefined ); XStoreName( pD, w, "xpar2" ); XMapWindow( pD, w ); // Set some settings for how input is to be received, get a // ptr to the ''keyboard mapping'' XSelectInput( pD, w, MY_EVT_MASK ); // Init our own ''grp_t'' struct grp_makenew( &grp, pD, bb ); // Draw on the back buffer !!, not on window 'w'. // // MAIN LOOP // while ( ! stop ) { // Draw new situation on screen if ( kmode == SINGLESTEP ) { print_cursor( &grp, xhalfinc, cx, cy ); } print_w( &grp, xhalfinc, &world, size ); swap_bb_to_foregrond( pD, w, bb ); // Sleep if ( kmode == AUTO ) { // usleep( 500000 ); usleep( 100000 ); } // Get all keystrokes from queue, and execute them for(;;) { XEvent evt; char cmd; _bool cmdok = 0; cmdok = ( XCheckWindowEvent( pD, w, MY_EVT_MASK, &evt ) == True ); if ( evt.type == KeyPress && cmdok ) { // Translate keypress-event 'evt' // to ASCII character 'cmd' XLookupString( &evt, &cmd, 1, NULL, NULL ); } if ( cmdok && ( cmd == 'q' ) ) { // Quit if ( really( "quit", &grp, pD, w, bb ) ) { stop = 1; break; } } if ( ! cmdok && kmode == AUTO ) { // No more keystrokes ==> leave this // key-input loop, to display new situation // on screen, and sleep awhile break; } if ( ! cmdok && kmode == SINGLESTEP ) { // Wait until one real keystroke received usleep( 100000 ); continue; } switch ( cmd ) { case 'b': // Toggle border behaviour borderType = ( borderType + 1 ) % N_BORDER; break; case 'a': kmode = AUTO; break; case 'w': kmode = SINGLESTEP; break; }//switch if ( kmode == SINGLESTEP ) { switch ( cmd ) { case 'h': if ( cx > 0 ) { cx--; } break; case 'k': if ( cy > 0 ) { cy--; } break; case 'l': if ( cx < size-1 ) { cx++; } break; case 'j': if ( cy < size-1 ) { cy++; } break; case '0': // Make empty case 'x': world.cell[cx][cy].cur = EMPTY; break; case '1': // Put in a host world.cell[cx][cy].cur = HOST; break; case '2': // Put in a parasite world.cell[cx][cy].cur = PARAS; break; case '\r': // Update game one time-step case '\n': { w_curtotmp( &world, size, borderType, probHost, probParas ); w_tmptocur( &world, size ); ngeneration++; } break; case 'c': if ( really( "clear", &grp, pD, w, bb ) ) { w_clear( &world, size ); } break; case 'g': printf( "ngeneration = %d\n", ngeneration ); break; }//switch } if ( kmode == SINGLESTEP ) { break; // Leave this loop after one keystroke // is received } }//for(;;) // Update game one time-step if ( kmode == AUTO ) { w_curtotmp( &world, size, borderType, probHost, probParas ); w_tmptocur( &world, size ); ngeneration++; } }//while(!stop) (MAIN LOOP) /* * Close/de-initialize grp, keyboard, window and X-server connection */ grp_delete( &grp ); XUnmapWindow( pD, w ); XdbeDeallocateBackBufferName( pD, bb ); XDestroyWindow( pD, w ); XCloseDisplay ( pD ); return 0; }