/* ----------------------------------------------------------------------------- Celluvolve 3.0 Allan Crossman, Edinburgh iGEM 2011. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- This program is a graphic demonstration of how cells with cellulases attached to their cell surface will evolve high expression of said cellulases faster than cells which simply release cellulases into the media (in both cases assuming that producing more cellulase is useful). The rules are: * There is an array; every point contains a bacterial cell. * Each cell has a "genotype": its ability to produce cellulase. * The genotype is a number between 0 and 255. * Every iteration, every cell produces that many cellulase molecules. * When produced, a cellulase molecule either: * (LEFT SIMULATION) stays where it was produced, or * (RIGHT SIMULATION) floats a short distance randomly * Each iteration, some random bacteria are given a chance to reproduce. * Cells with higher food supply have a greater chance to reproduce. * Food supply is defined as the amount of cellulase present at the cell. * Reproduction means: * Overwriting a nearby cell with one's own genotype. * Reproduction is not exact. There is "mutation". * The offspring's genotype is a number close to the parent's. ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include /* Some constants that can reasonably be changed... RANDOMDISPERSAL - define this to just send the daughter to a random spot anywhere DAUGHTERDISTANCE - how far away a daughter can be placed FLOATDISTANCE - how far the cellulase can float SELECTIONS - how many cells get a chance at reproducing each tick */ #undef RANDOMDISPERSAL_LEFT #define DAUGHTERDISTANCE_LEFT 2 #define FLOATDISTANCE_LEFT 0 #undef RANDOMDISPERSAL_RIGHT #define DAUGHTERDISTANCE_RIGHT 2 #define FLOATDISTANCE_RIGHT 2 #define SELECTIONS 10000 #define WIDTH 300 #define HEIGHT 300 // How long to run the simulation? #define FINALGENERATION 600 // How often to report the average genotype? #define REPORTTICK 5 // Should we draw BMP files? #define DRAWBMP #define DRAWTICK 5 // Leave this alone... #define DOUBLEWIDTH (((WIDTH) * 2)) // The genotype of the left and right simulations... int ability_left[WIDTH][HEIGHT]; int ability_right[WIDTH][HEIGHT]; // The amount of cellulase present at each point... int cellulase_left[WIDTH][HEIGHT]; int cellulase_right[WIDTH][HEIGHT]; float rnd (void) // Random number between 0 and 1. { return (float)rand() / RAND_MAX; } int intrnd (int max) // Return integer between 0 and max inclusive. { int result; result = rnd() * (max + 1); result %= max + 1; return result; } float getaverage_left (void) { int sum = 0; int x = 0; int y = 0; for (x = 0; x < WIDTH; x++) { for (y = 0; y < HEIGHT; y++) { sum += ability_left[x][y]; } } return (float) sum / (WIDTH * HEIGHT); } float getaverage_right (void) { int sum = 0; int x = 0; int y = 0; for (x = 0; x < WIDTH; x++) { for (y = 0; y < HEIGHT; y++) { sum += ability_right[x][y]; } } return (float) sum / (WIDTH * HEIGHT); } void drawbmp (char * filename) { unsigned int headers[13]; FILE * outfile; int extrabytes; int paddedsize; int x; int y; int n; int red, green, blue; extrabytes = 4 - ((DOUBLEWIDTH * 3) % 4); // How many bytes of padding to add to each // horizontal line - the size of which must // be a multiple of 4 bytes. if (extrabytes == 4) extrabytes = 0; paddedsize = ((DOUBLEWIDTH * 3) + extrabytes) * HEIGHT; // Headers... // Note that the "BM" identifier in bytes 0 and 1 is NOT included in these "headers". headers[0] = paddedsize + 54; // bfSize (whole file size) headers[1] = 0; // bfReserved (both) headers[2] = 54; // bfOffbits headers[3] = 40; // biSize headers[4] = DOUBLEWIDTH; // biWidth headers[5] = HEIGHT; // biHeight // Would have biPlanes and biBitCount in position 6, but they're shorts. // It's easier to write them out separately (see below) than pretend // they're a single int, especially with endian issues... headers[7] = 0; // biCompression headers[8] = paddedsize; // biSizeImage headers[9] = 0; // biXPelsPerMeter headers[10] = 0; // biYPelsPerMeter headers[11] = 0; // biClrUsed headers[12] = 0; // biClrImportant outfile = fopen(filename, "wb"); // // Headers begin... // When printing ints and shorts, we write out 1 character at a time to avoid endian issues. // fprintf(outfile, "BM"); for (n = 0; n <= 5; n++) { fprintf(outfile, "%c", headers[n] & 0x000000FF); fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8); fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16); fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24); } // These next 4 characters are for the biPlanes and biBitCount fields. fprintf(outfile, "%c", 1); fprintf(outfile, "%c", 0); fprintf(outfile, "%c", 24); fprintf(outfile, "%c", 0); for (n = 7; n <= 12; n++) { fprintf(outfile, "%c", headers[n] & 0x000000FF); fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8); fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16); fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24); } // // Headers done, now write the data... // for (y = HEIGHT - 1; y >= 0; y--) // BMP image format is written from bottom to top... { for (x = 0; x <= DOUBLEWIDTH - 1; x++) { if (x < WIDTH) { red = ability_left[x][y]; green = ability_left[x][y]; blue = ability_left[x][y] / 2; } else { red = ability_right[x - WIDTH][y]; green = ability_right[x - WIDTH][y]; blue = ability_right[x - WIDTH][y] / 2; } // Also, it's written in (b,g,r) format... fprintf(outfile, "%c", blue); fprintf(outfile, "%c", green); fprintf(outfile, "%c", red); } if (extrabytes) // See above - BMP lines must be of lengths divisible by 4. { for (n = 1; n <= extrabytes; n++) { fprintf(outfile, "%c", 0); } } } fclose(outfile); return; } int main (void) { int x; int y; int tarx; int tary; int n; int tick = 0; char filename[200]; printf("\nCelluvolve, an iGEM model from Edinburgh 2011...\n\n"); for (x = 0; x < WIDTH; x++) { for (y = 0; y < HEIGHT; y++) { ability_left[x][y] = 50; ability_right[x][y] = 50; } } srand((int)time(NULL)); printf("Iteration\tAverage genotype (left)\tAverage genotype (right)\n"); while (tick <= FINALGENERATION) { // Clear the grid of all cellulase... for (x = 0; x < WIDTH; x++) { for (y = 0; y < HEIGHT; y++) { cellulase_left[x][y] = 0; cellulase_right[x][y] = 0; } } // Find out how much cellulase goes to each spot... for (x = 0; x < WIDTH; x++) { for (y = 0; y < HEIGHT; y++) { for (n = 0; n < ability_left[x][y]; n++) { tarx = x + intrnd(FLOATDISTANCE_LEFT * 2) - FLOATDISTANCE_LEFT; tary = y + intrnd(FLOATDISTANCE_LEFT * 2) - FLOATDISTANCE_LEFT; if (tarx >= 0 && tarx <= WIDTH - 1 && tary >= 0 && tary <= HEIGHT - 1) { cellulase_left[tarx][tary] += 1; } } for (n = 0; n < ability_right[x][y]; n++) { tarx = x + intrnd(FLOATDISTANCE_RIGHT * 2) - FLOATDISTANCE_RIGHT; tary = y + intrnd(FLOATDISTANCE_RIGHT * 2) - FLOATDISTANCE_RIGHT; if (tarx >= 0 && tarx <= WIDTH - 1 && tary >= 0 && tary <= HEIGHT - 1) { cellulase_right[tarx][tary] += 1; } } } } // Reproduce left... for (n = 0; n < SELECTIONS; n++) { x = intrnd(WIDTH - 1); y = intrnd(HEIGHT - 1); if (intrnd(255) < cellulase_left[x][y]) { #ifdef RANDOMDISPERSAL_LEFT tarx = intrnd(WIDTH - 1); tary = intrnd(HEIGHT - 1); #else tarx = x + intrnd(DAUGHTERDISTANCE_LEFT * 2) - DAUGHTERDISTANCE_LEFT; tary = y + intrnd(DAUGHTERDISTANCE_LEFT * 2) - DAUGHTERDISTANCE_LEFT; #endif if (tarx >= 0 && tarx <= WIDTH - 1 && tary >= 0 && tary <= HEIGHT - 1) { ability_left[tarx][tary] = ability_left[x][y] + intrnd(24) - 12; if (ability_left[tarx][tary] < 0) ability_left[tarx][tary] = 0; if (ability_left[tarx][tary] > 255) ability_left[tarx][tary] = 255; } } } // Reproduce right... for (n = 0; n < SELECTIONS; n++) { x = intrnd(WIDTH - 1); y = intrnd(HEIGHT - 1); if (intrnd(255) < cellulase_right[x][y]) { #ifdef RANDOMDISPERSAL_RIGHT tarx = intrnd(WIDTH - 1); tary = intrnd(HEIGHT - 1); #else tarx = x + intrnd(DAUGHTERDISTANCE_RIGHT * 2) - DAUGHTERDISTANCE_RIGHT; tary = y + intrnd(DAUGHTERDISTANCE_RIGHT * 2) - DAUGHTERDISTANCE_RIGHT; #endif if (tarx >= 0 && tarx <= WIDTH - 1 && tary >= 0 && tary <= HEIGHT - 1) { ability_right[tarx][tary] = ability_right[x][y] + intrnd(24) - 12; if (ability_right[tarx][tary] < 0) ability_right[tarx][tary] = 0; if (ability_right[tarx][tary] > 255) ability_right[tarx][tary] = 255; } } } if (tick % REPORTTICK == 0) { printf("%6d\t%5f\t%5f\n", tick, getaverage_left(), getaverage_right()); } #ifdef DRAWBMP if (tick % DRAWTICK == 0) { sprintf(filename, "%05d.bmp", tick); drawbmp(filename); } #endif tick++; } return 0; }