3

そこで私はチェッカーのようなゲームのミニマックスの実装に取り​​組んでおり、Haskell をよりよく学べるようにしています。私が問題を抱えている関数は、ゲームの状態のリストを取得し、直後の後続のゲームの状態のリストを生成します。チェッカーと同様に、ジャンプが利用可能な場合、プレーヤーはジャンプする必要があります。複数ある場合は、プレイヤーが選択できます。

ほとんどの場合、これは list モナドとうまく機能します: すべての入力ゲーム状態をループし、ジャンプできるすべてのビー玉をループし、そのビー玉のすべてのジャンプをループします。このリストモナドは、最後にすべてのリストを単純な状態のリストにうまくフラット化します。

トリックは、特定のゲーム ステートでジャンプが見つからない場合、空のリストではなく、現在のゲーム ステートを返す必要があるということです。以下のコードは、私が思いついた最良の方法ですが、私には本当に醜いようです。それをきれいにする方法について何か提案はありますか?

eHex :: Coord -> Coord -- Returns the coordinates immediately to the east on the board
nwHex :: Coord -> Coord -- Returns the coordinates immediately to the northwest on the board

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states = do
    ws <- states
    case children ws of
      [] -> return ws
      n@_ -> n
  where 
    children ws@(ZertzState s1 s2 b p) = do
      (c, color)  <- occupiedCoords ws
      (start, end) <- [(eHex, wHex), (wHex, eHex), (swHex, neHex),
                       (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]
      if (hexOccupied b $ start c) && (hexOpen b $ end c)
        then case p of
          1 -> return $ ZertzState (scoreMarble s1 color) s2
                                   (jumpMarble (start c) c (end c) b) p
          (-1) -> return $ ZertzState s1 (scoreMarble s2 color)
                                      (jumpMarble (start c) c (end c) b) p
        else []

編集: *Hex 関数の型シグネチャの例を提供します。

4

2 に答える 2

3

トリックは、特定のゲーム ステートでジャンプが見つからない場合、空のリストではなく、現在のゲーム ステートを返す必要があるということです。

なんで?私はミニマックスを何度か書いてきましたが、そのような関数の用途は想像できません。タイプの関数を使用したほうがよいのではないでしょうか

nextStates :: [ZertzState] -> [Maybe [ZertzState]]

また

nextStates :: [ZertzState] -> [[ZertzState]]

ただし、「次の状態のリスト、またはそのリストが空の場合は元の状態」を本当に返したい場合は、必要なタイプは

nextStates :: [ZertzState] -> [Either ZertzState [ZertzState]]

その後、簡単に平らにすることができます。

実装方法については、型のヘルパー関数を定義することをお勧めします

[ZertzState] -> [(ZertzState, [ZertzState])]

そして、あなたがマッピングできるよりも

(\(start, succs) -> if null succs then Left start else Right succs)

結果に加えて、他のさまざまなことについて。

Fred Brooks が言ったように (言い換えると)、型を正しく取得すると、コードは実質的にそれ自体を記述します。

于 2009-08-05T19:59:08.427 に答える
1

リストにモナド表記を乱用しないでください。それは何の役にも立たないほど重いのです。さらに、同じ方法でリスト内包表記を使用できます。

do x <- [1..3]
   y <- [2..5]      <=>  [ x + y | x <- [1..3], y <- [2..5] ]
   return x + y

今「単純化」のために

listOfHex :: [(Coord -> Coord,Coord -> Coord)]
listOfHex = [ (eHex, wHex), (wHex, eHex), (swHex, neHex)
            , (neHex, swHex), (nwHex, seHex), (seHex, nwHex)]

generateJumpsIter :: [ZertzState] -> [ZertzState]
generateJumpsIter states =
    [if null ws then ws else children ws | ws <- states]
  where -- I named it foo because I don t know what it do....
    foo True   1  = ZertzState (scoreMarble s1 color) s2
                               (jumpMarble (start c) c (end c) b) p
    foo True (-1) = ZertzState s1 (scoreMarble s2 color)
                               (jumpMarble (start c) c (end c) b) p
    foo False  _  = []
    foo _ _ = error "Bleh"

    children ws@(ZertzState s1 s2 b p) =
      [ foo (valid c hex) p | (c, _)  <- occupiedCoords ws, hex <- listOfHex ]
        where valid c (start, end) =
                 (hexOccupied b $ start c) && (hexOpen b $ end c)

上部にあるletinlistの理解は少し気になりますが、すべてのコードがないため、他の方法でそれを行う方法がわかりません。より詳細に変更できる場合は、より多くのコンビネータ(map、foldr、foldl'など)を使用することをお勧めします。これは、私の経験ではコードサイズを実際に削減するためです。

コードはテストされておらず、コンパイルされない可能性があることに注意してください。

于 2009-08-05T09:04:54.933 に答える