となりオセロ

今回は、「となりオセロ」の解法プログラムを取り上げます。「となりオセロ」のルールは、1人で遊べるようにオセロを元に私が考えてみました。 パズルとしても多少は面白いところがあると思いますので、解法プログラムを理解するためにも遊んでみてください。 「となりオセロ」のルールは、次のようなものです。

      
    
    
    
      
      
スタート図       完成図       失敗図の一例

「となりオセロ」パズルは、左上に黒石が1つ置かれた盤面でスタートします。 スタート図では、左上の黒石の周囲の3ヶ所のみ石を置くことができます。 石の少ない間は石を置く毎に、石を置ける場所は増えていきます。すべての石を置き終わったときに、 すべて黒石であれば完成ということになります。(なお、パズルを改良 (?) して白石も混じった完成図も採用していますが、ここではすべてが黒石になる完成図のみを扱います。)

全手順探索

「となりオセロ」のルールはちょっとした思いつきですが、 何はともあれ正解がないことにはパズルが成立しないことになります。 また、正解がある場合にも、どの程度の正解率があるのか知りたいところです。 そこで、まずは最初に、総当たりの全手順探索のプログラムを作って調べてみました。

 1:  use strict;
 2:  my (%round, %board, $all, $black); my $start = "11"; my $n = 3;
 3:
 4:  foreach my $i (1 .. $n) {      # %round の作成
 5:    foreach my $j (1 .. $n) {
 6:      my @round = ();
 7:      foreach my $k (($i > 1 && $i < $n) ? ($i - 1, $i, $i + 1) : $i == 1 ? ($i, $i + 1) : ($i - 1, $i)) {
 8:        foreach my $l (($j > 1 && $j < $n) ? ($j - 1, $j, $j + 1) : $j == 1 ? ($j, $j + 1) : ($j - 1, $j)) {
 9:          next if $k == $i and $l == $j;    # 自分自身のマス目を除外
10:          push @round, $k . $l;
11:        }
12:      }
13:      $round{"$i$j"} = [@round];
14:    }
15:  }
16:
17:  foreach my $i (1 .. $n) {      # 石のない状態で %board を作成
18:    foreach my $j (1 .. $n) {
19:      my $key = $i . $j;
20:      $board{$key} = [0];
21:      my $k = ($i > 1 && $i < $n) ? 2 : 1;
22:      my $l = ($j > 1 && $j < $n) ? 2 : 1;
23:      push @{$board{$key}}, $k + $l;
24:      push @{$board{$key}}, scalar(@{$round{$key}});
25:    }
26:  }
27:
28:  $board{$start}->[0] = 1;       # 11 に石を置く
29:  foreach my $pos (@{$round{$start}}) {
30:    --$board{$pos}->[2];
31:    if ("$pos$start" =~ /(.).\1/) {
32:      --$board{$pos}->[1];
33:    }
34:  }
35:
36:  search();
37:
38:  sub search {
39:    my @next_pos = grep { !$board{$_}->[0] and $board{$_}->[2] < @{$round{$_}} } keys %board;
40:    foreach my $pos (@next_pos) {
41:      $board{$pos}->[0] = 1;    # 石を置く
42:      foreach my $round (@{$round{$pos}}) {
43:        --$board{$round}->[2];
44:        if ("$pos$round" =~ /(.).\1/) {
45:          --$board{$round}->[1];
46:          $board{$round}->[0] = 3 - $board{$round}->[0] if $board{$round}->[0];
47:        }
48:      }
49:
50:      if (@next_pos == 1) {     # 盤面が埋まった
51:        ++$all;
52:        ++$black unless grep { $board{$_}->[0] == 2 } keys %board;
53:      } else {                  # 次の石を置くための再帰呼び出し
54:        search();
55:      }
56:
57:      $board{$pos}->[0] = 0;    # 石を取り除く
58:      foreach my $round (@{$round{$pos}}) {
59:        ++$board{$round}->[2];
60:        if ("$pos$round" =~ /(.).\1/) {
61:          ++$board{$round}->[1];
62:          $board{$round}->[0] = 3 - $board{$round}->[0] if $board{$round}->[0];
63:        }
64:      }
65:    }
66:  }
67:
68:  print "all: $all,  black: $black\n";
   Line 4 〜 15: 周囲のマス目の数を保持する %round を作成する
