import Control.Monad
import Data.List.Index
import Data.List

data Player = PX | PO deriving (Eq, Show)

data Square = SEmpty | SFull Player deriving Eq

instance Show Square where
    show SEmpty     = "_"
    show (SFull PO) = "O"
    show (SFull PX) = "X"

opposite :: Player -> Player
opposite PX = PO
opposite PO = PX

-- dependent type 'list of 9 squares' would be better, but alas
newtype Board = Board [Square] deriving (Eq)

-- | Split a list into chunks of /n/ elements
chunkList :: Int -> [a] -> [[a]]
chunkList _ [] = []
chunkList n xs = as : chunkList n bs where (as,bs) = splitAt n xs

instance Show Board where
    show (Board b) = "\n\n" ++ intercalate "\n" (show <$> chunkList 3 b)

getPossibleMoves :: Board -> [Int]
getPossibleMoves (Board b) = elemIndices SEmpty b

startingBoard :: Board
startingBoard = Board (setAt 4 (SFull PX) . replicate 9 $ SEmpty)

play :: Board -> Int -> Player -> Board
play (Board b) idx p = Board $ setAt idx (SFull p) b

playAll :: Board -> Player -> [Board]
playAll b p = do
    move <- getPossibleMoves b
    pure (play b move p)

q :: Board -> Player -> Int -> Int
q board player move = sum $ do
    let board'  = play board move player
        player' = opposite player
    board'' <- playAll board' player'
    pure . maximum $ q board'' player <$> getPossibleMoves board''

main :: IO ()
main = print (playAll startingBoard PO)
