Last active
June 14, 2017 00:20
-
-
Save Jacajack/736ae6d85e49c7d2b6f73edf498290c1 to your computer and use it in GitHub Desktop.
A demonstration of how plagues spread
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
gcc -o plague plague.c -lSDL -lpthread -lm -Wall |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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