昨日の続き

ゲームと着手を受け取って、更新した新しいゲームを返す関数と、ゲームを受け取ってゲームが終わったかどうかと誰が勝ったのかを返す関数を作った。

-- ゲームと着手を受け取って、更新した新しいゲームを返す
step_game :: Game -> Play -> IO Game
step_game game play = return (MakeGame
    [if (i == (play_value play)) then (next_turn game) else x | (i, x) <- zip [0..] (value game)] 
    (3 - (next_turn game)))

-- ゲームを受け取ってゲームが終わったかどうかと、誰が勝ったのかを返す
check_is_finished :: Game -> (Bool, Int)
check_is_finished game = let lines = (map (\x -> (map ((value game)!!) x))
                                      [[0, 3, 6], [1, 4, 7], [2, 5, 8], 
                                       [0, 1, 2], [3, 4, 5], [6, 7, 8],
                                       [0, 4, 8], [2, 4, 6]])
                         in case find (== [1, 1, 1]) lines of
                              Just x -> (True, 1)
                              Nothing -> case find (== [2, 2, 2]) lines of
                                          Just x -> (True, 2)
                                          Nothing -> case find (== 0) (value game) of
                                                      Just x -> (False, 0)
                                                      Nothing -> (True, 0)

random_choiceやhuman_choiceなどの思考ルーチンはゲームに無関係なのでmonte.hsに移動した。これによりゲームの定義であるTicTacToe.hsはimport IOやimport Randomをしなくなった。正しい。

-- 「考える」とは与えられた選択肢の中から一つ選ぶこと
type Think = [Play] -> IO Play

-- ランダムに選択可能な着手を受け取って一つ選んで返す
random_choice :: Think
random_choice hands = do
  i <- getStdRandom (randomR (0, (length hands) - 1))
  return (hands !! i)

-- 人間が選択可能な着手を受け取って一つ選んで返す
human_choice :: Think
human_choice hands = do
  hSetBuffering stdout NoBuffering            
  putStr ("Choice a hand: " ++ (show (zip [0..] hands)))
  i <- readNum
  return (hands !! i)
      where readNum :: IO Int
            readNum = readLn

ゲームをするのに必要な部品が揃ったのでmainを頑張ってみた。これでランダムに考えるAIと人間とかゲームで対戦することが出来る。

main = do
  game <- init_game
  while_game_finished game

while_game_finished :: Game -> IO Int -- IO Who
while_game_finished game 
    = if finished then
          do
            putStr $ show game
            putStr "finished\n"
            return who_won
      else
          let 
              choice = if (next_turn game) == 2 then human_choice else random_choice
          in
            do
              putStr $ show game
              play <- choice $ possible_hands $ game 
              game <- step_game game play
              while_game_finished game 

    where (finished, who_won) = (check_is_finished game)

次はAIがモンテカルロする。これはつまり

choice = if (next_turn game) == 2 then human_choice else random_choice

の部分を変更できるようにする必要性を示唆している。

-- ゲームを受け取って、その情報によりAIを選択する
select_ai_for_human_play :: Game -> Think
select_ai_for_human_play game = if (next_turn game) == 2 then human_choice else random_choice

select_ai_for_monte :: Game -> Think
select_ai_for_monte game = random_choice

こんな感じかな。

ソース
http://bitbucket.org/nishio/montecarlo_game_test/src/tip/monte.hs
http://bitbucket.org/nishio/montecarlo_game_test/src/tip/TicTacToe.hs