/***************************************************************************
 *                                                                         *
 * Module  : life.c                                                        *
 *                                                                         *
 * Purpose : Game of life for the GameBoy Development Kit                  *
 *                                                                         *
 * Version : 2, 17 jan 1998, uses charset, fullscreen, start array         *
 *           1, 16 dec 1997                                                *
 *                                                                         *
 * Author  : Luc Van den Borre ( noc.base.org )                            *
 *                                                                         * 
 **************************************************************************/

/****************************************************************************
 Includes
 */
#include <gb.h>

/****************************************************************************
 Local Defines
 */
/* These must be multiple of 2 */
#define ROWS    36
#define COLS    40

/****************************************************************************
 Local (static) Globals
 */
unsigned char civ1[ROWS][COLS], 
              civ2[ROWS][COLS];

unsigned char bkg_tiles[ROWS/2][COLS/2];

/* A period 30 glider gun: gc/k30 on http://www.cs.jhu.edu/~callahan/patterns/gunprev.html */
unsigned char Seed[ROWS][COLS] = 
{
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,
    0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,1,0,0,1,1,0,0,0,
    0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,1,1,0,0,0,
    0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

/* Generated by GBTD v0.5 */
unsigned char bkg_data[] =
{
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x90,0x60,0x60,0xF0,0x60,0xF0,0x90,0x60,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x09,0x06,0x06,0x0F,0x06,0x0F,0x09,0x06,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66,
  0x99,0x66,0x66,0xFF,0x66,0xFF,0x99,0x66
};

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Functions 

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 LifeBlit

 Copy from 2D array to screen

 On entry : 
 On exit  :
 */
void LifeBlit(unsigned char Arr[ROWS][COLS])
{
    UBYTE   i,j;
    UWORD   c;

    /* Convert to 4 bits per character */
    c = 0;
    for (i = 0; i != ROWS; i+=2)
    {
        for (j = 0; j != COLS; j+=2)
        {
            ((unsigned char *)bkg_tiles)[c] = Arr[i][j] 
                                            + 4*Arr[i+1][j]
                                            + 2*Arr[i][j+1] 
                                            + 8*Arr[i+1][j+1];
            c++;
        }
    }

    disable_interrupts();   
    set_bkg_tiles(0,0,20,18,(unsigned char *)bkg_tiles);
    enable_interrupts();
 }

/****************************************************************************
 LifeStep

 Next generation of cells in Old is calculated and put into New

 On entry :
 On exit  :
 */
void LifeStep(unsigned char Old[ROWS][COLS], 
              unsigned char New[ROWS][COLS])
{
    UBYTE   n,
            i, 
            j,
            ti1,
            tj1,
            ti2,
            tj2,
            np1m4,
            xend,
            yend,
            sum;
    UBYTE   xfrom[] = { 0,  ROWS-1, ROWS-1, 0       },
            yfrom[] = { 0,  0,      COLS-1, COLS-1  };
	BYTE    delta[] = { 1,  1,      -1,     -1      };

    /*  First do special case for the edges (uses modulo for wrap around) 
        I might as well optimize this for each edge separately, but the main idea was 
        to get those modulo's out of the main loop 
    */
    for ( n = 0 ; n != 4 ; n++)
    {    
        xend = xfrom[(n+1)%4];
        yend = yfrom[(n+1)%4];
        i = xfrom[n];
        j = yfrom[n];
        ti1 = (i + ROWS - 1) % ROWS;
        tj1 = (j + COLS - 1) % COLS;
        ti2 = (i + 1) % ROWS;
        tj2 = (j + 1) % COLS;

        do
        {               
            sum =   Old[ti1][tj1] + Old[ti1][j] + Old[ti1][tj2] +
                    Old[i][tj1] + Old[i][tj2] +
                    Old[ti2][tj1] + Old[ti2][j] + Old[ti2][tj2];

            switch(sum)
            {
                case 0: case 1: case 4: 
                case 5: case 6: case 7: case 8:
                {
                    New[i][j] = 0;
                    break;
                }
                case 2:
                {
                    New[i][j] = Old[i][j];
                    break;
                }
                case 3:
                {
                    New[i][j] = 1;
                    break;
                }
            }            

            if (i != xend)
            {
                i+=delta[n];
                ti1 = (i + ROWS - 1) % ROWS;
                ti2 = (i + 1) % ROWS;
            }
            if (j != yend)
            {
                j+=delta[n];
                tj1 = (j + COLS - 1) % COLS;                
                tj2 = (j + 1) % COLS;
            }
        }
        while ((i != xend) || (j != yend));
    }
    
    /* Inside of the field */
    for (i = 1; i != ROWS-1; i++)
    {
        ti1 = i - 1;
        ti2 = i + 1;

        for (j = 1; j != COLS-1; j++)
        {
            tj1 = j - 1;
            tj2 = j + 1;

            sum =   Old[ti1][tj1] + Old[ti1][j] + Old[ti1][tj2] +
                    Old[i][tj1] + Old[i][tj2] +
                    Old[ti2][tj1] + Old[ti2][j] + Old[ti2][tj2];

            switch(sum)
            {
                case 0: case 1: case 4:
                case 5: case 6: case 7: case 8:
                {
                    New[i][j] = 0;
                    break;
                }
                case 2:
                {
                    New[i][j] = Old[i][j];
                    break;
                }
                case 3:
                {
                    New[i][j] = 1;
                    break;
                }
            }
        }              
    }
}

/****************************************************************************
 LifeInit

 Init first generation

 On entry :
 On exit  :
 */
void LifeInit(void)
{    
    memcpy((void *)civ1, (void *)Seed, (UWORD)ROWS*COLS);
}

/****************************************************************************
 main

 On entry :
 On exit  :
 */
int main(void)
{
    disable_interrupts();
    DISPLAY_OFF;
    /* Set palettes */
    BGP_REG = OBP0_REG = OBP1_REG = 0xE4U;
    /* Initialize the new character set */
    set_bkg_data(0x00, 0x1f, bkg_data); 
    /* Background scroll registers */
    SCX_REG = 0;
    SCY_REG = 0;
    /* Window out of the way */
    WX_REG = MAXWNDPOSX;
    WY_REG = MAXWNDPOSY;
    /* Turn the screen on */
    LCDC_REG = 0xE7;
    enable_interrupts();
    
    LifeInit();
    LifeBlit(civ1);

    do
    {
        LifeStep(civ1, civ2);
        LifeBlit(civ2);
        LifeStep(civ2, civ1);
        LifeBlit(civ1);
    }
    while(TRUE);

    return 1;
}