%round のキーはマス目番号で、値は無名配列の中に収めた周囲のマス目番号で 11 => [12, 21, 22] のようなデータ構造なります。%round はプログラム中に変化することなく、問い合わせに応じて周囲のマス目数 (スカラーコンテキスト) や周囲のマス目のリスト (リストコンテキスト) を返したりします。

   Line 17 〜 26: %board を作成 (本文中で詳述)

   Line 28 〜 34: 11 に石を置いてスタート図を作る (本文中で詳述)

   Line 36: search() サブルーチンを呼び出す

   Line 39: 石を置けるマス目を検出する
石を置けるのは、当然ですが石を置いていない (!$board{$pos}->[0]) ことと、周囲に石が置いてあること ($board{$pos}->[2] < @{$round{$pos}}) の2つが条件になります。この2つの条件を満たすマス目を検出して、配列 @next_pos に格納します。

   Line 41 〜 48 (57 〜 64): 該当のマス目に石を置く (除去する)
この2つのコードは、隣接するタテとヨコのマス目に置かれている石を反転する以外は Line 28 〜 34 のコードと同じです。石が置かれているとその値は1または 2 になっているので、3 からその値を引くと石が反転されたことになります。

   Line 50 〜 55: 再帰呼び出しの停止と再帰呼び出し
盤面が石で埋まったら、$all をインクリメントして全手順数の数え上げて、黒のみの石で埋まったときの $black のインクリメントで正解手順数の数え上げをします。盤面が石で埋まっていない場合は、 次の石を置くために search() を再帰呼び出しを行います。

   Line 68: 結果を報告する

プログラムの最初の方で、「となりオセロ」の盤面を作成します。盤面には2桁の番号を付け、 左上がマス目が 11 になります (下図、4 x 4 盤面)。盤面データは、ハッシュ %board で管理します。%board のキーがマス目の番号を表し、値には無名配列の中に3つのデータが格納されます。 3つのデータはそれぞれ、マス目の石の状態、隣接するタテとヨコの空きマス目の数、 隣接する周囲の空きマス目の数、となります。冒頭での盤面の作成は石のない状態で作成するので、%board の初期データは下図の右側のようになります。

11121314
21222324
31323334
41424344
    
%board = (11 => [0, 2, 3], 12 => [0, 3, 5], 13 => [0, 3, 5], 14 => [0, 2, 3],
          21 => [0, 3, 5], 22 => [0, 4, 8], 23 => [0, 4, 8], 24 => [0, 3, 5],
          31 => [0, 3, 5], 32 => [0, 4, 8], 33 => [0, 4, 8], 34 => [0, 3, 5],
          41 => [0, 2, 3], 42 => [0, 3, 5], 43 => [0, 3, 5], 44 => [0, 2, 3]);

無名配列の最初の「マス目の石の状態」は、0 が石が置かれていないことを意味し、 1が黒石であることを意味し、2 が白石であることを意味しています。石が置かれていない 0 の状態では、タテまたはヨコに石が置かれても変化しません。石は黒石として置かれるので、 そこで始めて1になります。その後は、タテまたはヨコに石が置かれると1と 2 の間で値がトグル (1の場合は 2 に、2 の場合は1になること) します。

2番目の「隣接するタテとヨコのの空きマス目の数」は、 隣接するタテまたはヨコに石が置かれたときに減らされます。逆の言い方をすると、 マス目に石を置いたときは、隣接するタテとヨコのデータを更新することになります。 3番目の「隣接する周囲の空きマス目の数」は2番目のデータとほぼ同様で、 タテとヨコに石を置いたときだけでなく斜めに隣接するマス目に置かれたときもデータを更新される点が異なります。

「となりオセロ」パズルは、11 のマス目に石を置いた状態からスタートします。11 に石を置くと %board は、初期値から太字で示したデータが変更されます。

121314
21222324
31323334
41424344
    
%board = (11 => [1, 2, 3], 12 => [0, 2, 4], 13 => [0, 3, 5], 14 => [0, 2, 3],
          21 => [0, 2, 4], 22 => [0, 4, 7], 23 => [0, 4, 8], 24 => [0, 3, 5],
          31 => [0, 3, 5], 32 => [0, 4, 8], 33 => [0, 4, 8], 34 => [0, 3, 5],
          41 => [0, 2, 3], 42 => [0, 3, 5], 43 => [0, 3, 5], 44 => [0, 2, 3]);

