応用情報技術者試験 過去問 2016年(平成28年) 春期 午後 問3

ライフゲーム

ライフゲームとは,数学者コンウェイが考案した,生命の誕生,生存,死滅などを再現したシミュレーションゲームである。マス目状の盤上の各マスに生命が存在でき,そのマス自身及び隣接するマスの状態によって次世代の誕生,生存,死滅が決まる。その条件を表1に示す。

なお,隣接するマスには,斜め方向のマスも含む。また,生命が存在するマスを"生のマス",生命が存在しないマスを"死のマス"と呼ぶ。

表1 誕生,生存,死滅の条件
条件名 条件
誕生 死のマスに隣接する生のマスが三つならば,死のマスは次の世代では生のマスとなる。
(死のマスに隣接する生のマスが三つ以下又は四つ以上ならば,死のマスは次の世代でも死のマスである。)
生存 生のマスに隣接する生のマスが二つ又は三つならば,生のマスは次の世代でも生のマスである。
過疎 生のマスに隣接する生のマスが一つ以下ならば,生のマスは過疎によって次の世代では死のマスとなる。
過密 生のマスに隣接する生のマスが四つ以上ならば,生のマスは過密によって次の世代では死のマスとなる。

4×4マスのシミュレーション

4×4マスの盤上における第3世代までのシミュレーションを図1に示す。

4×4マスの盤上における第3世代までのシミュレーション
図1 4×4マスの盤上における第3世代までのシミュレーション

第1世代は,初期値として設定されたものである。例として,第1世代の2行目1列目のマスを考える。現在,このマスは死のマスである。このマスに隣接するマスを網掛けで示す。これら五つのマスの中に生のマスが三つある。これは表1の"誕生"の条件に該当するので,第2世代の2行目1列目のマスは生のマスになる。同様に,第1世代の各マスについて,そのマス自身及び隣接するマスの状態を確認することで第2世代が決まる。次の世代への状態の更新は,全てのマスについて同時に行われる。

盤上のマスのデータ構造

N×Nマスの盤上の状態を表現するデータ構造を考える。多次元配列が利用できないプログラム言語を考慮し,盤上の各マスの生死状態を管理するデータ構造として1次元配列mを用いる。配列mのデータ構造のイメージを図2に示す。

配列mのデータ構造のイメージ
図2 配列mのデータ構造のイメージ

配列mを次世代に更新するプログラム

使用する定数,配列及び関数を表2に,配列mを次世代に更新する関数updateを図3に示す。

なお,関数に配列を引数として渡すときの方式は参照渡しである。

表2 使用する定数,配列及び関数
名称 種類 説明
N 定数 盤の一辺の大きさ。3以上の整数が入る。
m 配列 N×Nマスの生死状態を管理する1次元配列。生の場合は1が,死の場合は0が入る。
temp 配列 配列mと同じ大きさの1次元配列。
copy(array1, array2) 関数 配列array1の全ての要素を配列array2にコピーする。
clear(array) 関数 配列arrayの全ての要素の値を0にする。
function update(m)
    copy(m, temp)      // 配列mを配列tempにコピーして退避する
    clear(m)           // 配列mの全ての要素の値を0にする
    for( iを1からN×Nまで1ずつ増やす )
    
        if( i-1がNで割り切れる )    ┐
            a ← 0                   │
            b ← 1                   │
        elseif( iが1がNで割り切れる ) ├ α
            a ← -1                  │
            b ← 0                   │
        else                        │
            a ← -1                  │
            b ← 1                   │
        endif                       ┘
        
        e ← 0
        for( yを-1から1まで1ずつ増やす )
            for( xをaからbまで1ずつ増やす )
                if( (yと0が等しくない) or (xと0が等しくない) )
                    c ← i + y×N + x
                    if( (cが1以上) and (cがN×N以下) )
                        if(  )
                            e ← e+1
                        endif
                    endif
                endif
            endfor
        endfor
        
        // 生死を判定する
        if(  )
            m[i] ← 1
        elseif( (temp[i]と1が等しい) and ((eと2が等しい) or (eと3が等しい)) )
            
        endif
        
    endfor
endfunction
図3 関数updateのプログラム

テストプログラム

図3のプログラムをテストするために,配列mに第1世代が与えられたときの第p世代が,机上で作成した正しい結果である配列rと等しいことを確認するプログラムを作成した。作成した関数shouldBeを図4に示す。ここで,pには2以上の整数が入る。

1: function shouldBe( m, p, r )
2:     for( iを1からpまで1ずつ増やす )
3:         update(m)
4:     endfor
5:     for( iを1からN×Nまで1ずつ増やす )
6:         if( m[i]とr[i]が等しくない )
7:             return false    // テスト失敗
8:         endif
9:     endfor
10:    return true           // テスト成功
11: endfunction
図4 関数shouldBeのプログラム

図3のプログラムが正しく動作する状態で図4のプログラムを実行したところ,テストが失敗した。原因を調査した結果,図4の行目に問題があることが判明したので,①プログラムを修正してテストを成功させることができた。

出典:平成28年度 春期 応用情報技術者試験 午後問題