Conway's game of life applied to the mbed and an RA8875 LCD.

Dependencies:   LifeRules mbed RA8875

Inspired by a forum discussion on the mbed site, this version was scaled to support up to a 480x272 display - in a monochrome mode, or at a lower resolution in color (the color shows simple animation for birthing and dying cells).

Leveraging the LifeRules class, the game can be easily adapted to other displays - whether monochrome or color.

By default, this version allocates memory from the Ethernet ram banks, so avoids the memory limitations of some designs.

It should be simple to adapt it to any display - color or b&w, high or low resolution.

main.cpp

Committer:
WiredHome
Date:
2014-04-23
Revision:
2:f4aece10ba62
Parent:
0:d268818ad088
Child:
3:1139b132f983

File content as of revision 2:f4aece10ba62:

// game of life implementation inspired by this forum thread
// http://mbed.org/forum/helloworld/topic/4822/
// 
#include "mbed.h"
#include "RA8875.h"
#include "LifeRules.h"

// Define the life-map size
#define LIFE_W 300
#define LIFE_H 200
#define LIFE_C Life::color

// Define the screen size
#define SCREEN_W 480
#define SCREEN_H 272

extern "C" void mbed_reset();

//#define DEBUG "main"
// ...
// 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(LIFE_W, LIFE_H, LIFE_C);

// NOT USING p21, but the mbed lib v82 does not work if NC is part
// of a constructor. See thread http://mbed.org/forum/bugs-suggestions/topic/4859/
RA8875 lcd(p5, p6, p7, p12, p21, "tft");

// Where on screen do we locate it?
#define LIFE_OFFSET_X (SCREEN_W - LIFE_W)
#define LIFE_OFFSET_Y (SCREEN_H - LIFE_H)



unsigned char imgbuffer[3*LIFE_W];
FILE *img;

LocalFileSystem local("local"); //file system
Serial pc(USBTX,USBRX); // for debugging


int msDelay = 1000;     //delay between frames

void CheckForUserInteraction(void);
void genrand(); //random start
void genglidergun(); //glider gun
void genglider(); //glider
void genship1(); //light weight ship
void genBMP(); //from image
void gentest();
void genBlinker();

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();
}

int main()
{
    pc.baud(460800);    // I like a snappy terminal, so crank it up!
    pc.printf("\r\nConway's Game of Life - Build " __DATE__ " " __TIME__ "\r\n");

    lcd.frequency(5000000);
    lcd.puts("Welcome to Conway's Game of Life\r\n\r\n");
    INFO("Destroy all life");
    life.DestroyAllLife();
    INFO("Life destroyed");

    //pc.printf("choice value = %d\n\r",choice); //for debugging
    lcd.puts("Please Select Starting Configuration:\r\n");
    lcd.puts(" 0 = Glider     1 = Random\r\n"
             " 2 = Ship       3 = Glider Gun\r\n"
             " 4 = BMP image  5 = Blinker\r\n"
             " t = test\r\n"
             " r = reset\r\n"
             );
    
    // Frame the life map
    lcd.rect(LIFE_OFFSET_X-1,LIFE_OFFSET_Y-1,LIFE_OFFSET_X+LIFE_W,LIFE_OFFSET_Y+LIFE_H,Red);
    lcd.foreground(Blue);
    int choice = pc.getc();

    switch ( choice ) {
        case '0':
            lcd.puts(">Glider!\r\n");
            genglider();
            break;
        case '1':
            lcd.puts(">Random!\r\n");
            genrand();
            break;
        case '2':
            lcd.puts(">Ship!\r\n");
            genship1();
            break;
        case '3':
            lcd.puts(">Glider Gun!\r\n");
            genglidergun();
            break;
        case '4':
            lcd.puts(">BMP image!\r\n");
            genBMP();
            break;
        case '5':
            lcd.puts(">Blinker!\r\n");
            genBlinker();
            break;
        case 't':
            lcd.puts(">test!\r\n");
            gentest();
            break;
    }
    lcd.puts("\r\nChange Speed:\r\n"
             " +   = faster\r\n"
             " -   = slower\r\n"
             " 0-9 = updates/sec\r\n"
             );

    ScreenUpdate();
    wait(1);
    
    while(1) {
        CheckForUserInteraction();
        if (msDelay >= 0) {
            static uint16_t toggle = 0;
            
            if ((++toggle & 1) == 0) {
                life.GenerationStep();
            } else {
                life.UpdateLifeCycle();
            }
            ScreenUpdate();
            wait_ms(msDelay);
        }
    }
}

