challenge マルバツゲーム:賢いプレイヤー

#6190 の続編です。
マルバツゲームで、賢いプレイヤーの思考ルーチンを実装してください。

賢いといってもいろいろありますが、
1.負けない
2.できるだけ勝つ
という条件でいってみたいと思います。

ランダムプレイヤーと1万回バトルした結果(勝ち・負け・分け)を表示してください。
先攻になっても後攻になっても無敗!となれば言うことなしです。

Posted feedbacks - Haskell

後手無配までは行ってませんが...

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module Main where

import Char
import Control.Monad
import Control.Monad.State
import Data.Array.IArray
import Data.List
import Data.Maybe
import Data.Function
import System.Random

type Summary = Array Int Int
type Board = Array Int Int
type Idx = Int
type Token = Int
tkX = 1 :: Token
tkO = 2 :: Token

doIf :: Bool -> (a -> a) -> a -> a
doIf True f a = f a
doIf False _ a = a

mapArray array f idxs = array // ([(x, f (array ! x)) | x <- idxs])

idxSumIdx = [[0, 3, 6], [1, 3], 
    [2, 3, 7], [0, 4], [1, 4, 6, 7], 
    [2, 4], [0, 5, 7], [1, 5], [2, 5, 6]] 

idxToSumIdx idx = idxSumIdx!!idx

type TTT = ((Summary, Summary), Board, Int, Token)

turn :: TTT -> Idx -> TTT
turn (sum@(xSummary, oSummary), board, i, tk) idx
    | tk == tkX = ((sumMe', sumOpp), board', i + 1, tkO)
    | otherwise = ((sumOpp, sumMe'), board', i + 1, tkX)
    where
        sumMe' = mapArray sumMe (+ 1) (idxToSumIdx idx)
        (sumMe, sumOpp) = doIf (tk == tkO) (swap) sum
        board' = board // [(idx, tk)]
        swap (a, b) = (b, a)
        
won :: TTT -> Maybe Token
won ((xSummary, oSummary), _, _, _)
    | elem 3 (elems xSummary) = Just tkX
    | elem 3 (elems oSummary) = Just tkO
    | otherwise = Nothing

availableMoves :: TTT -> [Idx]
availableMoves (_, board, _, _) = map (fst) $ filter ((==0).snd) $ assocs board

lineScore :: (Summary, Summary) -> Int -> Int
lineScore (sumSelf, sumEn) iLine = check (sumSelf!iLine) (sumEn!iLine)
    where
        check 2 0 = 10000 -- can win in 1 attempts (2 self & no enemy)
        check 0 2 = 1000 -- can lose in 1 attempts (2 enemy & no self)
        check 0 1 = 100 -- can lose in 2 attempts (1 enemy & no self)
        check 1 0 = 10 -- can win in 2 attempts (1 self & no enemy)
        check 0 0 = 1 -- can win in 3 attempts (empty)
        check _ _ = 0 -- can't win nor lose (!0 enemy & !0 self)

scoreCell :: (Summary, Summary) -> Idx -> Int
scoreCell summaries idx = sum $ map (lineScore summaries) $ idxToSumIdx idx

type Player = TTT -> IO TTT

smartPlayer :: Player
smartPlayer ttt@(sum, _, _, tk) = return $ turn ttt bestMove
    where
        bestMove = snd $ maximumBy (compare `on` fst) $ zip (map (scoreCell sum) avm) avm
        avm = availableMoves ttt

randomPlayer :: Player
randomPlayer ttt = do
        idx <- bestMove
        return $ turn ttt idx
    where
        bestMove = randomChoice $ availableMoves ttt
        randomChoice xs = do
            idx <- getStdRandom $ randomR (0, (length xs) - 1)
            return $ xs!!idx

type STTT = StateT TTT IO ()

playTurn :: Player -> STTT
playTurn p = do
    ttt@(_, _, _, tk) <- get
    ttt' <- liftIO (p ttt)
    put ttt'
    liftIO (evaluate ttt')
    where
        evaluate :: TTT -> IO ()
        evaluate ttt''
            | isJust $ won ttt'' = fail (show $ fromJust $ won ttt'')
            | otherwise = return ()

play :: [Player] -> IO Int
play players =  do
    catch doGame catcher 
    where
        initialTTT     = ((initialSummary, initialSummary), initialBoard, 0, tkX)
        initialSummary = listArray (0, 7) $ repeat 0
        initialBoard   = listArray (0, 8) $ repeat 0

        
        doGame = do
            evalStateT (mapM playTurn players) initialTTT
            return 0

        catcher :: IOError -> IO Int
        catcher e
            | e == (userError "1") = return 1
            | e == (userError "2") = return 2
            | otherwise = return 0

main = do
    putStrLn "[draw, 1st win, 2nd win]"
    tries <- replicateM 10000 (play players)
    print $ elems $ foldl (\rg idx -> mapArray rg (+1) [idx]) rgSum tries
    where
        rgSum :: Array Int Int
        rgSum = listArray (0, 2) $ repeat 0
--        players = take 9 $ cycle [smartPlayer, smartPlayer]
        players = take 9 $ cycle [randomPlayer, smartPlayer]
--        players = take 9 $ cycle [smartPlayer, randomPlayer]
--        players = take 9 $ cycle [randomPlayer, randomPlayer]

Index

Feed

Other

Link

Pathtraq

loading...