module Game(
        boardFromMinmax
    )where

import Data.Maybe
import Data.Tree
import Data.List
import Data.Map as Map
import Board
import Control.Parallel.Strategies

rateBoard:: Board->Color->Int
rateBoard (Board innerMap) color = 
    (rate $ coordinatesOfColor color) - (rate $ coordinatesOfColor $ negateColor color)
        where
            rate listOfCoordinates = sum (Prelude.map sumPoints (rateDirections listOfCoordinates))
            coordinatesOfColor color = [x | x <- Map.keys innerMap, fromJust (Map.lookup x innerMap) == color]

rateDirections:: [Coordinates]->[[Int]]
rateDirections [] = [[0]]
rateDirections (coords:rest) = parMap r0 (rateDirection coords (coords:rest) 0) [(x,y) | x<-[0..1], y<-[-1..1], not(x==0 && y==(-1)), not(x==0 && y==0)]

rateDirection:: Coordinates->[Coordinates]->Int->(Int,Int)->[Int]
rateDirection _ [] seq (_,_) = [seq]
rateDirection (Coordinates x y) ((Coordinates x1 y1):rest) seq (xTranslation, yTranslation)
    | (Coordinates x y) `elem` ((Coordinates x1 y1):rest) = rateDirection (Coordinates (x+xTranslation) (y+yTranslation)) (Data.List.delete (Coordinates x y) ((Coordinates x1 y1):rest)) (seq+1) (xTranslation, yTranslation)
    | otherwise = [seq] ++ rateDirection (Coordinates (x1+xTranslation) (y1+yTranslation)) rest 1 (xTranslation, yTranslation)

sumPoints:: [Int]->Int
sumPoints [] = 0
sumPoints (xs:x)
    | xs == 5 = 2000 + (sumPoints x)
    | xs == 4 = 50 + (sumPoints x)
    | xs == 3 = 15 + (sumPoints x)
    | xs == 2 = 5 + (sumPoints x)
    | otherwise = 0 + (sumPoints x)

rateBoards:: [Tree Board]->Color->[Int]
rateBoards [] _ = []
rateBoards ((Node currentNode _):listOfBoards) color = [rateBoard currentNode color] ++ rateBoards listOfBoards color

possibleMoves:: Board->[Coordinates]
possibleMoves (Board map) = [Coordinates x y | x<-[1..19], y<-[1..19], notMember (Coordinates x y) map, hasNeighbors (Coordinates x y) (Map.keys map)]

buildTree:: Color->Board->[Coordinates]->Tree Board
buildTree color board neighbors = Node board (children neighbors)
    where
        newNeighbors coords = addIfNotPresent (Data.List.delete coords neighbors) $ neighborsOfCoordinates coords board
        children [] = [] 
        children (x:xs)= [buildTree (negateColor color) (addToBoard board x color) (newNeighbors x)] ++ children xs

addIfNotPresent:: [Coordinates]->[Coordinates]->[Coordinates]
addIfNotPresent list [] = list
addIfNotPresent list (x:xs)
    | x `elem` list = [] ++ addIfNotPresent xs list
    | otherwise = [x] ++ addIfNotPresent xs list

minmaxAlfa:: Color->Int->Int->Int->(Tree Board)->Int
minmaxAlfa color level alfa beta (Node a (x:xs))
    |level == 0 = finalRate
    |fiveSomewhere finalRate = finalRate
    |newAlfa >= beta = beta
    |otherwise = minmaxAlfa color level newAlfa beta (Node a xs) 
    where 
        finalRate = rateBoard a color
        newAlfa = maximum $ [alfa, (minmaxBeta color (level-1) alfa beta x)]
minmaxAlfa _ _ alfa _ (Node _ []) = alfa

minmaxBeta:: Color->Int->Int->Int->(Tree Board)->Int
minmaxBeta color level alfa beta (Node a (x:xs)) 
    |level == 0 = finalRate
    |fiveSomewhere finalRate = finalRate
    |alfa >= newBeta = alfa
    |otherwise = minmaxBeta color level alfa newBeta (Node a xs)
    where 
        finalRate = rateBoard a color
        newBeta = minimum $ [beta, (minmaxAlfa color (level-1) alfa beta x)]
minmaxBeta _ _ _ beta (Node _ []) = beta

fiveSomewhere::Int->Bool
fiveSomewhere rate = rate > 2000 || rate < (-2000)

minInt:: Int
minInt = (-(2^29))

maxInt:: Int
maxInt = (2^29-1)

boardFromMinmax:: Board->Color->Board
boardFromMinmax board color 
    | length neighbors == 0 = addToBoard board (Coordinates 10 10) color
    | otherwise = bestBoard
    where
        neighbors = possibleMoves board
        (Node node children) = buildTree color board neighbors
        minMaxMoves = parMap r0 (minmaxBeta color 3 minInt maxInt) children
        index = fromJust $ elemIndex (maximum minMaxMoves) minMaxMoves
        (Node bestBoard _ ) = children !! index