遺伝的アルゴリズムを使用して、三目並べのすべての可能なゲームを再帰的に再生し、(勝ち、負け、引き分け)のタプルを返す関数を作成しようとしています。ただし、以下の関数は、次のように呼び出されると常にスタックをオーバーフローします。
scoreOne :: UnscoredPlayer -> [String] -> ScoredPlayer
scoreOne player boards = ScoredPlayer (token player) (chromosome player) (evaluateG $! testPlayer player boards)
...
let results = map (\x->scoreOne x boards) players
print (maximum results)
ここplayers
で、は染色体のリストです。オーバーフローは、1人のプレーヤーだけでは発生しませんが、2人のプレーヤーでは発生します。
編集:関数が次のように呼び出された場合、スタックはオーバーフローしません。
let results = map (\player -> evaluateG (testPlayer player boards)) players
print (maximum results)
ただし、次の方法ではスタックがオーバーフローします。
let results = map (\player -> ScoredPlayer (token player) (chromosome player) (evaluateG $! testPlayer player boards)) players
参考までに、ScoredPlayer
は次のように定義されます(文字列はプレーヤートークン、[Int]は染色体、Floatはスコア):
data ScoredPlayer = ScoredPlayer String ![Int] !Float deriving (Eq)
Haskellについて私が知っていることから、私が使用している呼び出しは関数の結果に対してさらに処理を実行しているplayAll'
ため、関数は末尾再帰ではありません。ただし、可能なすべてのゲームを確実にプレイする必要があるため、呼び出しfoldl'
を削除する方法がわかりません。foldl'
関数を末尾再帰になるように(または少なくともスタックをオーバーフローさせないように)再構築する方法はありますか?
事前に感謝し、大量のコードリストをお詫びします。
playAll' :: (Num a) => UnscoredPlayer -> Bool -> String -> [String] -> (a,a,a) -> (a,a,a)
playAll' player playerTurn board boards (w,ls,t)=
if won == self then (w+1,ls,t) -- I won this game
else
if won == enemy then (w,ls+1,t) -- My enemy won this game
else
if '_' `notElem` board then (w,ls,t+1) -- It's a tie
else
if playerTurn then --My turn; make a move and try all possible combinations for the enemy
playAll' player False (makeMove ...) boards (w,ls,t)
else --Try each possible move against myself
(foldl' (\(x,y,z) (s1,s2,s3) -> (x+s1,y+s2,z+s3)) (w,ls,t)
[playAll' player True newBoard boards (w,ls,t)| newBoard <- (permute enemy board)])
where
won = winning board --if someone has one, who is it?
enemy = (opposite.token) player --what player is my enemy?
self = token player --what player am I?