2009-08-05 8 views
3

私はハスケルをよりよく学ぶのに役立つチェッカーのようなゲームのミニマックスの実装に取り​​組んでいます。私が問題を抱えている関数は、ゲーム状態のリストをとり、直後のゲーム状態のリストを生成します。チェッカーのように、ジャンプが利用可能な場合、プレイヤーはそれを取る必要があります。複数ある場合は、選ぶことができます。いくつかのハスケルコードを簡略化

ほとんどの場合、入力されたすべてのゲーム状態に対してmonad:loopというリストがうまく機能し、ジャンプできるすべての大理石をループし、その大理石のすべてのジャンプをループします。このリストモナドは、すべてのリストを終わりに単純な状態リストにまとめることができます。

トリックは、特定のゲーム状態でジャンプが見つからない場合は、空のリストではなく現在のゲーム状態を返す必要があるということです。下のコードは私がこれをやっている最善の方法ですが、それは私には本当に醜いようです。どのようにそれをきれいにする上の任意の提案?

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 
     [email protected]_ -> n 
    where 
    children [email protected](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 [] 

EDIT:* Hex関数のサンプル署名を提供します。

答えて

3

特定のゲーム状態でジャンプが見つからない場合は、空のリストではなく現在のゲーム状態を返す必要があります。

なぜですか?私はミニマックスを何度も書いてきましたが、そのような関数の使用を想像することはできません。そのリストが空の場合、あなたが本当に「次の状態のリストのいずれかを返したい場合、またはあなたは、しかしタイプ

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

または

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

の機能を持つほうではないでしょう元の状態 "であれば、希望のタイプは

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

です。このタイプは、簡単に十分に平坦化できます。あなたは

(\(start, succs) -> if null succs then Left start else Right succs) 
結果オーバー

、プラスの様々な他のものをマッピングすることができるよりも実装する方法については

は、私はタイプ

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

のヘルパー関数を定義してお勧めします。

フレッド・ブルックスが言ったように(言い換え)、いったんタイプが正しいとすれば、コードは実質的に自分自身を書きます。

+0

これは、minimaxへの入力の一部である可能な移動の集合を生成するためのコードのちょうど_part_です。特に、利用可能なジャンプの生成に関係する部分です。 – Resistor

+0

ルールはチェッカーのようなものです:利用可能なジャンプがある場合、それを実行する必要があります。利用可能なものが複数ある場合、プレイヤーはどちらを取るかを選択できます。連鎖ジャンプ(チェッカーでのダブルジャンプ)は許可され、同じ必要な場合は同じルールが適用されます。 – Resistor

+0

私はこれを再帰的に生成された状態のツリーと考えています。私は最初の状態を含むリストから始めます。それから、私は、最大で1回離れて到達可能な状態のリストを生成します。それらから、私はリストが変わるのを止めるまで、このプロセスを繰り返します。 「デッドエンド」の状態がリストに残り、空のリストに行く場合にのみ有効です。 – Resistor

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 [email protected](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) 

「簡素化」のためのletをLETにリストcommprehensionに上部に少し、私のように私を気にすべてのコードを持っていない、私は本当に他の方法でそれを行う方法を知りません。より深く変更することができれば、より多くのコンビネータ(マップ、foldr、foldlなど)を使用することをお勧めします。

注:コードはテストされていないため、コンパイルできないことがあります。

+0

最初のリストの理解はちょうど[nullの場合(子供のws)、次にwsの他の子供のws | ws-状態]?私は、関数呼び出しの繰り返しは、コンパイラによって排除されることを期待します。 –

+0

あなたのコメントに従うように編集されたコード –

+0

listOfHexはタイプ を持っていますlistOfHex :: [Coord - > Coord] ボード上のすべてのヘクスはCoord(これはIntのペアの同義語です)によって識別されます。たとえば、関数nwHexは、入力ヘックスの北西にあるヘクスの座標を返します。 listOfHexには、これらの関数のペアが含まれており、ジャンプが可能かどうかを識別するために使用されます。 – Resistor