再帰を用いて試行錯誤を漏れなく行うアルゴリズム。
class EightQueen def initialize @board = Array.new(8, false) @board.each_index do |i| @board[i] = Array.new(8, false) end end # (x,y)にクィーンがあるかどうかチェック def check( x, y ) # 左方向をチェック p = 0 while p < x return false if @board[p][y] p += 1 end # 左上方向をチェック p = x q = y while (p > 0) && (q > 0) return false if @board[p -= 1][q -= 1] end # 左下方向をチェック p = x q = y while p > 0 && q < 7 return false if @board[p -= 1][q += 1] end return true end def showBoard @board.each_index do |y| @board[y].each_index do |x| if @board[x][y] printf "Q " else printf ". " end end printf "¥n" end end def solve( x ) if x == 8 # 全ての列にクィーンをおけたら return true end showBoard puts "---" @board[x].each_index do |i| if check(x, i) # (x,i)にクィーンがおけたら実際に置く @board[x][i] = true if solve(x + 1) # 次の列以降が成功ならこの列も成功 return true else # 次の列以降が失敗ならクィーンを置き直す @board[x][i] = false end end end return false end end queen = EightQueen.new if queen.solve(0) queen.showBoard end
【冨倉メモ】
上記のスクリプトでは、開始の位置が(0,0)の場合は、解を発見できるけれど、それ以外の開始位置では正しい解が発見できない。僕の記述が悪いのかと思ったのだけれど、書籍の Java コードでも同様の問題が発生してしまっている。うーん。
キューを用いて実装することが一般的。
class Pattern attr_reader :hash, :patternFrom def initialize( hash, patternFrom ) # 今までに現れた局面を記録するクラス # このクラスの配列はキュー代わりにも使われる @hash = hash @patternFrom = patternFrom end end class SevenPuzzle def initialize @history = Array.new @queueBottom = 0 # キューの末尾位置 end # 局面と対応する数字を作り出す関数 def makeHash( pattern ) hash = 0 i = 0 while i < 8 hash |= ((pattern[7 - i].to_i) << (i * 4)).to_f i += 1 end return hash end # 数字から局面を復元する関数 def patternFromHash( pattern, hash ) i = 0 while i < 8 pattern[7 - i] = ((hash >> (i * 4)) & 0xf).to_s i += 1 end end def saveHistory( pattern, patternFrom ) hash = makeHash(pattern) i = 0 while i < @history.size if @history[i].hash == hash return end i += 1 end @history.push( Pattern.new(hash, patternFrom) ) end def solve7Puzzle() pattern = Array.new(8) @queueBottom = 0 solve = makeHash(["1","2","3","4","5","6","7","0"]) while @queueBottom != @history.size hash = @history[@queueBottom].hash # 解にたどり着いたら終了 return true if hash == solve patternFromHash(pattern, hash) blankPos = 0 while blankPos < 8 break if pattern[blankPos].to_i == 0 blankPos += 1 end if blankPos > 3 # 上から空いている場所へ移動 pattern[blankPos] = pattern[blankPos - 4] pattern[blankPos - 4] = 0 # 新しいパネル配置を保存した後、元の位置に戻す saveHistory(pattern, @queueBottom) patternFromHash(pattern, hash) end if blankPos < 4 # 下から空いている場所へ移動 pattern[blankPos] = pattern[blankPos + 4] pattern[blankPos + 4] = 0 saveHistory(pattern, @queueBottom) patternFromHash(pattern, hash) end if blankPos != 0 && blankPos != 4 # 左から空いている場所へ移動 pattern[blankPos] = pattern[blankPos -1] pattern[blankPos - 1] = 0 saveHistory(pattern, @queueBottom) patternFromHash(pattern, hash) end if blankPos != 3 && blankPos != 7 # 右から空いている場所へ移動 pattern[blankPos] = pattern[blankPos + 1] pattern[blankPos + 1] = 0 saveHistory(pattern, @queueBottom) patternFromHash(pattern, hash) end @queueBottom += 1 end # 解が見つからなかった return false end def main() pattern = [2, 7, 4, 1, 5, 0, 3, 6] # 最初の1つ目のパターンを記録 saveHistory(pattern, -1) if solve7Puzzle() == false puts "解が見つからなかった" else puts "解が見つかった" # 解を表示する last = -1 while last != @queueBottom i = @queueBottom while @history[i].patternFrom != last i = @history[i].patternFrom end last = i # パネルを表示 patternFromHash(pattern, @history[last].hash) i = 0 while i < 8 if pattern[i] != 0 printf "#{pattern[i]} " else printf " " end if i % 4 == 3 printf "¥n" end i += 1 end gets() end end end end seven = SevenPuzzle.new seven.main