2つ目からの石は、search() サブルーチンを呼び出して置いて行きます。 2つ目の石は、石が置いてなくかつ周囲に石の置いてある 12, 21, 23 の内の1つに置くことになります。仮に 12 に石を置いたとすると %board のデータは、次のように変更されます。

1314
21222324
31323334
41424344
    
%board = (11 => [2, 1, 2], 12 => [1, 2, 4], 13 => [0, 2, 4], 14 => [0, 2, 3],
          21 => [0, 2, 3], 22 => [0, 3, 6], 23 => [0, 4, 7], 24 => [0, 3, 5],
          31 => [0, 3, 5], 32 => [0, 4, 8], 33 => [0, 4, 8], 34 => [0, 3, 5],
          41 => [0, 2, 3], 42 => [0, 3, 5], 43 => [0, 3, 5], 44 => [0, 2, 3]);

再帰呼び出し型の search() サブルーチンで次々に石を置いていくと、 総当たりの全手順探索ができます。盤面にすべて石が置かれたときに、 全手順数と盤面がすべて黒石になる正解手順数を数えていきます。ちなみに、盤面がすべて黒石になったときの %board のデータは、1番目がすべて1に、2番目と3番目がすべて 0 になります (下図)。なお、白石がある場合は、1番目のデータが 2 になるので簡単に判別できます。

    
%board = (11 => [1, 0, 0], 12 => [1, 0, 0], 13 => [1, 0, 0], 14 => [1, 0, 0],
          21 => [1, 0, 0], 22 => [1, 0, 0], 23 => [1, 0, 0], 24 => [1, 0, 0],
          31 => [1, 0, 0], 32 => [1, 0, 0], 33 => [1, 0, 0], 34 => [1, 0, 0],
          41 => [1, 0, 0], 42 => [1, 0, 0], 43 => [1, 0, 0], 44 => [1, 0, 0]);

全手順探索プログラムを私のパソコン (PentiumM 1.8GHz, 2GB RAM) で実行した結果は、次のようになりました。3 x 3 盤面は短時間で実行できることは分かっていましたが、4 x 4 盤面はどのくらい時間がかかるのか分かりませんでした。そこで、あらかじめ盤面に 4, 5 個の石を置いておいて、どのくらい時間がかかるか見て全体の時間を推測しました。 結果的に、約1カ月かかって実行を終了しました。

 盤面        時間           全手順    正解手順  正解率
3 x 3          1s            9,240         304   3.29%
4 x 4  2,593,202s   19,157,271,840   5,813,496   0.03%
      (30d0h20m2s)

正解率は、3 x 3 盤面では 3.29%、4 x 4 盤面では 0.03% となりました。 この正解率では、ランダムにただ石を置いて行くだけでは正解できないことを示しています。 多少なりとも考える要素があるということで、パズルとして楽しめるものと思います。

正解手順探索

