import Reversi import System.IO import System.Console.ANSI instance Show Piece where show Head = (setSGRCode [ SetColor Foreground Vivid Red ] ++ "H") show Tail = (setSGRCode [ SetColor Foreground Vivid Blue ] ++ "T") showGameFieldWithHighlighted:: [Pos] -> GameField -> String showGameFieldWithHighlighted highlighted field = concatWith "\n" ((map (showRow field) [0..(height field)-1]) ++ [setSGRCode [ SetColor Foreground Vivid White ]]) -- the last element is added to make sure that the color is switched back to white where showRow field row = concatMap showPos [(x,row) | x <- [0..(width field)-1]] concatWith separator items = (foldl (\a b -> a++separator++b) (head items) (tail items)) showPos pos = if pos `elem` highlighted then (setSGRCode [ SetColor Foreground Vivid Green ] ++ "█") else showPiece (get pos field) showPiece (Just p) = show p showPiece (Nothing) = (setSGRCode [ SetColor Foreground Vivid White ] ++ "—") --"—" {- helpText is displayed to the user when this player is about to play -} data GamePlayer = GamePlayer { play::(GameField -> IO Pos), pieceType::Piece, helpText::String } minimaxPlay:: Piece -> GameField -> IO Pos minimaxPlay piece field = let (pos,score) = minimax field piece 330 in do putStrLn (" Computer has chosen: " ++ (show pos)) putStrLn (" Press any key to let the computer play...") getChar return (pos) userPlay:: Piece -> GameField -> IO Pos userPlay piece field = do userPlay' 0 0 where isAllowed:: Pos -> Bool isAllowed pos = any (pos==) (getPossibleMoves piece field) userPlay':: Int -> Int -> IO Pos userPlay' x y = do setCursorPosition y x c <- getChar case c of 'w' -> if y > 0 then userPlay' x (y-1) else userPlay' x y 's' -> if (y+1) < (height field) then userPlay' x (y+1) else userPlay' x y 'a' -> if x > 0 then userPlay' (x-1) y else userPlay' x y 'd' -> if (x+1) < (width field) then userPlay' (x+1) y else userPlay' x y 'e' -> return (-1, -1) -- the end of the game is indicated by this val ' ' -> if (isAllowed (x,y)) then return (x,y) else userPlay' x y _ -> userPlay' x y game :: GameField -> GamePlayer -> GamePlayer -> IO () game field player1 player2 = let moves = getPossibleMovesWithEval (pieceType player1) field in let opponentMoves = getPossibleMoves (switchPiece $ pieceType player1) field in let movesPos = [pos | (start,pos,len) <- moves] in do clearScreen setCursorPosition 0 0 putStr (showGameFieldWithHighlighted movesPos field) setCursorPosition ((height field)+2) 0 if ((length moves) == 0) && ((length opponentMoves) == 0) then do putStrLn (" This is the end of the game: the winner is " ++ (show winner)) putStr (setSGRCode [ SetColor Foreground Vivid White ]) return () else do if (length moves) > 0 then do putStrLn (helpText player1) pos <- ((play player1) field) if pos == (-1,-1) then do return () -- when user ends the game deliberately, we don't show any message else do clearScreen newField <- (updateField' pos player1 field moves) game newField player2 player1 else do putStrLn (" This player cannot play...\n Press any key to continue...") getChar game field player2 player1 where updateField' :: Pos -> GamePlayer -> GameField -> [(Pos,Pos,Int)] -> IO GameField updateField' pos player field moves = let field' = push pos (pieceType player) field in let fields = flipLines pos moves field' in do showFields (field':fields) pos return (head $ reverse fields) showFields :: [GameField] -> Pos -> IO () showFields [] _ = do return () showFields (x:xs) pos = do clearScreen setCursorPosition 0 0 putStr (showGameFieldWithHighlighted [pos] x) delay 500000 showFields xs pos delay :: Int -> IO () delay 0 = return () delay x = do delay (x-1) winner :: Piece winner = let tailsCount = length $ filter (\(_,p) -> p == Tail) $ pieces field in let headsCount = length $ filter (\(_,p) -> p == Head) $ pieces field in if tailsCount > headsCount then Tail else Head main :: IO () main width height = let field = createGameField width height [((2,2),Head), ((3,3),Head), ((3,2),Tail), ((2,3),Tail)] in let computer = GamePlayer (minimaxPlay Head) Head " Computer is now thinking..." in let human = GamePlayer (userPlay Tail) Tail " Use w, s, d, a keys to navigate in the game field \n Press space to choose current the cell \n Press e to cancel the game" in do hSetEcho stdin False hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering game field human computer hSetEcho stdin True