#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>

typedef struct tagCell
{
    int alive; // 今の状態
    int life;  // 次の状態
    struct tagCell *around[9]; // 自分の周囲(自分含む)
} CELL;

static const char *CELL_CHAR[] = { "□", "■" };

#define HEIGHT (10)
#define WIDTH  (10)
static CELL cells[ HEIGHT ][ WIDTH ];

/* 初期パターン (0:死 /1:生) */
// お題サンプル(池になって終わり)
static const int graph_paturn1[ HEIGHT * WIDTH ] =
{
//         1   2   3   4   5   6   7   8   9  10 
/*  1 */   0,  1,  0,  0,  0,  0,  1,  1,  1,  0,
/*  2 */   0,  0,  0,  0,  1,  0,  0,  1,  1,  0,
/*  3 */   0,  0,  0,  1,  0,  0,  1,  0,  1,  0,
/*  4 */   1,  0,  1,  1,  0,  0,  1,  0,  0,  0,
/*  5 */   0,  1,  0,  0,  0,  0,  0,  0,  1,  0,
/*  6 */   1,  0,  0,  0,  1,  0,  1,  1,  0,  1,
/*  7 */   0,  1,  0,  0,  0,  0,  1,  0,  0,  0,
/*  8 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  1,
/*  9 */   1,  0,  0,  0,  0,  0,  1,  0,  0,  1,
/* 10 */   0,  0,  0,  0,  1,  1,  0,  0,  1,  0
};

// 基本パターン固定型
static const int graph_paturn2[ HEIGHT * WIDTH ] =
{
//         1   2   3   4   5   6   7   8   9  10 
/*  1 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/*  2 */   0,  1,  0,  0,  0,  0,  0,  1,  0,  0,
/*  3 */   0,  1,  0,  0,  0,  1,  0,  0,  1,  0,
/*  4 */   0,  1,  0,  0,  0,  1,  0,  0,  1,  0,
/*  5 */   0,  0,  0,  0,  0,  0,  1,  0,  0,  0,
/*  6 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/*  7 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/*  8 */   0,  0,  1,  0,  0,  0,  0,  0,  0,  0,
/*  9 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
/* 10 */   0,  0,  0,  0,  0,  0,  0,  0,  0,  0
};

inline void CLS(void){  printf("\x1b[2J"); }
inline void LOCATE(int x, int y){ printf("\x1b[%d;%dH", y, x); }
inline void STORE_LOCATE(void){ printf("\x1b[s"); }
inline void RESTORE_LOCATE(void){ printf("\x1b[u"); }

/* 指定した座標のセル取得 */
inline int getCell(CELL **out, int x, int y)
{
    assert(out != NULL );

    /* 座標の正規化 */
    y %= HEIGHT; if( y < 0 ){ y = HEIGHT + y; }
    x %= WIDTH;  if( x < 0 ){ x = WIDTH  + x; }

    /*  */
    *out = &cells[y][x];
    assert(*out != NULL );
    return 0;
}

/* 現在の状態から次世代の状態を設定する */
int set_next_life(struct tagCell *cell)
{
    int score;
    assert(cell != NULL );
    score = cell->around[0]->alive   + cell->around[1]->alive + cell->around[2]->alive
            + cell->around[3]->alive                          + cell->around[5]->alive
            + cell->around[6]->alive + cell->around[7]->alive + cell->around[8]->alive;
    switch( score )
    {
        case 2: // 維持
            break;
        case 3: // 誕生
            cell->life = 1;
            break;
        default: // 死亡
            cell->life = 0;
            break;
    }
    return 0;
}

/* 初期化いろいろ */
int initialize(void)
{
    static const int *graph = graph_paturn1; // 初期配置

    struct tagCell   *cell;

    /* スクリーンの初期化 */
    CLS(); LOCATE(1,3);

    /* セルの初期化 */
    memset( &cells, 0, sizeof(cells) );
    for( int h=0; h < HEIGHT; h ++ )
    {
        for( int w=0; w < WIDTH; w ++ )
        {
            cell = &cells[ h ][ w ];
            cell->alive = *graph++;
            cell->life  = cell->alive;
            /* 周囲セルの設定 */
            getCell(&cell->around[0], w-1, h-1);
            getCell(&cell->around[1], w  , h-1);
            getCell(&cell->around[2], w+1, h-1);
            getCell(&cell->around[3], w-1, h  );
            getCell(&cell->around[4], w  , h  ); //自分自身
            getCell(&cell->around[5], w+1, h  );
            getCell(&cell->around[6], w-1, h+1);
            getCell(&cell->around[7], w  , h+1);
            getCell(&cell->around[8], w+1, h+1);
        }
    }
    return 0;
}

/* 現在の状態を表示 */
int print_cells(void)
{
    struct tagCell *cell;
    for( int h=0; h < HEIGHT; h ++ )
    {
        for( int w=0; w < WIDTH; w ++ )
        {
            cell = &cells[ h ][ w ];
//          printf("[%c]", (cell->alive == 1)?'*':' ');
            printf("%s", CELL_CHAR[ cell->alive ] );
            cell->alive = cell->life; // 表示したので次世代の状態を反映する
        }
        printf("\n");
    }
    return 0;
}

/*  */
void play(int frame)
{
    struct timespec interval = 
    { 
        .tv_sec = 0,
        .tv_nsec = 125000000
    };

    STORE_LOCATE();
    for( int t=0; t<=frame || frame==-1; t++ )
    {
        RESTORE_LOCATE();
        /* 表示前に次の状態を計算 */
        for( int h=0; h < HEIGHT; h ++ )
        {
            for( int w=0; w < WIDTH; w ++ )
            {
                set_next_life( &cells[ h ][ w ] );
            }
        }

        /* 表示 */
        printf("t=%d\n", t);
        print_cells();

        /* ディレイ */
        nanosleep(&interval, NULL);
    }
}

int main(int argc, char *argv[])
{
    initialize();
    play( (argc>1)?atoi( argv[1] ):-1 );
    return 0;
}
