Skip to content

Instantly share code, notes, and snippets.

@Jacajack
Last active June 14, 2017 00:20
Show Gist options
  • Save Jacajack/736ae6d85e49c7d2b6f73edf498290c1 to your computer and use it in GitHub Desktop.
Save Jacajack/736ae6d85e49c7d2b6f73edf498290c1 to your computer and use it in GitHub Desktop.
A demonstration of how plagues spread
all:
gcc -o plague plague.c -lSDL -lpthread -lm -Wall
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <pthread.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <SDL/SDL.h>
#define RES_X 640
#define RES_Y 480
#define NEIGHBOUR_RANGE 30
#define INFECT_THRESHOLD 2000
#define INFECT_FACTOR 50
#define PLAGUE_VERSION "v0.5"
typedef enum
{
healthy = 0,
vulnerable = 1,
infected = 2,
} NodeStatus;
struct Node
{
int x, y;
struct Node **neighbours;
unsigned int neighbourCount;
NodeStatus status;
};
struct status
{
unsigned int running : 1;
unsigned int gfxready : 1;
unsigned int pause : 1;
unsigned int : 0;
SDL_Surface *scr;
struct Node *nodes;
unsigned int nodeCount;
struct
{
unsigned int infected, vulnerable, nodes;
} initial;
} status;
int initNode( struct Node *node )
{
const unsigned int range = 80;
unsigned int i, j;
if ( node == NULL ) return 1;
node->neighbourCount = 0;
for ( i = 0; i < status.nodeCount; i++ )
if ( pow( node->x - status.nodes[i].x, 2 ) + pow( node->y - status.nodes[i].y, 2 ) <= pow( range, 2 ) )
node->neighbourCount++;
if ( ( node->neighbours = (struct Node**) calloc( node->neighbourCount, sizeof( struct Node* ) ) ) == NULL )
return 1;
j = 0;
for ( i = 0; i < status.nodeCount; i++ )
if ( pow( node->x - status.nodes[i].x, 2 ) + pow( node->y - status.nodes[i].y, 2 ) <= pow( range, 2 ) )
node->neighbours[j++] = &status.nodes[i];
return 0;
}
void infectNodes( unsigned int amount, NodeStatus val )
{
unsigned int node;
while ( amount-- )
{
node = rand( ) % status.nodeCount;
if ( status.nodes[node].status == healthy ) status.nodes[node].status = val;
else ++amount;
}
}
int initNodes( unsigned int count )
{
unsigned int i;
status.nodeCount = count;
status.nodes = (struct Node*) calloc( status.nodeCount, sizeof( struct Node ) );
for ( i = 0; i < status.nodeCount; i++ )
{
status.nodes[i].x = rand( ) % RES_X;
status.nodes[i].y = rand( ) % RES_Y;
status.nodes[i].status = healthy;
}
for ( i = 0; i < status.nodeCount; i++ )
if ( initNode( &status.nodes[i] ) )
{
perror( "Node allocation error!\n" );
return 1;
}
return 0;
}
void freeNode( struct Node *node )
{
if ( node->neighbours != NULL )
{
free( node->neighbours );
node->neighbours = NULL;
}
}
void freeNodes( )
{
while ( status.nodeCount-- )
{
freeNode( &status.nodes[status.nodeCount] );
}
free( status.nodes );
status.nodes = NULL;
}
int updateNode( struct Node *node )
{
unsigned int i, infection = 0;
unsigned int dx, dy;
if ( node == NULL ) return -1;
switch ( node->status )
{
case healthy:
for ( i = 0; i < node->neighbourCount; i++ )
{
if ( node->neighbours[i]->status != infected ) continue;
dx = abs( node->x - node->neighbours[i]->x );
dy = abs( node->y - node->neighbours[i]->y );
if ( dy == 0 && dx == 0 ) infection += INFECT_THRESHOLD;
else
infection += INFECT_FACTOR * NEIGHBOUR_RANGE / sqrt( pow( dx, 2 ) + pow( dy, 2 ) );
}
if ( infection >= INFECT_THRESHOLD )
{
node->status = infected;
return 1;
}
break;
case vulnerable:
for ( i = 0; i < node->neighbourCount; i++ )
{
if ( node->neighbours[i]->status == infected )
{
node->status = infected;
return 1;
break;
}
}
break;
default:
break;
}
return 0;
}
void drawNode( struct Node* node )
{
const unsigned int radius = 4;
unsigned int i, j, x, y;
uint32_t *pix;
if ( node == NULL ) return;
for ( i = 0; i < radius * 2; i++ )
{
for ( j = 0; j < radius * 2; j++ )
{
x = radius - i;
y = radius - j;
if ( node->x + x >= 0 && node->y + y >= 0 && node->x + x < RES_X && node->y + y < RES_Y && \
( x * x + y * y ) < ( radius * radius ) )
{
pix = (uint32_t*)( (uint8_t *) status.scr->pixels + ( node->y + y ) * status.scr->pitch + ( node->x + x ) * sizeof *pix );
switch ( node->status )
{
case healthy:
*pix = 0x7ee027;
break;
case vulnerable:
*pix = 0xe3d12b;
break;
case infected:
*pix = 0xde1212;
break;
}
}
}
}
}
void *renderer( void *data )
{
unsigned int i;
SDL_Event ev;
status.running = 1;
status.scr = NULL;
SDL_Init( SDL_INIT_EVERYTHING );
SDL_EnableUNICODE( 1 );
status.scr = SDL_SetVideoMode( RES_X, RES_Y, 32, SDL_SWSURFACE );
SDL_WM_SetCaption( "plague" , NULL );
status.gfxready = 1;
while ( status.running )
{
while ( SDL_PollEvent( &ev ) )
{
switch ( ev.type )
{
case SDL_QUIT:
status.running = 0;
break;
case SDL_KEYDOWN:
switch ( ev.key.keysym.unicode )
{
case ' ':
status.pause ^= 1;
break;
}
break;
}
}
SDL_FillRect( status.scr, NULL, 0xFFFFFF );
for ( i = 0; i < status.nodeCount; i++ )
{
drawNode( &status.nodes[i] );
}
SDL_Flip( status.scr );
SDL_Delay( 16 );
}
SDL_Quit( );
return NULL;
}
void help( const char *exename )
{
fprintf( stderr, \
"%s - "PLAGUE_VERSION"\n"\
"Usage: %s [OPTIONS]\n"\
"\t-n - nodes count\n"\
"\t-i - infected nodes count\n"\
"\t-v - vulnerable nodes count\n"\
, exename, exename );
exit( 0 );
}
int main( int argc, char **argv )
{
unsigned int i = 0, badarg, inf;
pthread_t rendererth;
struct timespec updatetime;
updatetime.tv_sec = 0;
updatetime.tv_nsec = 1000000 * 100;
srand( time( NULL ) );
status.pause = 0;
status.initial.nodes = 2000;
status.initial.infected = 0;
status.initial.vulnerable = 0;
if ( argc <= 1 ) help( argv[0] );
for ( i = 1; i < (unsigned) argc; i++ )
{
badarg = 1;
if ( !strcmp( argv[i], "-h" ) || !strcmp( argv[i], "--help" ) ) help( argv[0] );
if ( !strcmp( argv[i], "-n" ) )
{
if ( ++i < (unsigned int) argc ) { sscanf( argv[i], "%d", &status.initial.nodes ); badarg = 0; }
else { fprintf( stderr, "%s: bad value for %s\n", argv[0], argv[i - 1] ); exit( 1 ); }
}
if ( !strcmp( argv[i], "-i" ) )
{
if ( ++i < (unsigned int) argc ) { sscanf( argv[i], "%d", &status.initial.infected ); badarg = 0; }
else { fprintf( stderr, "%s: bad value for %s\n", argv[0], argv[i - 1] ); exit( 1 ); }
}
if ( !strcmp( argv[i], "-v" ) )
{
if ( ++i < (unsigned int) argc ) { sscanf( argv[i], "%d", &status.initial.vulnerable ); badarg = 0; }
else { fprintf( stderr, "%s: bad value for %s\n", argv[0], argv[i - 1] ); exit( 1 ); }
}
if ( !strcmp( argv[i], "-t" ) )
{
if ( ++i < (unsigned int) argc ) { sscanf( argv[i], "%ld", &updatetime.tv_nsec ); badarg = 0; }
else { fprintf( stderr, "%s: bad value for %s\n", argv[0], argv[i - 1] ); exit( 1 ); }
}
if ( badarg )
{
fprintf( stderr, "%s: bad argument: %s\n", argv[0], argv[i] );
exit( 1 );
}
}
if ( updatetime.tv_nsec == 0 )
{
perror( "Infection interval must be greater than 0ms!\n" );
exit( 1 );
}
if ( updatetime.tv_nsec >= 500 )
{
perror( "Infection interval must be less than 500ms!\n" );
exit( 1 );
}
updatetime.tv_nsec *= 1000000;
if ( status.initial.vulnerable + status.initial.infected > status.initial.nodes )
{
perror( "Too many infected and vulnerable nodes!\n" );
exit( 1 );
}
fprintf( stderr, "%s: preparing nodes...\n", argv[0] );
initNodes( status.initial.nodes );
fprintf( stderr, "%s: infecting nodes...\n", argv[0] );
infectNodes( status.initial.infected, infected );
infectNodes( status.initial.vulnerable, vulnerable );
fprintf( stderr, "%s: starting...\n", argv[0] );
status.gfxready = 0;
pthread_create( &rendererth, NULL, renderer, NULL );
while ( !status.gfxready );
inf = 1;
while ( status.running && inf )
{
inf = 0;
for ( i = 0; i < status.nodeCount && !status.pause; i++ )
{
if ( updateNode( &status.nodes[i] ) == 1 )
{
inf++;
nanosleep( &updatetime, NULL );
}
}
}
updatetime.tv_nsec = 10 * 1000000;
fprintf( stderr, "%s: done...\n", argv[0] );
while ( status.running ) nanosleep( &updatetime, NULL );
fprintf( stderr, "%s: deleting nodes...\n", argv[0] );
freeNodes( );
pthread_join( rendererth, NULL );
fprintf( stderr, "%s: quitting...\n", argv[0] );
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment