Snippets

sironekotoro ライフゲーム(オブジェクト指向使わない

Created by sironekotoro
use strict;
use warnings;

use Clone qw(clone)
    ; # 次世代の座標と、その値を格納したリファレンスを $next_generation からコピーするために必要
use List::Util qw/sum/;

my @cells = (
    [ 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 1, 1, 1, 0 ],
    [ 0, 1, 1, 1, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0 ],
);

my $MIN = 0;
my $MAX = $#cells;

# セルのの中身チェック
check_cells(@cells);

my $coordinate
    ; # x,yによる座標とその生死状態を格納するリファレンス

# マップを作る
# x座標とy座標に交差する所の状態を格納する二次元配列
for my $y ( 0 .. $#cells ) {
    for my $x ( 0 .. $#{ $cells[$y] } ) {
        $coordinate->[$x][$y] = $cells[$y][$x];
    }
}

# 世代交代を何回繰り返すかのメインループ
for ( 1 .. 8 ) {
    $coordinate = change_generation($coordinate);
    for my $y ( 0 .. $MAX ) {
        for my $x ( 0 .. $MAX ) {
            print $coordinate->[$x][$y];
        }
        print "\n";
    }
    print "\n";
}

# 世代交代
sub change_generation {
    my $coordinate = shift;

    my $next_generation;    # 次の世代保存用変数

    for my $y ( 0 .. $MAX ) {
        for my $x ( 0 .. $MAX ) {

            my $judge = judge( $x, $y, $coordinate );

            if ( $judge eq 'birth' || $judge eq 'survival' ) {
                $next_generation->[$x][$y] = 1;
            }
            else {
                $next_generation->[$x][$y] = 0;
            }
        }
    }

# リファレンスは別の変数に代入しても参照先は同じになる。
# このため、リファレンスをちゃんと複製できるCloneモジュールを利用する
    return clone($next_generation);
}

# セルの状態を調べて、次の世代での生死を決める
sub judge {
    my ( $x, $y ) = @_;

    my $dead_or_alive = $coordinate->[$x][$y];

    # セルの周辺の状態を調べる
    my $sum = window_state( $x, $y, $coordinate );

# 死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代が誕生する。
    if ( $dead_or_alive == 0 && $sum == 3 ) {
        return 'birth';
    }

    # 生きているセルの条件
    if ( $dead_or_alive == 1 ) {

# 生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する。
        if ( $sum <= 1 ) {
            return 'depopulation';
        }

# 生きているセルに隣接する生きたセルが2つか3つならば、次の世代でも生存する。
        elsif ( $sum == 2 || $sum == 3 ) {
            return 'survival';
        }

# 生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する。
        elsif ( $sum >= 4 ) {
            return 'overcrowding';
        }
    }
    else {
        return 'dead';
    }

}

# セルの周辺にある8つのセルの状態を調べる
sub window_state {
    my ( $x, $y, $coordinate ) = @_;

    my %window = (
        upper_left  => upper_left( $x, $y, $coordinate ),     # 左斜め上
        upper       => upper( $x, $y, $coordinate ),          # 上
        upper_right => upper_right( $x, $y, $coordinate ),    # 右斜め上
        left        => left( $x, $y, $coordinate ),           # 左
        right       => right( $x, $y, $coordinate ),          # 右
        lower_left  => lower_left( $x, $y, $coordinate ),     # 左斜め下
        lower       => lower( $x, $y, $coordinate ),          # 下
        lower_right => lower_right( $x, $y, $coordinate ),    # 右斜め下
    );
    return sum( values %window );

}

# 左斜め上
sub upper_left {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x - 1 < $MIN || $y - 1 < $MIN ) {
        return 0;
    }
    return $coordinate->[ $x - 1 ][ $y - 1 ] || 0;
}

# 上
sub upper {
    my ( $x, $y, $coordinate ) = @_;
    if ( $y - 1 < $MIN ) {
        return 0;
    }
    return $coordinate->[$x][ $y - 1 ] || 0;

}

# 右斜め上
sub upper_right {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x + 1 > $MAX || $y - 1 < $MIN ) {
        return 0;
    }
    return $coordinate->[ $x + 1 ][ $y - 1 ] || 0;
}

# 左
sub left {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x - 1 < $MIN ) {
        return 0;
    }
    return $coordinate->[ $x - 1 ][$y] || 0;
}

# 右
sub right {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x + 1 > $MAX ) {
        return 0;
    }
    return $coordinate->[ $x + 1 ][$y] || 0;

}

# 左斜め下
sub lower_left {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x - 1 < $MIN || $y + 1 > $MAX ) {
        return 0;
    }
    return $coordinate->[ $x - 1 ][ $y + 1 ] || 0;
}

# 下
sub lower {
    my ( $x, $y, $coordinate ) = @_;
    if ( $y + 1 > $MAX ) {
        return 0;
    }
    return $coordinate->[$x][ $y + 1 ] || 0;
}

# 右斜め下
sub lower_right {
    my ( $x, $y, $coordinate ) = @_;
    if ( $x + 1 > $MAX || $y + 1 > $MAX ) {
        return 0;
    }
    return $coordinate->[ $x + 1 ][ $y + 1 ] || 0;
}

# セルのチェック
sub check_cells {
    @cells = @_;

    # 0,1 以外が入っていないかチェック
    for my $row (@cells) {
        for my $col (@$row) {
            if ( $col ne '0' && $col ne '1' ) {
                die("$col is invalid. only use 0 or 1");
            }
        }
    }

    # 縦軸と同じ数の横軸があるか
    for my $index ( 0 .. $#cells ) {
        my $state = $cells[$index]->[$#cells];
        if ( $state ne '0' && $state ne '1' ) {
            die("not equal vartical count - holizontal count");
        }

        # 範囲外のセルがないかチェック
        my $outside_state = $cells[$index]->[ $#cells + 1 ];
        if ($outside_state) {
            die("outside cell include");
        }
    }

}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.