算法 – Haskell – 如何使用列表monad在tic tac toe游戏中生成下一步

我正在为Haskell中的n * n板实现一个tic tac toe游戏,我需要生成所有可以从下一步移动获得的板配置.

我的董事会定义如下:

data Cell = E
      | X  
      | O 
      deriving (Eq,Show)

type Row a = [a]
type Board = Row (Row Cell)

iniBoard :: Int -> Board
iniBoard n = let row = replicate n E in replicate n row

我可以确定,给定的棋盘配置是否为玩家x赢了,所以我有

win :: Cell -> Board -> Bool
win E _   = False
win x brd = any full $diags brd ++ rows brd ++ cols brd
where
    diags brd = mainDiag : [secondDiag]
    mainDiag = zipWith (!!) brd [0..]
    secondDiag = zipWith (!!) revBrd [0..]
    revBrd = do
        xs <- brd
        return (reverse xs)
    rows = id
    cols = transpose
    full xs = all (==x) xs

但是我不知道如何生成玩家x可以在下一步移动时制作的所有棋盘配置.

我明白,我需要遍历所有单元格并检查,如果单元格为空,那么我可以在此处放置标记并返回新配置.如果我已经获胜配置,那么没有下一步,我必须返回空列表

我有这样的代码:

nxt :: Cell -> Board -> [Board]
nxt x brd = do
if (win x brd || win (switch x) brd)
    then
        []
    else
        undefined

我怎么能这样做,使用list monad?感谢帮助!

最佳答案

picks :: [x] -> [([x], x, [x])]
picks [] = []
picks (x : xs) = ([] , x, xs) : [(x : sy, y, ys) | (sy, y, ys) <- picks xs]

(这是this的调整版),所有可能的下一个板都是

import Data.List.Split (chunksOf)

next :: Int -> Cell -> Board -> [Board]
next n who b =  
    picks (concat b) >>= \(sy, y, ys) ->
             case y of E -> [chunksOf n $sy ++ [who] ++ ys] ;
                       _ -> []

谁是X或O之一,或者当然.

这只不过是一个过滤器,用于保存空箱,以及一个映射到那些已经过滤的地图.列表推导更简单,

next n who b = [ chunksOf n $sy ++ [who] ++ ys
                 | (sy, E, ys) <- picks $concat b ]

picks函数在连接的行中一个接一个地拾取所有可能的单元格,同时保留前缀和后缀; chunksOf n从一排长的单元格中重建板,连续排列成n个单元格.因此整体效果是所有可能的电路板列表,其中E被替换为谁.

更高效的选择将以相反的顺序构建其前缀(sy);创建所谓的“拉链”列表.然后在重建时,他们必须相应地逆转.

编辑:正如列表理解所示,它可能首先用do notation编写:

next n who b =  do 
    (sy, E, ys) <- picks $concat b
    return (chunksOf n $sy ++ [who] ++ ys])

在符号中,模式不匹配被转换为失败调用,在列表monad中,导致跳过元素,而整个计算继续而不会失败.

edit2:一个基于Data.List的代码,它通过输入一次性完成,是

import Data.List (mapAccumL)

-- mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
next who b = concat . snd $mapAccumL f (id, drop 1 xs) xs 
  where
    xs = concat b
    n = length b
    f (k,r) x = ( (k.(x:), drop 1 r) , [chunksOf n $k (who:r) | x==E] ) 

感谢גלעדברקן的讨论.

转载注明原文:算法 – Haskell – 如何使用列表monad在tic tac toe游戏中生成下一步 - 代码日志