/* ----------------------------------------------------------------------- * * Life: A game program that emulates the growth of cells in a petri dish * * Author: Suzi Anvin * * Rules: Cells die if lonely or overcrowded. They only live if the 8 * surrounding squares have 2 or 3 other cells. Cells can be created * in any empty square that is adjacent to 3 other cells. * * Operation: (Version 1.1) User enters a string of text on several lines, * representing cells as 'x' and empty spaces as '.' This is * converted into an array of bits. "Life" then runs step by step * through the lifecycle of the cells, re-drawing the display each * time the user presses enter. The display ends when the user * types 'E' and then asks the user if they want to play again. * If the user's answer begins with 'N' the program ends. * Otherwise, it restarts. * * Changes to this version: Allows incomplete and even blank lines. * User types Q instead of [enter] to finish entering lines early. * * Potential changes for later versions (in no particular order): * 1) Allow user to add or remove cells from the array at any point. * 2) Change display to a graphical interface and accept mouse input * 3) Allow the user to choose an array size. * ----------------------------------------------------------------------- */ #include #include #include inline void set_bit (uint8_t array[], unsigned int bit) { array[bit/8] |= 1<<(bit%8); } inline void clr_bit (uint8_t array[], unsigned int bit) { array[bit/8] &= ~(1<<(bit%8)); } /* test_bit returns 1 if the bit is 1 and 0 if the bit is 0. */ inline int test_bit (const uint8_t array[], unsigned int bit) { return (array[bit/8]>>(bit%8)) & 1; } /* set_bmp_line takes a text line of x's and .'s. It assigns them to * one line of a bitmap array with x = 1 and . = 0 * * When passing the array to set_bmp_line, be sure to specify the line * number [in brackets]. * * set_bmp_line returns 0 if all bits are correctly assigned and 1 if * it does not recognize a character from the text line. */ int set_bmp_line (uint8_t array[], char line[]) { int i; /* counter */ clr_bit (array, 0); for (i=1; i <= 62; i++) { if (line[i-1] == '\n') break; switch (line[i-1]) { case '@': case 'x': set_bit (array, i); break; case ' ': case '.': clr_bit (array, i); break; default: return (1); } } for (; i<64; i++){ clr_bit (array, i); } return (0); } /* draw_bmp_line is a preliminary function to draw one line of the bitmap * seperately. Each line of the array is passed individually, and bits 1-38 * are drawn on the screen, with 1 = 'x' and 0 = '.'. the comand call needs to * specify the current line # [in brackets] to use the function. * * I'm told I'm "already using pointers" to pass in the 1 * dimensional array. That's fine, I'll wait til I have a better clue before * passing in both dimensions. In version 1.1 or so this function should * be updated to draw the whole bitmap. */ void draw_bmp_line (uint8_t array[]) { int i; /* counter(s) */ for (i=1; i<= 62; i++) { if (test_bit(array, i)) putchar('@'); else putchar(' '); } fputs("|\n", stdout); } /* debug_bits prints out the bits as the appear in the array. This tests that * they have been encoded correctly. */ void debug_bits (uint8_t array[]) { int i; for (i=0; i < 64; i++) { printf("%d", test_bit(array, i)); } putchar('\n'); } int main() { char line[100]; char y_n[10]; /* User inputs yes/no answer */ uint8_t bitmap[24][8]; /* two dimensional bitmap array */ uint8_t old_bitmap [24][8]; /* to copy array to in manipulation stage */ int i, j, k, l; /* counter(s) */ while (1) { /* loop until user quits */ fputs("\x1b[H\x1b[2J" "Welcome to the game of Life, version 1.0, by Suzi Anvin.\n" "Please enter your cells one line at a time, with 62\n" "characters per line as indicated by the line of ---'s.\n" "You may enter as many as 22 lines, or as few as 1 line.\n" "At any time, press 'Q' and [return] to stop entering lines.\n" "Use x to indicate a cell and a period to " "indicate an empty space.\n" "--------------------------------------------------------------\n", stdout); /* Initializing the bits in the bitmap. Lines [0] and [23] are set to * all 0's. Lines [1-22] take user input and are initialized with * set_bmp_line, or, if left blank, are set to all 0's. */ for (j=0; j < 8; j++) { /* initializing 0th line to 0 */ bitmap [0][j] = 0; } for (i=1; i<=22; i++) { /* initializing lines 1-22 with user input */ fputs("\x1b[K",stdout); fgets(line, sizeof(line), stdin); if (line[0] == 'q' || line[0] == 'Q') break; if (set_bmp_line(bitmap[i], line) == 1) { fputs("Error: Unknown character. Only x's and .'s please." "\x1b[1A\x1b[1G", stdout); --i; continue; } } for (; i < 24; i++) { for (j=0; j < 8; j++) { /* initializing remaining lines to 0 */ bitmap [i][j] = 0; } } fputs("\x1b[H\x1b[2J", stdout); for (i=1; i<=22; i++) { /* print out array as user drew it it */ draw_bmp_line(bitmap[i]); } fputs("[Enter]: Draw next step. E: End current simulation and restart.", stdout); fgets(y_n, sizeof(y_n), stdin); if (y_n [0] == 'E' || y_n [0] == 'e') continue; /*********************************************************************** * the following section goes through the user-space array bit by bit, * counts the cells around it, and then performs an action, regardless * of whether there is a living cell in the bit. The following rules * are applied: * 1) If 2 cells are in the surrounding 8 bits, the central bit remains * unchanged. (i.e. if it is living, it continues to live. If dead, * no new growth occurs * 2) If 3 cells are in the surrounding 8 bits, the central bit is set. * (i.e. if the bit is already living, it will be set and thus remain * unchanged. If the bit is dead, growth occurs and the bit is changed * from 0 to 1.) * 3) In all other cases (1 or 0 cells, 4 or more cells) the central bit * is cleared. (i.e. if the cell was already dead, the operation clears * the bit and it remains unchanged. If the cell was living, death * occurs and the bit is changed from 1 to 0. ) * * Note on counters: * i counts the row of the array for the central bit. * j counts the collumn of the array for the central bit. * k counts the living cells around the central bit. * l counts the number of cycles through the whole loop. *********************************************************************/ for (l = 0; l >= 0; ++l) { /* loop until break, count # of loops */ memcpy (old_bitmap, bitmap, sizeof(bitmap)); for (i=1; i<=22; i++) { for (j=1; j<=62; j++) { k = 0; /* start each loop with 0 cells counted */ k += test_bit (old_bitmap [i-1], (j-1)); /* cell 1 above*/ k += test_bit (old_bitmap [i-1], j); /* 2 */ k += test_bit (old_bitmap [i-1], (j+1)); /* 3 */ k += test_bit (old_bitmap [i], (j-1)); /* 4 */ k += test_bit (old_bitmap [i], (j+1)); /* 5 */ k += test_bit (old_bitmap [i+1], (j-1)); /* 6 */ k += test_bit (old_bitmap [i+1], j); /* 7 */ k += test_bit (old_bitmap [i+1], (j+1)); /* 8 */ switch (k) { case 2: break; /* central bit continues living or empty */ case 3: set_bit (bitmap[i], j); break; /* central bit continues to live or grows */ default: clr_bit (bitmap[i], j); break; /* central bit dies or never existed */ } } } fputs("\x1b[H", stdout); /* move back to top of screen */ for (i=1; i<=22; i++) { /* print out new array over old one */ draw_bmp_line(bitmap[i]); } printf("Press [Enter] to continue or 'E' to end this simulation." " %3d Cycles.\x1b[K", l+1); fgets(y_n, sizeof(y_n), stdin); if (y_n[0] == 'E' || y_n[0] == 'e') break; } fputs("Would you like to play again? (y/n)", stdout); fgets(y_n, sizeof(y_n), stdin); if (y_n[0] == 'n') break; } return (0); }