正解手順探索のプログラムは、前節の全手順探索プログラムが出発点になります。 全手順探索プログラムから、不正解の手順を除外します。また、正解手順にも対称解や手順前後解もたくさんあります。 そのような解も、除外していきます。

  1  use strict;
  2  my (%round, %board, %hist, $black); my $start = "11"; my $n = 4;
  3
  4  foreach my $i (1 .. $n) {
  5    foreach my $j (1 .. $n) {
  6      my @round = ();
  7      foreach my $k (($i > 1 && $i < $n) ? ($i - 1, $i, $i + 1) : $i == 1 ? ($i, $i + 1) : ($i - 1, $i)) {
  8        foreach my $l (($j > 1 && $j < $n) ? ($j - 1, $j, $j + 1) : $j == 1 ? ($j, $j + 1) : ($j - 1, $j)) {
  9          next if $k == $i and $l == $j;
 10          push @round, $k . $l;
 11        }
 12      }
 13      $round{"$i$j"} = [@round];
 14    }
 15  }
 16
 17  foreach my $i (1 .. $n) {
 18    foreach my $j (1 .. $n) {
 19      my $key = $i . $j;
 20      $board{$key} = [0];
 21      my $k = ($i > 1 && $i < $n) ? 2 : 1;
 22      my $l = ($j > 1 && $j < $n) ? 2 : 1;
 23      push @{$board{$key}}, $k + $l;
 24      push @{$board{$key}}, scalar(@{$round{$key}});
 25    }
 26  }
 27
 28  $board{$start}->[0] = 1;
 29  foreach my $pos (@{$round{$start}}) {
 30    --$board{$pos}->[2];
 31    if ("$pos$start" =~ /(.).\1/) {
 32      --$board{$pos}->[1];
 33    }
 34  }
 35
 36  search([11, join('', map { $board{$_}->[0] } sort keys %board)]);
 37
 38  sub search {
 39    my $ref = shift;
 40    my @next_pos = grep { !$board{$_}->[0] and $board{$_}->[2] < @{$round{$_}}
 41                          and $board{$_}->[1] % 2 == 0 and $board{$_}->[1] } keys %board;
 42    my @stone = grep { $board{$_}->[0] } keys %board;
 43
 44    my $diag = 1;    # 対角線対称マス目の除外
 45    foreach my $pos (@stone) {
 46      my $d_pos = reverse $pos;
 47      next if $pos == $d_pos;
 48      if ($board{$pos}->[0] != $board{$d_pos}->[0]) { $diag = 0; last; }
 49    }
 50    if ($diag) {
 51      @next_pos = grep { substr($_, 0, 1) <= substr($_, 1, 1) } @next_pos;
 52    }
 53
 54    if (@stone >= $n) {    # 逆対角線対称マス目のの除外
 55      my $gaid = 1;
 56      foreach my $pos (@stone) {
 57        my $g_pos = ($n + 1 - substr($pos, 1, 1)) . ($n + 1 - substr($pos, 0, 1));
 58        next if $pos == $g_pos;
 59        if ($board{$pos}->[0] != $board{$g_pos}->[0]) { $gaid = 0; last; }
 60      }
 61      if ($gaid) {
 62        @next_pos = grep { substr($_, 0, 1) + substr($_, 1, 1) <= $n + 1 } @next_pos;
 63      }
 64    }
 65
 66    foreach my $pos (@next_pos) {
 67      $board{$pos}->[0] = 1;
 68      foreach my $round (@{$round{$pos}}) {
 69        --$board{$round}->[2];
 70        if ("$pos$round" =~ /(.).\1/) {
 71          --$board{$round}->[1];
 72          $board{$round}->[0] = 3 - $board{$round}->[0] if $board{$round}->[0];
 73        }
 74      }
 75
 76      my $patt = join '', map { $board{$_}->[0] } sort keys %board;
 77      my $stone = @stone + 1;
 78      unless (grep /$patt/, @{$hist{$stone}}) {
 79        push @{$hist{$stone}}, $patt;
 80        if (grep { !$board{$_}->[0] and $board{$_}->[1] } keys %board) {
 81          search([$pos, $patt, $ref]);
 82        } else {
 83          display([$pos, $patt, $ref]);
 84          ++$black;
 85        }
 86      }
 87
 88      $board{$pos}->[0] = 0;
 89      foreach my $round (@{$round{$pos}}) {
 90        ++$board{$round}->[2];
 91        if ("$pos$round" =~ /(.).\1/) {
 92          ++$board{$round}->[1];
 93          $board{$round}->[0] = 3 - $board{$round}->[0] if $board{$round}->[0];
 94        }
 95      }
 96    }
 97  }
 98
 99  sub display {    # 正解手順の表示
100    my $tmp_ref = shift;
101    my @work;
102    while ($tmp_ref) {
103      my ($pos, $board);
104      ($pos, $board, $tmp_ref) = @$tmp_ref;
105      unshift @work, [sprintf("%-${n}s", $pos), $board =~ /.{$n}/g];
106    }
107
108    my @line;
109    foreach my $i (0 .. $n) {
110      push @line, join("  ", map { $_->[$i] } @work);
111    }
112    print "$_\n" foreach @line;
113    print "\n";
114  }
115
116  print "black: $black\n";

全手順探索プログラムで解説した部分は省略して、正解手順探索プログラムで追加した部分を主に解説します。

   Line 36, 81: search サブルーチンを呼び出す
全手順探索プログラムでは引数を渡しませんでしたが、 正解手順探索プログラムでは無名配列に格納したデータを渡します。search サブルーチンの起動時には置き石マス目番号と盤面の石の配置の2つのデータですが、 再帰呼び出しでは末尾に直前の手数の置き石のデータを格納した無名配列のリファレンスが追加されます。
スタート図:        ['11', '1000000000000000']
2つ目の置き石時:  ['12', '2100000000000000', 上の配列のリファレンス]
3つ目の置き石時:  ['13', '2210000000000000', 上の配列のリファレンス]
.....

