2009-08-05 4 views
3

Итак, я работаю над минимаксной реализацией для игры с шашками, чтобы помочь себе лучше изучить Haskell. Функция, с которой у меня возникают проблемы, содержит список состояний игры и генерирует список непосредственных состояний игры преемника. Как и шашки, если прыжок доступен, игрок должен его взять. Если есть более одного, игрок может выбрать.Упрощение кода Haskell

По большей части это прекрасно работает со списком monad: loop над всеми входными игровыми состояниями, петлями над всеми шариками, которые можно было прыгать, петлями над всеми прыжками этого мрамора. Этот список monad красиво сглаживает все списки в простой список состояний в конце.

Фокус в том, что если никаких прыжков не найдено для данного состояния игры, мне нужно вернуть текущее состояние игры, а не пустой список. Код ниже - лучший способ, которым я придумал это, но для меня это кажется уродливым. Любые предложения по его очистке?

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 [] 

РЕДАКТИРОВАТЬ: Подпишите примерные сигнатуры для * Hex-функций.

ответ

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) 

над результатом, а также различные другие вещи.

Как сказал Фред Брукс (перефразируя), как только вы получите правильные типы, код практически сам пишет.

+0

Это просто _part_ кода для генерации набора возможных ходов, который является частью ввода минимакс. В частности, это часть, связанная с генерированием доступных прыжков. – Resistor

+0

Правила похожи на шашки: если есть прыжки, они должны быть приняты. Если доступно более одного, игрок может выбрать, что взять. Разрешены прыжки с цепочкой (т. Е. Двойные прыжки в шашки), и применяется одно и то же правило «требуется», если доступно. – Resistor

+0

Я думал об этом как о рекурсивно сгенерированном дереве состояний. Я начинаю с списка, содержащего начальное состояние. Из этого я создаю список достижимых состояний, которые не более одного прыгают. Из них я создаю список состояний, которые не более двух прыжков и т. Д. Я повторяю этот процесс до тех пор, пока список не перестанет меняться. Это работает только в том случае, если в списке присутствуют «тупиковые» состояния, а идут в пустой список. – 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) 

Выпускаемые в впустить в списке commprehension в верхней удосужился меня немного, но, как я не имеют всего кода, я действительно не знаю, как это сделать другим способом. Если вы можете более подробно изменить глубину, я предлагаю вам использовать больше комбинаторов (map, foldr, foldl 'и т. Д.), Поскольку они действительно уменьшают размер кода в моем опыте.

Примечание. Код не проверен и может не компилироваться.

+0

Невозможно, чтобы первое понимание списка было просто [если null (дети ws), то ws else children ws | ws <- states]? Я ожидаю, что повторение вызова функции будет устранено компилятором. –

+0

yep, Код для изменения: –

+0

listOfHex будет иметь тип listOfHex :: [Координата -> Координата] Каждый гексагон на доске идентифицируется координатой (которая является всего лишь синонимом пары Int). Например, функция nwHex возвращает координаты hex на северо-запад от входного шестнадцатеричного. listOfHex содержит пары этих функций, которые используются для определения возможного перехода. – Resistor