1

だから私は次のように、ネストされたタプルの形で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]

しかし、私はそれを理解することはできません。私が最初に考えたのは、すべてのフィールドを繰り返し処理し、空かどうかを確認してから、新しいボードをリストに追加するか、何もしないことです。ただし、すべてのフィールドを反復処理する方法はありません。if ステートメントやガードを使用してすべてのフィールドを手動でチェックしたとしても、移動の可能性があるかどうかに関係なく、次のフィールドに移動してチェックするにはどうすればよいですか?

ボード形式をリストに変換すればできますが、それではこの問題の目的に反する気がします。ボードの再構築を必要としない、より良い解決策が必要です。

4

3 に答える 3

1

タプルのフィールドを反復処理することはできません。タプルはそのためのものではありません。リストのリストは、おそらくこの問題のより自然な表現です。

とはいえ、タイプに従って、使用しているボード表現でこの関数を実装できます。a の移動Boardは、1 行目、2 行目、または 3 行目の移動です。行の移動は、プレーヤーが 1 番目、2 番目、または 3 番目のフィールドに配置されることです。タプルは一般に異種であるため、表現の難しさは、タプルをマップする簡単な方法がないことです。代わりに、関数をタプル内の場所に適用する一般的な方法を自分で作成することができます。これを行う1つの方法は次のとおりです(Monad混乱した場合は、精神的に「fooのリスト」を表示するすべての場所に置き換えれm 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)

これらの関数は、それぞれタプルの 1 番目、2 番目、3 番目のスロットに関数を適用する方法を提供します。それらはモナドに包まれているので、スロットの可能性のリストを返す関数を持ち、それをタプル全体の可能性のリストに自動的に変換できます。

これらを使用すると、これらの呼び出しをつなぎ合わせるだけで関数全体を作成できます。

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 つの可能な移動 (プレイヤーをそのスロットに配置する) があります。

于 2013-10-03T23:12:45.573 に答える
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

これは nextStates 関数の一部です

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
于 2013-10-03T23:28:22.063 に答える
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)
于 2013-10-03T22:42:33.007 に答える