所以我有一个Tic Tac Toe板,以嵌套元组的形式,如下所示:
type Row = (Field, Field, Field)
type Board = (Row, Row, Row)
data Field = X | O | B
deriving (Eq, Ord)
B代表空。我需要选择一个玩家,一个给定的董事会状态,然后在下一步之后生成所有可能的董事会状态列表。
moves :: Player -> Board -> [Board]
然而,我无法弄明白。我最初的想法是,我需要遍历每个字段,检查它是否为空,然后将新的Board添加到列表中或不执行任何操作。但是,我认为无法遍历所有字段。即使我用if语句或警卫手动检查每个字段,我如何移动到下一个字段进行检查,无论我是否最终进行了可能的移动?
如果我将电路板格式转换为列表,我可以做到,但我觉得这样做会破坏这个问题的目的。必须有一个更好的解决方案,不需要重组董事会。
答案 0 :(得分:1)
你无法遍历元组的字段 - 元组不是为了那个。列表列表可能是此问题的更自然的表示。
也就是说,您可以通过遵循类型来使用您正在使用的电路板表示来实现此功能。 Board
上的移动是第一,第二或第三行的移动。移动一行是将玩家放置在第一,第二或第三个字段上。你的表示的困难在于没有简单的方法来映射元组,因为元组通常是异构的。因此,您可以做的一件事就是为自己编写一个将函数应用于元组中某个位置的通用方法。这是一种方法(如果Monad
内容混淆了你,在你看到m foo
的任何地方,在心理上替换“foo列表”,你就可以了:
mReplace1 :: Monad m => (a -> m d) -> (a,b,c) -> m (d,b,c)
mReplace1 f (a,b,c) = f a >>= \d -> return (d,b,c)
mReplace2 :: Monad m => (b -> m d) -> (a,b,c) -> m (a,d,c)
mReplace2 f (a,b,c) = f b >>= \d -> return (a,d,c)
mReplace3 :: Monad m => (c -> m d) -> (a,b,c) -> m (a,b,d)
mReplace3 f (a,b,c) = f c >>= \d -> return (a,b,d)
这些函数提供了一种将函数分别应用于元组中的第一个,第二个和第三个插槽的方法。它们被包装在monad中,这样我们就可以有一个函数返回插槽的可能性列表,并自动将其转换为整个元组的可能性列表。
通过这些,我们可以通过将这些调用串在一起来编写整个函数。
moves p board = mReplace1 rowMoves board ++
mReplace2 rowMoves board ++
mReplace3 rowMoves board
where rowMoves row = mReplace1 fieldMoves row ++
mReplace2 fieldMoves row ++
mReplace3 fieldMoves row
fieldMoves B = [p]
fieldMoves _ = []
那就是:板的移动是第1行中移动的所有可能性,加上第2行的所有可能性,为第3行提供了所有可能性。对于给定的行,可能的移动是所有移动对于插槽1,加上插槽2的所有移动,加上插槽3的所有移动。对于给定的插槽,如果已经存在X或O,则没有可能的移动;否则就有一个可能的举动(将玩家放在那个位置)。
答案 1 :(得分:0)
这是我之前使用的简单解决方案
import qualified Data.Map as Map
data Piece = O | X deriving (Eq,Ord)
type Position = (Int,Int)
type Board = Map.Map Position Piece
positions = [(i,j) | i <- [0,1,2], j <- [0,1,2]]
spaces board = map (\pos -> Map.notMember pos board) positions
moves player board = map (\pos -> Map.insert pos player board) (spaces board)
答案 2 :(得分:0)
正如其他人所说,元组对于这种方法并不是一个好主意,因为没有办法遍历它们。
你说你需要元组,所以你去,我几乎可以肯定它是有效的,测试它。
import Control.Monad (msum)
import Control.Applicative ((<*>), pure)
data Player = P1 | P2 deriving (Eq, Show)
data Field = X | O | B deriving (Eq, Show)
type Board = ((Field,Field,Field)
,(Field,Field,Field)
,(Field,Field,Field))
symbolToPlayer :: Field -> Player
symbolToPlayer X = P1
symbolToPlayer O = P2
checkThree :: (Field,Field,Field) -> Maybe Player
checkThree (a,b,c)
| a == b && a == c = Just $ symbolToPlayer a
| otherwise = Nothing
winHorizontal :: Board -> Maybe Player
winHorizontal (r1, r2, r3) = msum $ map checkThree [r1, r2, r3]
winVertical :: Board -> Maybe Player
winVertical ((a,b,c), (d,e,f), (g,h,i)) =
msum $ map checkThree [(a,d,g), (b,e,h), (c,f,i)]
winDiagonal :: Board -> Maybe Player
winDiagonal ((a,_,c), (_,e,_), (g,_,i)) =
msum $ map checkThree [(a,e,i), (c,e,g)]
hasWinner :: Board -> Maybe Player
hasWinner b = msum $ [winHorizontal, winVertical, winHorizontal] <*> pure b
boardBlanks :: Board -> Int
boardBlanks (r1,r2,r3) = rowBlanks r1 + rowBlanks r2 + rowBlanks r3
rowBlanks :: (Field, Field, Field) -> Int
rowBlanks (a,b,c) = foldr hack 0 [a,b,c]
where hack B c = 1 + c
hack _ c = c
changeBoard :: Field -> Int -> Board -> Board
changeBoard f i (a,b,c)
| hack [a] > i = (changeRow f (i - hack []) a, b, c)
| hack [a,b] > i = (a, changeRow f (i - hack [a]) b, c)
| hack [a,b,c] > i= (a, b, changeRow f (i - hack [a,b]) c)
where
hack ls = sum $ map rowBlanks ls
changeRow f 0 row =
case row of
(B,a,b) -> (f,a,b)
(a,B,b) -> (a,f,b)
(a,b,B) -> (a,b,f)
otherwise -> row
changeRow f 1 row =
case row of
(B,B,a) -> (B,f,a)
(a,B,B) -> (a,B,f)
otherwise -> row
changeRow f 2 row =
case row of
(B,B,B) -> (B,B,f)
otherwise -> row
nextStates :: Board -> [Board]
nextStates b = os ++ xs
where
os = foldr (hack O) [] . zip [0..] $ replicate (boardBlanks b) b
xs = foldr (hack X) [] . zip [0..] $ replicate (boardBlanks b) b
hack f (i,a) ls = changeBoard f i a : ls