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

LifeRules.h

Committer:
WiredHome
Date:
2014-04-23
Revision:
3:860ae49fedb7
Parent:
2:c11005ab38db

File content as of revision 3:860ae49fedb7:

#include "mbed.h"

// Defined and Derived values to check if it exceeds available memory
#define FRAMES_PER_BIT 2    /* current and next life cycle */

//#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

/// 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.
///
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 
    ///
    /// 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/)
    ///
    /// @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[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[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[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);
    
    /// 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[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;     // dimensions of the life map
    int LIFE_H;
    int maxX;       // set max element for ease of iterations
    int maxY;
    int frame;      // the current life cycle
    int animation;  // mode - color or b&w
    uint8_t * pLifeMap;
};