// xpar2.c MR Dec 2000, Jan 2001 #include #include #include #include #include #include "bool.h" #include "rand.h" #include "xgrp.h" // #define BGCOLOR CLR_GREY #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 ) // // (( Old-version comment: // Age eggs and transform eggs that reach ''hatching time'' // to 'npara' ( = active parasite ). // )) // // 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 ul = gettmp( pW, size, i-1, j-1 ); cont_t ur = gettmp( pW, size, i+1, j-1 ); cont_t dl = gettmp( pW, size, i-1, j+1 ); cont_t dr = gettmp( pW, size, i+1, j+1 ); // if ( ! ( left == HOST || right == HOST || // up == HOST || down == HOST )) // || ul == HOST || ur == HOST || // dl == HOST || dr == HOST ) ) int nnsq = ( ( left == HOST ) + ( right == HOST ) + ( up == HOST ) + ( down == HOST ) ); int nnobl = ( ( ul == HOST ) + ( ur == HOST ) + ( dl == HOST ) + ( dr == HOST ) ); // double nnd = 1.0 * nnsq + .7 * nnobl; if ( ( nnsq + nnobl ) < 1 ) // if ( nnsq < 1 ) { 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 // // // Transform 'npara' to eggs : i.e., execute action of active // parasite (= square which has npara == 1). // // (( comment for old version // For each square S where npara > 0 : // 1. If square S contains a host aged >= 'aT' : // - //Kill host (set nhost := 0) in square S // Degrade host (decrease nhost) in square S // - Bury a new hibernating egg in square S // - Enter an egg with nhibe == 0 (which will therefore // hatch immediately in the next turn) in all // neighbouring squares of square S where nhost >= 'aT' // 2. Then always: Set npara of square S to 0. // )) { 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 ul; cont_t ur; cont_t dl; cont_t dr; 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 ); ul = getcur( pW, size, i-1, j-1 ); ur = getcur( pW, size, i+1, j-1 ); dl = getcur( pW, size, i-1, j+1 ); dr = 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 ); } // #define OBLIQUEFACTOR 0.7 #define OBLIQUEFACTOR 1.0 // #define OBLIQUEFACTOR 0 if ( ul == target && f_rand(1.0) <= prob * OBLIQUEFACTOR ) { settmp( pW, size, i-1, j-1, own ); } if ( ur == target && f_rand(1.0) <= prob * OBLIQUEFACTOR ) { settmp( pW, size, i+1, j-1, own ); } if ( dl == target && f_rand(1.0) <= prob * OBLIQUEFACTOR ) { settmp( pW, size, i-1, j+1, own ); } if ( dr == target && f_rand(1.0) <= prob * OBLIQUEFACTOR ) { settmp( pW, size, i+1, j+1, own ); } }//for(j) }//for(i) for ( i = 0; i < size; i++ ) { for ( j = 0; j < size; j++ ) { }//for(j) }//for(i) } // -------------------------------------------------------------------------- // SCREEN I/O FUNCTIONS // -------------------------------------------------------------------------- void print_w( grp_t * pgrp, int pixsize, const world_t * pW, int size ) { int i, j; int x, y; int pixinnersize = ( pixsize > 1 ? (pixsize-1) : pixsize ); assert ( pixsize >= 1 ); for ( i = 0, x = 1; i < size; i++, x += pixsize ) { for ( j = 0, y = 1; j < size; j++, y += pixsize ) { 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_paintrect( pgrp, x, y, pixinnersize, pixinnersize, GLOB_colorpixel[iclr] ); } } } void print_cursor( grp_t * pgrp, int pixsize, int cx, int cy ) { int x0 = cx * pixsize; int x1 = ( cx + 1 ) * pixsize; int y0 = cy * pixsize; int y1 = ( cy + 1 ) * pixsize; long color = GLOB_colorpixel[CLR_WHITE]; grp_paintrect( pgrp, x0, y0, pixsize+1, 1, color ); grp_paintrect( pgrp, x0, y0, 1, pixsize+1, color ); grp_paintrect( pgrp, x0, y1, pixsize+1, 1, color ); grp_paintrect( pgrp, x1, y0, 1, pixsize+1, color ); } // ------------------------------------------------------------------------- // 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: xpar2 [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 pixsize = 5; 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", &pixsize ) != 1 || ! ( 1 <= pixsize && pixsize <= 6 ) ) { usage(); return -1; } break; case 'v': pfl = stdout; break; case 'l': pfl = fopen( "XPAR2.LOG", "w" ); if ( pfl == NULL ) { fprintf( stderr, "fopen(XPAR2.LOG) failed\n" ); } break; default: usage(); return -1; break; }//switch } else { usage(); return -1; } }//for assert( size < SIZEMAX ); grp.ourScreenWidth = 1 + size * pixsize; grp.ourScreenHeight = 1 + size * pixsize; // // 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, pixsize, cx, cy ); } print_w( &grp, pixsize, &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; }