上のデータ構造は、正解手順を表示するために使います。正解手順が得られたときに、 リファレンスをスタート図まで辿ると正解手順を表示するためのデータを取得できます。

   Line 44 〜 52: 対角線対称マス目を除外 (本文で詳述)

   Line 54 〜 64: 逆対角線対称マス目を除外 (本文で詳述)

   Line 101 〜 106: スタート図から完成図までの盤面を収集する
while ループが終了すると、スタータ図から完成図までの盤面が @work 配列に収集されます。@work の各要素は、無名配列に入れられた盤面です。無名配列のデータは、スタート図を例にすると ['11  ', '1000', '0000', '0000', '0000'] のようになっています。 無名配列の各要素はそれぞれ、置いた石のマス目番号、盤面の1列目、2列目、3列目、 4列目となり、連続した行に表示すると見やすい盤面となります。

   Line 108 〜 112: 正解手順表示用の各行を作成し表示する
正解手順の表示では、見やすいようにスタート図から完成図までを1列に並べます。@work の無名配列から同じ添字の要素を抽出して、スペース2つで繋いで各行を作成し配列 @line に格納します。 @line は、foreach ループを使って出力します。文書の末尾に載せている 3 x 3 と 4 x 4 盤面の正解手順は、このコードによって出力したものです。