void CheckForUserInteraction(void)
{
    while (pc.readable()) {
        int c = pc.getc();
        if (c == '+' && msDelay >= 0)
            msDelay -= 10;
        else if (c == '-' && msDelay < 1000)
            msDelay += 10;
        else if (c >= '0' && c <= '9') {
            if (c == '0')
                msDelay = -1;
            else
                msDelay = 10 * ((1000 / (c - '0'))/10);
        } else if (c == 'r')
            mbed_reset();
        lcd.locate(49, 0);
        if (msDelay < 0) {
            msDelay = -10;
            lcd.printf("Paused      ");
        } else {
            lcd.printf("Delay %4d", msDelay);
        }
    }
}


void genBlinker()
{
    life.setbit(1,1, Life::living);
    life.setbit(1,2, Life::living);
    life.setbit(1,3, Life::living);
}

void gentest()
{
    // Point
    life.setbit(6,3, Life::living);
    // Block
    life.setbit(1,1, Life::living);
    life.setbit(1,2, Life::living);
    life.setbit(2,1, Life::living);
    life.setbit(2,2, Life::living);
    // Beehive
    life.setbit(6,1, Life::living);
    life.setbit(7,1, Life::living);
    life.setbit(5,2, Life::living);
    life.setbit(8,2, Life::living);
    life.setbit(6,3, Life::living);
    life.setbit(7,3, Life::living);
    // Blinker
    life.setbit(11,2, Life::living);
    life.setbit(12,2, Life::living);
    life.setbit(13,2, Life::living);
    // Glider
    //  x
    //   x
    // xxx
    life.setbit(2,10, Life::living);
    life.setbit(3,11, Life::living);
    life.setbit(1,12, Life::living);
    life.setbit(2,12, Life::living);
    life.setbit(3,12, Life::living);    
}


void genrand()
{
    for (int i = 0; i < LIFE_W; i++) { //loop through each cell
        for (int j = 0; j < LIFE_H; j++) {
            if (rand() & 1) { //50% chance
                life.setbit(i,j, Life::living);
            }
        }
    }
}

void genBMP()
{
    for (int i = 0; i < LIFE_W; i++) {
        img = fopen("/local/TESTIM~1.BMP", "rb");
        fseek (img , 54 + (LIFE_W*i*3) , SEEK_SET);
        fread(imgbuffer, (LIFE_W*3), 1, img);
        fclose(img);
        for (int j = 0; j < LIFE_H; j++) {
            int red = imgbuffer[j*3];
            if (red == 0) {
                life.setbit(j, LIFE_H - 1 - i, Life::living);
            }
        }
    }
}


void genglider()   //set certain pixels
{
    life.setbit(0,0, Life::living);
    life.setbit(1,1, Life::living);
    life.setbit(1,2, Life::living);
    life.setbit(2,0, Life::living);
    life.setbit(2,1, Life::living);    
}

void genglidergun()   //set certain pixels
{
    life.setbit(0,7, Life::living); //gun
    life.setbit(0,8, Life::living);
    life.setbit(1,7, Life::living);
    life.setbit(1,8, Life::living);
    life.setbit(8,8, Life::living);
    life.setbit(8,9, Life::living);
    life.setbit(9,7, Life::living);
    life.setbit(9,9, Life::living);
    life.setbit(10,7, Life::living);
    life.setbit(10,8, Life::living);
    life.setbit(16,9, Life::living);
    life.setbit(16,10, Life::living);
    life.setbit(16,11, Life::living);
    life.setbit(17,9, Life::living);
    life.setbit(18,10, Life::living);
    life.setbit(22,6, Life::living);
    life.setbit(22,7, Life::living);
    life.setbit(23,5, Life::living);
    life.setbit(23,7, Life::living);
    life.setbit(24,5, Life::living);
    life.setbit(24,6, Life::living);
    life.setbit(24,17, Life::living);
    life.setbit(24,18, Life::living);
    life.setbit(25,17, Life::living);
    life.setbit(25,19, Life::living);
    life.setbit(26,17, Life::living);
    life.setbit(34,5, Life::living);
    life.setbit(34,6, Life::living);
    life.setbit(35,5, Life::living);
    life.setbit(35,6, Life::living);
    life.setbit(35,12, Life::living);
    life.setbit(35,13, Life::living);
    life.setbit(35,14, Life::living);
    life.setbit(36,12, Life::living);
    life.setbit(37,13, Life::living);

    life.setbit(50,38, Life::living); //eater
    life.setbit(51,38, Life::living);
    life.setbit(50,39, Life::living);
    life.setbit(52,39, Life::living);
    life.setbit(52,40, Life::living);
    life.setbit(52,41, Life::living);
    life.setbit(53,41, Life::living);
}

void genship1()   //set certain pixels
{
    life.setbit(10,10, Life::living);
    life.setbit(13,10, Life::living);
    life.setbit(14,11, Life::living);
    life.setbit(10,12, Life::living);
    life.setbit(14,12, Life::living);
    life.setbit(11,13, Life::living);
    life.setbit(12,13, Life::living);
    life.setbit(13,13, Life::living);
    life.setbit(14,13, Life::living);
}