2011年7月15日

EGGXを用いたライフゲーム処理系の作成

EGGXを使ってライフゲームの処理系を作る。

以下は、グローバル変数が多い、読みにくいコード。状態が遷移した回数だけgif画像を出力する(要ImageMagick)。

LIM回状態が遷移するか、状態が変わらなかったとき、終了する。ダブルバッファリングなどの洒落た機能は入れて無いので、画面がチラチラするかもしれない。
#include <stdio.h>
#include <string.h>
#include <eggx.h>
#define EGGX_BLACK 0
#define EGGX_WHITE 1

/* 最大100*100マスまで許容する */
#define MAX_SIZE 100

#define CELL_WIDTH 20
#define CELL_HEIGHT 20

#define DEAD 0
#define LIVE 1

/* 最大1000回まで状態を遷移させる */
#define LIM 1000

/* 状態遷移間隔(ミリ秒) */
#define DELAY 200

int win;
int w, h;
int width, height;
int board[MAX_SIZE][MAX_SIZE];
int prev[MAX_SIZE][MAX_SIZE];

void init(void);
int next_state(int y, int x);
void change_cells(void);
void draw_cell(int y, int x);
void draw_cells(void);

void init(void)
{
    int x, y;

    memset(prev, -1, sizeof(prev));
    scanf("%d %d", &w, &h);
    width = w*CELL_WIDTH;
    height = h*CELL_HEIGHT;
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            scanf("%d", &board[y][x]);
}

int next_state(int y, int x)
{
    int i, live;
    int dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
    int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 };

    live = 0;
    for (i = 0; i < 8; i++) {
        int ty = y+dy[i], tx = x+dx[i];
        if (!(0 <= ty && ty < h))   continue;
        if (!(0 <= tx && tx < w))   continue;
        if (board[ty][tx] == LIVE)  live++;
    }

    switch (live) {
        case 2:     return board[y][x];
        case 3:     return LIVE;
        default:    return DEAD;
    }
}

void change_cells(void)
{
    int y, x;
    int buf[MAX_SIZE][MAX_SIZE];

    memset(buf, 0, sizeof(buf));
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            buf[y][x] = next_state(y, x);
    memcpy(board, buf, sizeof(buf));
}

void draw_cell(int y, int x)
{
    int kind;
    int px, py;

    kind = board[y][x];
    newpen(win, kind==DEAD?EGGX_WHITE:EGGX_BLACK);
    px = x*CELL_WIDTH;
    py = (h-1-y)*CELL_HEIGHT;
    fillrect(win, px, py, CELL_WIDTH, CELL_HEIGHT);
}

void draw_cells(void)
{
    int y, x;
    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            if (board[y][x] != prev[y][x])
                draw_cell(y, x);

    newpen(win, EGGX_BLACK);
    drawrect(win, 0, 0, width-1, height-1);
}

int main(void)
{
    int i;

    init();
    win = gopen(width, height);
    for (i = 0; i < LIM; i++) {
        if (memcmp(board, prev, sizeof(board))==0)
            break;

        winname(win, "turn %d", i);
        draw_cells();
        saveimg(win, 0, 0, 0, width-1, height-1, "convert", 16, "img%d.gif", 1000+i);

        memcpy(prev, board, sizeof(board));
        change_cells();
        msleep(DELAY);
    }
    gclose(win);

    return 0;
}

入力例と、それに対応する出力された画像をまとめたgif動画。入力のはじめ一行の二値は、それぞれ幅と高さを意味する。それに続くデータは、0なら死、1なら生を表す。
10 10
0 1 0 0 0 0 0 0 0 0
0 0 1 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