正解手順探索のプログラムは、前節の全手順探索のプログラムに1つ1つコードを 追加しながら作っています。プログラムの説明は、4 x 4 盤面を使って行っていきます。4 x 4 盤面の次のデータが出発点となり、その遷移を見ていくことにします。

  1. 奇数マス目除外  時間: 730s(12m10s)  正解手順数: 5,813,496
    下の図は 11, 22, 33, 44 と石を置いたところで、14 と 41 以外のマス目に石を置くことができます。 石を置くことができるマス目に書かれた数字は、タテとヨコに隣接する空きマス目の数です。 隣接する空きマス目の数が偶数の場合は最終的に黒石に、奇数の場合は最終的に白石になります。 奇数のマス目を除外すると、不正解手順を除外することができます。

        
    13 
    123
    321
     31
        
    my @next_pos = grep { !$board{$_}->[0] and $board{$_}->[2] < @{$round{$_}}
                          and $board{$_}->[1] % 2 == 0 } keys %board;

    配列 @next_pos は、次に石を置くことのできる空きマス目のリストです。全手順探索では 12, 13, 21, 23, 24, 31, 32, 34, 42, 43 の 10 個ですが、太字のコードを追加すると奇数のマス目は除外されて 23 と 32 の2ヶ所だけ残ることになります。

    奇数マス目を石を置く対象から外すと、大幅に時間が短縮されました。4 x 4 盤面で正解率が 0.03% ですので、期待したのですがその通りとなりました。これで不正解手順は生成されなくなりましたので、 以降は正解手順数のみ表示することにします。全手順探索は繰り返し実行することは簡単にはできませんが、 これで何度も実行できるくらいの時間になりました。

  2. 対角線対称除外  時間: 228s(3m48s)  正解手順数: 1,246,202
    盤面の石の配置が 11, 22, 33, 44 の対角線に対して対称になっている場合は、11, 22, 33, 44 の対角線を含む上半分 (下図の水色のマス目) だけに石を置くようにします。11 のみに石が置いてあるスタート図も対角線対称で、石を置くことができる12, 22, 21 の内 21 は石を置く対象からは外されます。 下図の石の配置になるには、12 → 21 の順序のみに限定され、21 → 12 の順序は排除されます。 また、3つの石が置かれている下の図も対角線対称で、水色のマス目が石を置ける対象になります。 実際には周囲に石のある 13, 22, 23 のみに石を置けて、31, 32 は石を置けないことになります。

        
      
        
         
         
        
    my $diag = 1;
    foreach my $pos (@stone) {
      my $d_pos = reverse $pos;
      next if $pos == $d_pos;
      if ($board{$pos}->[0] != $board{$d_pos}->[0]) { $diag = 0; last; }
    }
    if ($diag) {
      @next_pos = grep { substr($_, 0, 1) <= substr($_, 1, 1) } @next_pos;
    }
  3. 逆対角線対称除外  時間: 213s(3m33s)  正解手順数: 952,572
    逆対角線対象とは、14, 23, 32, 41 の対角線に対して対称になっているものをいいます。 逆対角線対称になっているのは、対角線対称よりもはるかに少なくなります。 逆対角線対称になる最小の置き石は、スタート図から 22, 33, 44 と石を置いた場合 (対角線対称でもある) です。逆対角線対称も、対角線対称と同じように逆対角線を含む上半分のマス目が石を置ける対象になります。

  4. 閉所マス目の除外  時間: 0.5s  正解手順数: 1,007
    盤面に石を置いていく過程で、隣接するタテとヨコにすべて石が置かれたマス目が出現します。 最小で4個のマス目を置いたときに、閉所マス目が出現することがあります (下図)。 この閉所マス目は、石を置かなくても最終的な結果に影響がなく石を置くことを省略できます。 閉所マス目に石を置かずに完成図とすると、4 〜 6 箇所のマス目に石を置かなくても済みます。 閉所マス目を省略した完成図は、ランダムの順序で空きマス目に石を置くことができ、 すべて石を置くと盤面が黒石のみの図となります。

        
     
       
        
        
        
    my @next_pos = grep { !$board{$_}->[0] and $board{$_}->[2] < @{$round{$_}}
                          and $board{$_}->[1] % 2 == 0 and $board{$_}->[1] } keys %board;
    ...
    
        if (grep { !$board{$_}->[0] and $board{$_}->[1] } keys %board) {
          search(...);

    閉所マス目を除外するコードは、少し離れている2ヶ所を修正します。配列 @next_pos から閉所マス目を除外するコードを追加し、それにともない閉所マス目のみになったときには search() 呼び出しを停止するコードの追加です。盤面の空きマス目が閉所マス目のみになった場合は、 その時点で完成したことにします。次の図は、閉所マス目に石を置かない完成図の1例です。

        
     
     
     
      

    上の図では、末尾の5手を省略することができます。対角線対称、逆対角線対称がなければ、 最大で 5! が正解手順数から除外されることになります。また、1つ上の図の4個の置き石の例では、 5個目以降に常に置き石の対象になるのを除外しています。閉所マス目を除外するコードを追加すると、 正解手順数は劇的に減少し、時間も1秒未満になりました。

  5. 手順前後除外  時間: 0.1s  正解手順数: 10
    最後に追加したのは、手順前後を除外するコードです。下の図では、対角線対称の 32, 43 を除いた 23, 34, 44 が次に石を置けるマス目です。次に連続して 23 と 34 に石を置く場合に 23 → 34 と 34 → 23 の2つの順序は、手順前後による同一図になります。また、23 → 44 と 44 → 23 も同じです。

        
       
       
       
        
        
    my $patt = join '', map { $board{$_}->[0] } sort keys %board;
    my $stone = @stone + 1;
    unless (grep /$patt/, @{$hist{$stone}}) {
      push @{$hist{$stone}}, $patt;
      ...
    }

    手順前後を除外すると、正解手順数は 10 になり、時間はほぼ瞬時に終わるようになりました。 何をもって正解手順数とするのかは難しいのですが、全手順探索の正解手順数 5,813,496 から対角線対象、逆対角線対象、閉所マス目、手順前後を除外した 10 を 4 x 4 盤面の正解手順数とすることにしました。なお、3 x 3 盤面では正解手順数が 2 に、5 x 5 盤面では時間が 416 秒かかり正解手順数が 203 になりました。

以上説明してきたほかに正解手順探索では、正解手順を表示するコードを追加しています。 正解手順が得られたら、その都度 display サブルーチンを呼び出しています。 最初の行に置いた石のマス目番号を表示し、次の行からは盤面の石の配置を表示しています。3 x 3 盤面では、次の2つの正解手順が表示されます。

11   12   21   22   23   32 
100  210  110  120  120  120
000  000  100  210  221  211
000  000  000  000  000  010

11   22   13   31   33 
100  100  101  101  101
000  010  010  010  010
000  000  000  100  101

3 x 3 盤面の手順は、最初に 12 に置くか 22 に置くかの選択肢があるだけで、 その後の手順は1つの手順に集約されることになります。2つのうちの最初の正解手順の3手目以降を、 詳細に見てみることにしましょう。

最初の手順は、3手目以降に複数の選択肢がない絶対的な手順になります。 2つ目の正解手順は、3手目に 13 か 33 に置くことになりどちらに置いたとしても、 その後2つの石を置くと完成図以外には到達しません。手順前後が成立するだけで、 解としては1つになります。3 x 3 盤面での正解手順が2つであるのは、 妥当なものとして受け入れることができるものと思います。4 x 4 盤面では、次の 10 の正解手順が表示されます。

11     12     13     21     22     23     24     31     32     33     42     44
1000   2100   2210   1210   1110   1120   1120   1120   1120   1120   1120   1120
0000   0000   0000   1000   2100   2210   2221   1221   1121   1111   1111   1111
0000   0000   0000   0000   0000   0000   0000   1000   2100   2210   2110   2110
0000   0000   0000   0000   0000   0000   0000   0000   0000   0000   0100   0101

11     12     13     21     22     23     24     31     32     34     42     43
1000   2100   2210   1210   1110   1120   1120   1120   1120   1120   1120   1120
0000   0000   0000   1000   2100   2210   2221   1221   1121   1122   1122   1122
0000   0000   0000   0000   0000   0000   0000   1000   2100   2101   2201   2201
0000   0000   0000   0000   0000   0000   0000   0000   0000   0000   0100   0210

11     12     13     21     22     23     24     31     34     33     43     42
1000   2100   2210   1210   1110   1120   1120   1120   1120   1120   1120   1120
0000   0000   0000   1000   2100   2210   2221   1221   1222   1212   1212   1212
0000   0000   0000   0000   0000   0000   0000   1000   1001   1012   1022   1022
0000   0000   0000   0000   0000   0000   0000   0000   0000   0000   0010   0120

11     12     13     21     22     23     24     34     33     32     41     43
1000   2100   2210   1210   1110   1120   1120   1120   1120   1120   1120   1120
0000   0000   0000   1000   2100   2210   2221   2222   2212   2112   2112   2112
0000   0000   0000   0000   0000   0000   0000   0001   0012   0122   0122   0112
0000   0000   0000   0000   0000   0000   0000   0000   0000   0000   1000   1010

11     12     13     21     22     31     33     34     24     43     42
1000   2100   2210   1210   1110   1110   1110   1110   1110   1110   1110
0000   0000   0000   1000   2100   1100   1100   1100   1101   1101   1101
0000   0000   0000   0000   0000   1000   1010   1021   1022   1012   1012
0000   0000   0000   0000   0000   0000   0000   0000   0000   0010   0120

11     12     13     21     22     33     32     34     24     41     43
1000   2100   2210   1210   1110   1110   1110   1110   1110   1110   1110
0000   0000   0000   1000   2100   2100   2200   2200   2201   2201   2201
0000   0000   0000   0000   0000   0010   0120   0111   0112   0112   0122
0000   0000   0000   0000   0000   0000   0000   0000   0000   1000   1010

11     12     21     22     33     23     14     31     34     43     42
1000   2100   1100   1200   1200   1200   1201   1201   1201   1201   1201
0000   0000   1000   2100   2100   2210   2210   1210   1210   1210   1210
0000   0000   0000   0000   0010   0020   0020   1020   1011   1021   1021
0000   0000   0000   0000   0000   0000   0000   0000   0000   0010   0120

11     12     21     22     33     23     14     32     34     41     43
1000   2100   1100   1200   1200   1200   1201   1201   1201   1201   1201
0000   0000   1000   2100   2100   2210   2210   2110   2110   2110   2110
0000   0000   0000   0000   0010   0020   0020   0110   0121   0121   0111
0000   0000   0000   0000   0000   0000   0000   0000   0000   1000   1010

11     12     21     23     14     32     33     34     41     43
1000   2100   1100   1100   1101   1101   1101   1101   1101   1101
0000   0000   1000   1010   1010   1010   1020   1020   1020   1020
0000   0000   0000   0000   0000   0100   0210   0221   0221   0211
0000   0000   0000   0000   0000   0000   0000   0000   1000   1010

11     22     33     23     13     24     32     31     42     44  
1000   1000   1000   1000   1010   1010   1010   1010   1010   1010
0000   0100   0100   0210   0220   0211   0111   0111   0111   0111
0000   0000   0010   0020   0020   0020   0110   1210   1110   1110
0000   0000   0000   0000   0000   0000   0000   0000   0100   0101

(2010/08/15)

TopPage