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 16 22:25:19 2014 +0000
Child:
1:9e88d16ab21e
Commit message:
LifeRules apply the Conway game of life rules to a LifeMap defined in an array.

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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LifeRules.cpp	Wed Apr 16 22:25:19 2014 +0000
@@ -0,0 +1,153 @@
+
+#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)
+{
+    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!");
+}
+
+void Life::setbit(int x, int y, Life::ValueOfLife b, int otherframe)
+{
+    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));
+    
+    if (animation == monochrome)
+        b = (ValueOfLife)(b & 1);
+    if (x >= 0 && x <= maxX && y >= 0 && y <= maxY) {
+        uint8_t maskBitsOfInterest = ((1 << (1 << animation)) -1) << (bitIndex);
+        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);
+    }
+}
+
+Life::ValueOfLife Life::getbit(int x, int y, int otherframe)
+{
+    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);
+        uint8_t curCell = pLifeMap[byteIndex] & maskBitsOfInterest;
+        ValueOfLife b = (ValueOfLife)(curCell >> (bitIndex));
+        if (animation == monochrome)
+            b = (ValueOfLife)(b & 1);
+        return b;
+    } else {
+        ERR(" Out of Bounds value (%d, %d)\r\n", x, y);
+        return dead;
+    }
+}
+
+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 f = 0; f < 2; f++) {
+                setbit(x,y, dead, f);
+            }
+        }
+    }
+}
+
+// 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 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
+                total++;
+            }
+        }
+    }
+    if (getbit(x,y)) { // minus middle of 9 cells (current cell) if it is alive
+        total--;
+    }
+    return total;
+}
+
+void Life::GenerationStep(void)
+{
+    INFO("Generation Step");
+    for (int i = 0; i <= maxX; i++) { //loop through cells
+        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
+        }
+    }
+    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)
+                setbit(x,y, living);
+        }
+    }
+}
+
+void Life::CycleOfLife(int x, int y, int neighbors)
+{
+    ValueOfLife currently = getbit(x,y);
+    
+    if ((neighbors < 2) || (neighbors > 3)) {
+        if (currently == living)
+            setbit(x,y, dying, 1);
+        else
+            setbit(x,y, dead, 1);
+    }
+    if (neighbors == 3) {
+        if (currently == living)
+            setbit(x,y, living, 1);
+        else
+            setbit(x,y, birthing, 1);
+    }
+    if (neighbors == 2) {
+        setbit(x,y, currently, 1);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LifeRules.h	Wed Apr 16 22:25:19 2014 +0000
@@ -0,0 +1,101 @@
+
+#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
+//#endif
+
+
+class Life {
+public:
+    typedef enum {
+        monochrome,     ///< life cycle is dead or living
+        color           ///< life cycle is dying to dead and birthing to living
+    } Animation;
+    
+    typedef enum {
+        dead,
+        living,
+        dying,
+        birthing
+    } ValueOfLife;
+
+    /// constructor for the life 
+    ///
+    /// @param w is the width of the life map
+    /// @param h is the height of the life map
+    ///
+    Life(int w, int h, Animation animate = color);
+
+    /// 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
+    /// 
+    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
+    /// @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
+    /// @returns the number of neighbors
+    /// 
+    int CountNeighbors(int x, int y);
+    
+    /// Destroy all life - typically at startup.
+    ///
+    void DestroyAllLife(void);
+
+    /// Step life forward by one generation
+    ///
+    void GenerationStep(void);
+    
+    /// Update the life-cycle.
+    ///
+    /// This method applies partial step to a life cycle.
+    /// It does this when the Life is set up for color mode
+    /// where you can then see birthing and dying cells as
+    /// a result of this method.
+    ///
+    void UpdateLifeCycle(void);
+
+    /// Apply the cycle of life to a cell.
+    ///
+    /// Based on the number of neighbors, and the current
+    /// 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.
+    ///
+    void CycleOfLife(int x, int y, int neighbors);
+
+private:
+    int LIFE_W;
+    int LIFE_H;
+    int maxX;   // set max element
+    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!
+    uint8_t * pLifeMap;
+};
+    
\ No newline at end of file