一次セルオートマトン

学類のシミュレーション物理の課題。
オートマトンを作れとか。


で、なぜか思いっきり作り込んでしまった。
getoptとかgdとか。
正直こんだけ作り込んでもただの課題。
無駄だ。


どうやら最近使い方覚えたので使いたかったらしい > 2時間前の俺
ということで2時間の成果物↓

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#ifdef USE_GETOPT
#include <getopt.h>
#endif /* USE_GETOPT */

#ifdef USE_GD
#include <gd.h>
#endif /* USE_GD */

#define DEFAULT_RULE 184
#define DEFAULT_DENSITY (RAND_MAX * 0.3)
#define DEFAULT_SEED 149
#define DEFAULT_LENGTH 32
#define DEFAULT_STEPS 16

#ifdef USE_GD
#define DEFAULT_OUTPUT "out.png"
#endif /* USE_GD */

#define BOOL int
#define TRUE (0==0)
#define FALSE (0!=0)

#ifdef USE_GETOPT
void usage( int argc, char** argv )
{
	printf( "使用法: %s [オプション]...\n", argv[0] );
	printf( "\n" );
	printf( "一次セルオートマトンを行います\n" );
	printf( "\n" );
	printf( "  -r, --rule=NUM             オートマトンルール[\"0x%X\"]\n", DEFAULT_RULE );
	printf( "  -d, --density=FLOAT        初期密度[\"%f\"]\n", DEFAULT_DENSITY / RAND_MAX );
	printf( "  -s, --seed=NUM             乱数のシード[\"%d\"]\n", DEFAULT_SEED );
	printf( "  -l, --length=NUM           オートマトン長[\"%d\"]\n", DEFAULT_LENGTH );
	printf( "  -g, --generation=NUM       試行回数[\"%d\"]\n", DEFAULT_STEPS );
#ifdef USE_GD
	printf( "  -o, --output=FILE          pngの出力先[\"%s\"]\n", DEFAULT_OUTPUT );
#endif /* USE_GD */
	printf( "  -h, --help                 このメッセージを表示する\n" );
	printf( "\n" );
}
#endif /* USE_GETOPT */

/* ループした配列アクセス */
int* nth( BOOL* buf, int index, int length )
{
	while( index < 0 ) {
		index += length;
	}
	while( index >= length ) {
		index -= length;
	}
	return &buf[index];
}

