Conway's game of life - derived from another project, turned into a c++ class, and scaled to support up to a 480x272 display, or a lower resolution color display.
Revision 1:9e88d16ab21e, committed 2014-04-23
- Comitter:
- WiredHome
- Date:
- Wed Apr 23 11:40:10 2014 +0000
- Parent:
- 0:d43dc92ae767
- Child:
- 2:c11005ab38db
- Commit message:
- Updated documentation.
Changed in this revision
LifeRules.cpp | Show annotated file Show diff for this revision Revisions of this file |
LifeRules.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/LifeRules.cpp Wed Apr 16 22:25:19 2014 +0000 +++ b/LifeRules.cpp Wed Apr 23 11:40:10 2014 +0000 @@ -1,31 +1,33 @@ - +// Conway's game of life +// +// http://en.wikipedia.org/wiki/Conway's_Game_of_Life +// +// A simple cellular automation where pixels are born, live, and die +// following a simple set of rules. The oddest of the rules is that +// it takes 3 to create birth of a new life... +// +// Early on (back in the 70's), computers were not accessible to +// most, and yet somehow this simulation flourished. I was one of +// many that used pencil and graph paper, chalk on a board and +// even pennies on paper to simulate each cell. It was so much +// more animated when I had access to an 8008 micro and a "TV +// typewriter" interface. +// #include "LifeRules.h" - -//#define DEBUG "LIFE" -// ... -// INFO("Stuff to show %d", var); // new-line is automatically appended -// -#if (defined(DEBUG) && !defined(TARGET_LPC11U24)) -#define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); -#else -#define INFO(x, ...) -#define WARN(x, ...) -#define ERR(x, ...) -#endif - -Life::Life(int w, int h, Life::Animation animate) +// Hopefully, the least obvious thing in this code, once you understand +// the basic rules of "life" is that access to the pLifeMap, which +// takes the 32K Ethernet buffer on the LPC1768. Of course, your +// application must not be using either AHBSRAM0 or AHBSRAM1 for this +// to succeed. +Life::Life(int w, int h, Life::Animation animate, uint8_t * pMap) { LIFE_W = w; LIFE_H = h; maxX = w - 1; maxY = h - 1; animation = animate; - pLifeMap = (uint8_t *)(0x2007C000); // not using either AHBSRAM0 or AHBSRAM1 - if ((w * h * (1 << animate) * FRAMES_PER_BIT / 8) > (BYTE_RSVD_RAM)) - error("ERROR, you've overallocated memory and are doomed!"); + pLifeMap = pMap; } void Life::setbit(int x, int y, Life::ValueOfLife b, int otherframe) @@ -40,8 +42,6 @@ uint8_t curCell = pLifeMap[byteIndex] & ~maskBitsOfInterest; uint8_t mask = b << (bitIndex); pLifeMap[byteIndex] = curCell | mask; - //INFO("set(%d,%2d,%2d,%d) => by %3d, bi %1d, cur %02X of %02X mask %02X [Val: %02X]", x,y,b,frame,byteIndex,bitIndex, curCell, - // maskBitsOfInterest, mask, pLifeMap[byteIndex]); } else { ERR(" Out of Bounds value (%d, %d)\r\n", x, y); } @@ -51,8 +51,6 @@ { unsigned long byteIndex = ((x + LIFE_W * y) * (1 << animation) * FRAMES_PER_BIT) / 8; unsigned long bitIndex = (((x + LIFE_W * y) * (1 << animation) * FRAMES_PER_BIT) % 8) + (((frame + otherframe) & 1) * (1 << animation)); - //INFO("getbit(%d,%2d,%2d) => byteIndex: %2d, bitIndex: %2d, [Val: %02X]", - // frame,x,y, byteIndex,bitIndex, pLifeMap[byteIndex]); if (x >= 0 && x <= maxX && y >= 0 && y <= maxY) { uint8_t maskBitsOfInterest = ((1 << (1 << animation)) -1) << (bitIndex); @@ -69,9 +67,8 @@ void Life::DestroyAllLife(void) { - INFO("Destroying all life"); for (int y = 0; y <= maxY; y++) { - for (int x = 0; x <= maxX; x++) { //loop through cells + for (int x = 0; x <= maxX; x++) { for (int f = 0; f < 2; f++) { setbit(x,y, dead, f); } @@ -79,24 +76,19 @@ } } -// Values: -// 0 = dead -// 1 = dying -// 2 = living -// 3 = birthing int Life::CountNeighbors(int x, int y) { int total = 0; - for (int k = -1; k <= 1; k++) { //loop through neighbours + for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { ValueOfLife lifeState = getbit((x+LIFE_W+k)%LIFE_W,(y+LIFE_H+l)%LIFE_H); - if (lifeState == living) { //if living neighbour is found increment total + if (lifeState == living) { total++; } } } - if (getbit(x,y)) { // minus middle of 9 cells (current cell) if it is alive + if (getbit(x,y)) { // minus the cell of interest if it is alive total--; } return total; @@ -104,25 +96,20 @@ void Life::GenerationStep(void) { - INFO("Generation Step"); - for (int i = 0; i <= maxX; i++) { //loop through cells + for (int i = 0; i <= maxX; i++) { for (int j = 0; j <= maxY; j++) { int total = CountNeighbors(i, j); - INFO("(%d,%d) has %d", i, j, total); - CycleOfLife(i, j, total); //check neighbors + CycleOfLife(i, j, total); } } frame = (frame + 1) & 1; } -// If dying, kill it. If birthing, give it life. void Life::UpdateLifeCycle(void) { - INFO("Update Life Cycle"); for (int x = 0; x <= maxX; x++) { //loop through cells for (int y = 0; y <= maxY; y++) { ValueOfLife currently = getbit(x,y); - INFO(" (%d,%d) = %d", x,y, currently); if (currently == dying) setbit(x,y, dead); else if (currently == birthing)
--- a/LifeRules.h Wed Apr 16 22:25:19 2014 +0000 +++ b/LifeRules.h Wed Apr 23 11:40:10 2014 +0000 @@ -1,10 +1,22 @@ - +/// Conway's game of life +/// +/// http://en.wikipedia.org/wiki/Conway's_Game_of_Life +/// +/// A simple cellular automation where pixels are born, live, and die +/// following a simple set of rules. The oddest of the rules is that +/// it takes 3 to create birth of a new life... +/// +/// Early on (back in the 70's), computers were not accessible to +/// most, and yet somehow this simulation flourished. I was one of +/// many that used pencil and graph paper, chalk on a board and +/// even pennies on paper to simulate each cell. It was so much +/// more animated when I had access to an 8008 micro and a "TV +/// typewriter" interface. +/// #include "mbed.h" // Defined and Derived values to check if it exceeds available memory -//#define BITS_PER_LIFE 2 /* 0, 1, 2, 3 provides a more colorful view of life */ #define FRAMES_PER_BIT 2 /* current and next life cycle */ -#define BYTE_RSVD_RAM 0x8000 //#if (LIFE_W * LIFE_H * BITS_PER_LIFE * FRAMES_PER_BIT / 8) > (BYTE_RSVD_RAM) //#error Bummer, can't make the lifemap that big @@ -27,33 +39,121 @@ /// constructor for the life /// - /// @param w is the width of the life map - /// @param h is the height of the life map + /// Constructor for the life object. The most important thing + /// when constructing life, is to have room for the occupants, + /// so take care that memory is actually available for the + /// life map on your system. + /// + /// The required memory in bits is: + /// + /// w * h * 2 * (animate == color) ? 2 : 1 + /// + /// The '2' is because it keeps a current generation map and + /// a next generation map. + /// + /// As an example: + /// 480 * 272 * 2 * 1 => 261120 bits => 32640 bytes + /// + /// Fragments from a sample application you might find in main.cpp + /// are shown here, this one for the RA8875 display graphics + /// library. (see http://mbed.org/components/RA8875-Based-Display/) /// - Life(int w, int h, Animation animate = color); + /// @code + /// #define SCREEN_W 480 + /// #define SCREEN_H 272 + /// Life life(LIFE_W, LIFE_H, Life::monochrome); + /// RA8875 lcd(p5, p6, p7, p12, NC, "tft"); + /// + /// // Where on screen do we locate it? + /// #define LIFE_OFFSET_X (SCREEN_W - LIFE_W) + /// #define LIFE_OFFSET_Y (SCREEN_H - LIFE_H) + /// + /// int main() + /// { + /// life.setbit(1,1, Life::living); + /// life.setbit(1,2, Life::living); + /// life.setbit(1,3, Life::living); + /// while(1) { + /// static uint16_t toggle = 0; + /// if ((++toggle & 1) == 0) { + /// life.GenerationStep(); + /// } else { + /// life.UpdateLifeCycle(); + /// } + /// ScreenUpdate(); + /// } + /// } + /// + /// void ScreenUpdate() + /// { + /// lcd.window(LIFE_OFFSET_X, LIFE_OFFSET_Y, LIFE_W, LIFE_H); + /// lcd._StartGraphicsStream(); + /// for (int j = 0; j < LIFE_H; j++) { + /// for (int i = 0; i < LIFE_W; i++) { + /// Life::ValueOfLife lifeState = life.getbit(i,j); + /// switch (lifeState) { + /// case Life::dead: + /// lcd._putp(Black); + /// break; + /// case Life::dying: + /// lcd._putp(RGB(64,0,0)); + /// break; + /// case Life::living: + /// lcd._putp(Charcoal); + /// break; + /// case Life::birthing: + /// lcd._putp(Blue); + /// break; + /// default: + /// lcd._putp(Orange); + /// ERR(" lifeState = %d\r\n", lifeState); + /// break; + /// } + /// } + /// } + /// lcd._EndGraphicsStream(); + /// lcd.WindowMax(); + /// } + /// @endcode + /// + /// @param[in] w is the width of the life map in pixels, commonly + /// chosen to match the display device capability. + /// @param[in] h is the height of the life map in pixels, commonly + /// chosen to match the display device capability. + /// @param[in] animate is an optional parameter that can be either + /// 'monochrome' or 'color' (the default), which determines + /// if each life cycle has one step (e.g. from life directly + /// to death) or two steps (e.g. life to dying to death). + /// @param[in] pMap is an optional pointer to a block of memory to + /// host the life map. If not specified, it will assume + /// the memory space of an LPC1768 where the 32K Ethernet + /// buffers are not being used (and take that space for the + /// life map). + /// + Life(int w, int h, Animation animate = color, uint8_t * pMap = (uint8_t *)(0x2007C000)); /// sets a life value in the frame at location x,y. /// - /// @param x is the x location in the life-map - /// @param y is the y location in the life-map - /// @param otherframe selects the next life-cycle of interest - /// @param b is the value of life to assign to that location + /// @param[in] x is the x location in the life-map + /// @param[in] y is the y location in the life-map + /// @param[in] otherframe selects the next life-cycle of interest + /// @param[in] b is the value of life to assign to that location /// void setbit(int x, int y, ValueOfLife b, int otherframe = 0); /// gets the life value from the specified location. /// - /// @param x is the x location in the life-map - /// @param y is the y location in the life-map - /// @param otherframe selects the next life-cycle of interest + /// @param[in] x is the x location in the life-map + /// @param[in] y is the y location in the life-map + /// @param[in] otherframe selects the next life-cycle of interest /// @returns the value of life at that location. /// ValueOfLife getbit(int x, int y, int otherframe = 0); /// Count and return the number of living neighbors. /// - /// @param x is the x location in the life-map - /// @param y is the y location in the life-map + /// @param[in] x is the x location in the life-map + /// @param[in] y is the y location in the life-map /// @returns the number of neighbors /// int CountNeighbors(int x, int y); @@ -81,21 +181,19 @@ /// state of a cell, this determines whether the cell /// is born, lives, or dies. /// - /// @param x is the x offset into the life map. - /// @param y is the y offset into the life map. - /// @param neighbors is the count of neighbors. + /// @param[in] x is the x offset into the life map. + /// @param[in] y is the y offset into the life map. + /// @param[in] neighbors is the count of neighbors. /// void CycleOfLife(int x, int y, int neighbors); private: - int LIFE_W; + int LIFE_W; // dimensions of the life map int LIFE_H; - int maxX; // set max element + int maxX; // set max element for ease of iterations int maxY; - int frame; // the current life cycle - int animation; - - // CAUTION: This gives a pointer to a 32K byte range which is all being used! + int frame; // the current life cycle + int animation; // mode - color or b&w uint8_t * pLifeMap; }; \ No newline at end of file