{-# LANGUAGE FlexibleContexts #-}
module TTT.Minimax.Internal where

import Control.Monad.State (MonadState(..), modify)
import qualified Data.Map as M (Map(..), empty, insert, lookup)

import TTT.GameState (GameState(..), Token(..), availableMoves, choose, isWinFor, makeMove)

minimax gameState = do
    cache <- get
    case M.lookup gameState cache of
        Just score -> return score
        Nothing -> cacheMinimax gameState

cacheMinimax:: MonadState (M.Map GameState Int) m => GameState -> m Int
cacheMinimax gameState@(GameState board currentPlayer)
        |gameState `isWinFor` X = store 100
        |gameState `isWinFor` O = store (-100)
        |moves == [] = store 0
        |otherwise = do
            scores <- mapM (minimax . makeMove gameState) moves
            store ((choose currentPlayer (+) (-)) ((choose currentPlayer maximum minimum) scores) (length moves))
        where store score = do modify (M.insert gameState score); return score 
              moves = availableMoves gameState