int main ( int argc, char** argv )
{
	int rule = DEFAULT_RULE;
	int density = DEFAULT_DENSITY;
	int seed = DEFAULT_SEED;
	int length = DEFAULT_LENGTH;
	int steps = DEFAULT_STEPS;
#ifdef USE_GD
	const char* output = DEFAULT_OUTPUT;
#endif /* USE_GD */
	
#ifdef USE_GETOPT
	while( 1 ) {
		static struct option longopts[] = {
			{ "rule", required_argument, NULL, 'r' },
			{ "density", required_argument, NULL, 'd' },
			{ "seed", required_argument, NULL, 's' },
			{ "length", required_argument, NULL, 'l' },
			{ "generation", required_argument, NULL, 'g' },
			{ "help", no_argument, NULL, 'h' },
#ifdef USE_GD
			{ "output", required_argument, NULL, 'o' },
#endif /* USE_GD */
			{ 0, 0, 0, 0 }
		};
#ifdef USE_GD
		int c = getopt_long( argc, argv, "r:d:s:l:g:ho:", longopts, NULL );
#else /* USE_GD */
		int c = getopt_long( argc, argv, "r:d:s:l:g:h", longopts, NULL );
#endif /* USE_GD */
		if( c == -1 ) {
			break;
		}
		switch( c ) {
			case 'r':
				rule = atoi( optarg );
				break;
			case 'd':
				density = RAND_MAX * atof( optarg );
				break;
			case 's':
				seed  = atoi( optarg );
				break;
			case 'l':
				length = atoi( optarg );
				break;
			case 'g':
				steps = atoi( optarg );
				break;
			case 'h':
				usage( argc, argv );
				exit( 0 );
#ifdef USE_GD
			case 'o':
				output = optarg;
				break;
#endif /* USE_GD */
			default:
				usage( argc, argv );
				exit( 1 );
		}
	}
#else /* USE_GETOPT */
	/* getoptが無い環境用の簡易引数処理 */
	/* ルール 密度 シード 長さ ステップ数 出力ファイル名 の順で指定 */
	/* オプションは省略可能で無い場合はDEFAULT_*が使われる */
	if( argc > 1 ) {
		rule = atoi( argv[1] );
	}
	if( argc > 2 ) {
		density = RAND_MAX * atof( argv[2] );
		assert( density >= 0 );
	}
	if( argc > 3 ) {
		seed = atoi( argv[3] );
		assert( seed > 0 );
	}
	if( argc > 4 ) {
		length = atoi( argv[4] );
		assert( length > 0 );
	}
	if( argc > 5 ) {
		steps = atoi( argv[5] );
		assert( steps > 0 );
	}
#ifdef USE_GD
	if( argc > 6 ) {
		output = argv[6];
	}
#endif /* USE_GD */
#endif /* USE_GETOPT */
	/* 引数処理ここまで */

	srand( seed );

	/* 表バッファと裏バッファを用意 */
	/* 表から裏へ結果を落とし込みswap */
	/* 表示は常にprimaryBuf */
	BOOL* primaryBuf = (BOOL*)calloc( length, sizeof( BOOL ) );	
	if( !primaryBuf ) {
		perror( NULL );
	}
	BOOL* secondaryBuf = (BOOL*)calloc( length, sizeof( BOOL ) );
	if( !secondaryBuf ) {
		perror( NULL );
	}
	BOOL* swap;
	
	/* 初期化 */
	int i;
	for( i = 0; i < length; i++ ) {
		*nth( primaryBuf, i, length ) = rand() < density;
	}


#ifdef USE_GD
	/* gdイメージを作成 */
	gdImagePtr im = gdImageCreate( length, steps );
	if( !im ) {
		perror( NULL );
		exit( 1 );
	}
	int white = gdImageColorAllocate( im, 0xff, 0xff, 0xff );
	int black = gdImageColorAllocate( im, 0x00, 0x00, 0x00 );
#endif /* USE_GD */

	/* セルオートマトンスタート */
	int t;
	for( t = 0; t < steps; t++ ) {
		/* primaryBufにセルオートマトンの結果を書いていく */
		for( i = 0; i < length; i++ ) {
			/* 表示 */
			printf( "%d", *nth( primaryBuf, i, length ) );
#ifdef USE_GD
			if( *nth( primaryBuf, i, length ) ) {
				gdImageSetPixel( im, i, t, black );
			} else {
				gdImageSetPixel( im, i, t, white );
			}
#endif /* USE_GD */
			/* 裏バッファへオートマトンの結果をいれる */
			int n = (( *nth( primaryBuf, i - 1, length ) ? 1 : 0 ) << 2) +
				(( *nth( primaryBuf, i    , length ) ? 1 : 0 ) << 1) +
				( *nth( primaryBuf, i + 1, length ) ? 1 : 0 );
			*nth( secondaryBuf, i, length ) = ( rule & 1 << n ) != 0;
		}
		printf( "\n" );

		/* スワップ */
		swap = primaryBuf;
		primaryBuf = secondaryBuf;
		secondaryBuf = swap;
	}
#ifdef USE_GD
	FILE* fp = fopen( output, "w" );
	if( !fp ) {
		perror( NULL );
		exit( 1 );
	}
	gdImagePng( im, fp );
	fclose( fp );
#endif /* USE_GD */
	return 0;
}

ifdef切り分けしてるのでlibgdやgetoptがない環境でもばっちこい。
オートマトンルール184は渋滞シミュレーションらしい。ほむ。


makefaileはこちら

CC=g++
CFLAGS=-Wall -DUSE_GETOPT -DUSE_GD
LDFLAGS=-lgd
SRCS=$(wildcard *.c)
BINS=$(subst .c,,$(SRCS))

.PHONY: clean

all: $(BINS)

clean:
	$(RM) $(BINS)

wildcardは授業とかeasyなプログラムをまとめてコンパイルする時乱用したり...。
wildcar→substの組み合わせは覚えておくと吉。かも。


あ、そうそう。
コピペして自分の課題として提出すんのは禁止ね。
まぁ、こんなの出そうとは思わないだろうけどね。

  • 8< - - - 切 - り - 取 - り - 線 - - - - -

2008/12/12 追記。
なんか後輩の子がまさにこの課題やってた。
懐かしいなとか思いつつ、
http://d.hatena.ne.jp/goth_wrist_cut/20071215/1197744772
に追記したことをこっちに書き忘れてたので追記。