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.

Dependents:   GameOfLife

Files at this revision

API Documentation at this revision

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