マルバツゲーム:賢いプレイヤー
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]
|

syat
#6207()
Rating0/2=0.00
マルバツゲームで、賢いプレイヤーの思考ルーチンを実装してください。
賢いといってもいろいろありますが、
1.負けない
2.できるだけ勝つ
という条件でいってみたいと思います。
ランダムプレイヤーと1万回バトルした結果(勝ち・負け・分け)を表示してください。
先攻になっても後攻になっても無敗!となれば言うことなしです。
[ reply ]