2013-10-27 8 views
7

私はKalahworking implementationを持っています。これは、ゲームの最初のターンに最適な移動の連続を計算するアプリケーションです。ボードゲームでターンを表現するのにどのような構造を使うべきですか?

私はこのアプリケーションを再実装していますが、今度はテストスイートと、より面白い構造を使ってモノイドやモナドなどのコードを使っています。

あなたがoriginal codeで見ることができるように(かどうか、それは非常に複雑だと私はそれを書き換えてる理由です)、次のように私は1つ「移動」を定義しました:

  1. 私が渡しています私のボードとしてPotのリスト、ボードの私の側の開始位置と一緒に。
  2. 私はPotのリストの終わりに達するまで大理石をピックアップして落とします。
  3. 変更されたボード([Pot])、私が手に持っているかもしれない大理石の数、別のラップに行くべきかどうかを表現するADT(LapResult)を返します。

私は、関数への入力引数として渡すことができる巧妙なデータ構造でボード状態を表現すると、動きをラップに分割する必要はないと考えています同じデータ構造が戻り値として出てきます。少なくともそれは私の推測です、私の考えは、ボードの状態が私がモノイドについて読んだことを思い出させるということでした。

空のポットや店舗に着くまで、ピックアップとドロップの大理石のすべてを「移動」と定義すると、「移動」のコードを書き直す明白な方法があります"作品ですか?

現在の再実装状態はhereです。

答えて

1

注:私はこれをテストしていません。おそらくバギー。

私はあなたの問題は、ボードを2つの視点から考えて、「白」と「黒」と呼ぶ必要があると思います。

data Player = White | Black 

otherPlayer :: Player -> Player 
otherPlayer White = Black 
otherPlayer Black = White 

Mancalaボードは、モジュラーアースメントを示唆する円形構造です。

import Data.Vector -- More efficient version of Array 

type PotNum = Int -- Use Int for simple index of pot position. 

type Pot = Int -- Just record number of marbles in the pot. 

Intの代わりにData.Word8を使用すると、よりコンパクトなデータ構造が得られるかもしれませんが、わかりません。今はそれを簡単に保つ。

type Board = Vector Pot 

はその後isStoreはPotNumの簡単な関数とプレイヤー

isStore :: Player -> PotNum -> Bool 
isStore White 0 = True 
isStore Black 7 = True 
isStore _ _ = False 

もしているあなたは、他のプレイヤーの店舗をスキップして、ボードの周りに前方に移動したいです。..

nextPot :: Player -> PotNum -> PotNum 
nextPot White 6 = 8 -- Skip Black's store 
nextPot White 13 = 0 
nextPot Black 12 = 0 -- Skip White's store 
nextPot _ n = n + 1 

各プレイヤー

playerPots :: Player -> [PotNum] -- Implementation omitted. 

ための制御されたポットのリスト与えられたポット

marblesIn :: PotNum -> Board -> Int -- Implementation omitted. 

でビー玉の数を今、あなたは移動機能を書くことができます。違法行為のために何も返さないようにします。

move :: Player -> PotNum -> Board -> Maybe Board -- Implementation omitted. 

あなたは、これはだから今、あなたがData.Tree.unfoldを使用して、任意の開始位置からの完全なゲーム木を得ることができるすべての潜在的な動き、そして得られたボードの状態

allMoves :: Player -> Board -> [(PotNum, Board)] 
allMoves p b1 = do 
    n <- playerPots p 
    case move p n b1 of 
     Nothing -> fail "" -- List monad has this as [] 
     Just b2 -> return (n, b2) 

を生成することができますリストモナドを使いますこれはmove関数の変形をとる。これはやや控えめです。我々はポジションをもたらした移動を知りたいが、最初のポジションはそれにつながる動きがない。したがって、おそらく。

unfoldTree関数は、現在の状態を取り、子ノードの値のリストを返す関数(以下のコードではf)を受け取ります。現在の状態と現在のノードは、単に移動したプレイヤーのトリプル、作成した移動、および結果のボードの両方です。従って "f"の最初のビット。 "f"の2番目のビットは、 "allMoves"によって返された値を変換して正しいデータを追加する "opponentMoves"関数を呼び出します。

unfoldGame :: Player -> Board -> Tree (Player, Maybe PotNum, Board) 
unfoldGame p b = unfoldTree f (p, Nothing, b) 
    where 
     f (p1, n1, b1) = ((p1, n1, b1), opponentMoves (otherPlayer p1), b1 
     opponentMoves p2 b2 = map (\(n3, b3) -> (p2, Just n3, b3)) $ allMoves p2 b2 

これでツリーを散歩するだけです。法的な動きが残っていないので、各リーフはゲームの終わりです。 unfoldGame関数は怠惰なので、現在検討中のゲーム状態を保持するメモリだけが必要です。

関連する問題