#!/usr/bin/env ruby # coding: utf-8 #=============================================================================== # # 石取りゲーム # # 1 〜 3 個の石を順に取り合い、最後の 1 個を取ったら負け # #--------------------------------------------------------------- # # 勝負クラス # class Game attr_reader :stones def initialize(game = nil, stones = 20) if(game) # 状況のコピーを返す @stones = game.stones else # 初期の状況を返す @stones = stones end end #----------------------------------------------------------- # # 状況を出力する # def show puts puts('残りの石の数[%d]' % @stones) puts end #----------------------------------------------------------- # # 打てる手について順に返す # def each_hand [3, 2, 1].each {|n| @stones - n > -1 and yield(n) # 石が存在すれば 3 つまで取れる } end #----------------------------------------------------------- # # 仮想の手を打つ # def virt_hand(n) @stones -= n end #----------------------------------------------------------- # # 手を打つ、勝ったら真を返す # def hand(n) virt_hand(n) @stones == 1 ? true : false # 手番後に石の残りを 1 にしたら勝ち end end #--------------------------------------------------------------- # # プレーヤクラス # class Player attr_reader :name end #--------------------------------------------------------------- # # 人間プレーヤクラス # class HumanPlayer < Player def initialize @name = 'あなた' end def think(game) print('いくつ石を取る?') $stdin.readline.to_i end end #--------------------------------------------------------------- # # コンピュータプレーヤクラス # class ComputerPlayerX < Player def initialize(think_depth) @name = 'ワタシ' @think_depth = think_depth end #----------------------------------------------------------- # # 再帰的に思考し、最善の手/状況の評価点を返す # def think(game, depth = 0, turn = 1) # turn 1:自分, -1:相手 debug = depth < 1 ? '> ' * (depth + 1) : false #------------------------------------------------------- # # 読みの深さが一定に達したら、その状況の評価点を返す # if(depth > @think_depth) return(evaluate(game) * turn) #------------------------------------------------------- # # 打てる手について順に評価し、最高の評価点を返す # else turn_h = turn > 0 ? '自分' : '相手' maxpoint = -99999; besthand = nil game.each_hand {|hand| # 打てる手について順に繰り返す virt_game = Game.new(game) # 仮想の状況に複製 virt_game.virt_hand(hand) # 仮想の盤面に対して仮想の手を打つ puts(debug + '[%s]の手[%d]...' % [turn_h, hand]) if(debug) point = -think(virt_game, depth + 1, -turn) # 再帰的に思考する point > maxpoint and maxpoint = point and besthand = hand puts(debug + '[%s]の手[%d]...評価点[%d]' % [turn_h, hand, point]) if(debug) } if(maxpoint == -99999) # このゲームにおいては # 自分が打てる手なし => 最後の石を相手が取った => 勝った # 相手が打てる手なし => 最後の石を自分が取った => 負けた puts(debug + '[%s]の打てる手なし' % [turn_h]) if(debug) maxpoint = turn > 0 ? 0 : 99998 end puts(debug + '最善の手を選択、[%s]の手[%s]...評価点[%d]' % [turn_h, besthand, maxpoint]) if(debug) return(depth == 0 ? besthand : maxpoint) # 最後は評価点でなく、最善の手を返す end end #----------------------------------------------------------- # # 手番後の状況の評価点を返す # def evaluate(game) point = 50 # 標準の評価点 game.stones == 1 and point = 99999 # 手番後に石の残りを 1 にしたら勝ち point end end #--------------------------------------------------------------- # # メイン # # 5, 9, 13, 17, 21...でのスタートは最善手なし # think_depth = 9 なら 18 までの最善手を算出できる # think_depth = 9 game = Game.new(nil, (it = ARGV[0]) ? it.to_i : 20) playerA = HumanPlayer.new playerB = ComputerPlayerX.new(think_depth) game.show loop { [playerA, playerB].each {|player| n = player.think(game) puts('%sは[%d]個の石を取りました' % [player.name, n]) if(game.hand(n)) game.show puts('%sの勝ち!' % player.name) exit end game.show } } __END__