Я пытаюсь создать функцию, которая рекурсивно воспроизводит все возможные игры в крестики-нолики с использованием генетического алгоритма, а затем возвращает кортеж (выигрыши, проигрыши, связи). Тем не менее, функция ниже всегда переполняет стек при вызове так:
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
- список хромосом. Переполнение не происходит только с одним игроком, но с двумя это происходит.
РЕДАКТИРОВАТЬ: если функция вызывается следующим образом, она не переполняет стек.
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?