module Exercise_10 where import Data.List {-H10.1-} data Player = V | H -- vertical or horizontal player deriving (Eq,Show) data Field = P Player | E -- a field is occupied by a player or empty deriving (Eq,Show) type Row = [Field] type Column = [Field] type Board = [Row] -- we assume that boards are squares and encode a board row by row data Game = Game Board Player -- stores the currenty board and player deriving (Eq,Show) -- get a given row of a board row :: Board -> Int -> Row row = (!!) -- get a given column of a board column :: Board -> Int -> Column column = row . transpose -- width of a board width :: Board -> Int width [] = 0 width (x:xs) = length x -- height of a board height :: Board -> Int height = length {-H10.1.1-} prettyShowBoard :: Board -> String prettyShowBoard [] = "" prettyShowBoard (r:rows) = (rowtoString r) ++ "\n" ++ (prettyShowBoard rows) rowtoString [] = "" rowtoString (E:xs) = "+" ++ rowtoString xs rowtoString ((P V):xs) = "V" ++ rowtoString xs rowtoString ((P H):xs) = "H" ++ rowtoString xs {-H10.1.2-} -- position on a board (row, column) -- (0,0) corresponds to the top left corner type Pos = (Int, Int) isValidMove :: Game -> Pos -> Bool isValidMove (Game b V) (r, c) = r >= 0 && c >= 0 && r < (height b) - 1 && c < (width b) && isEmpty b (r, c) && isEmpty b (r + 1, c) isValidMove (Game b H) (r, c) = r >= 0 && c >= 0 && r < (height b) && c < (width b) - 1 && isEmpty b (r, c) && isEmpty b (r, c + 1) isEmpty b (r, c) = if r < 0 || c < 0 || r >= (height b) || c >= (width b) then False else b!!r!!c == E {-H10.1.3-} canMove :: Game -> Bool canMove g = cm g (0,0) cm (Game b p) (r, c) = if r >= (height b) then False else if c >= (width b) then cm (Game b p) (r+1, 0) else (isValidMove (Game b p) (r, c)) || cm (Game b p) (r, c+1) {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard b (r, c) f = let (xs,_:ys) = splitAt r b (as,_:bs) = splitAt c (row b r) in xs ++ ((as ++ (f : bs)) : ys) {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game b (V)) (r, c) = Game (updateBoard (updateBoard b (r, c) (P V)) (r + 1, c) (P V)) (H) playMove (Game b (H)) (r, c) = Game (updateBoard (updateBoard b (r, c) (P H)) (r, c + 1) (P H)) (V) {-H10.1.6-} -- the first paramter of a strategy is an infite list of -- random values between (0,1) (in case you wanna go wild with -- probabilistic methods) type Strategy = [Double] -> Game -> Pos {-WETT-} christmasAI :: Strategy -- receives a game and plays a move for the next player christmasAI _ (Game b p) = let m = maximum [computeScore (Game b p) (r, c) | r <- [0..(height b)], c <- [0..(width b)], isValidMove (Game b p) (r, c)] in head [(r, c) | r <- [0..(height b)], c <- [0..(width b)], isValidMove (Game b p) (r, c), (computeScore (Game b p) (r, c)) >= m] computeScore :: Game -> Pos -> Int computeScore (Game b V) (r, c) = let v = 3 w = 4 x = 6 y = -2 z = 0 in (if isEmpty b (r - 1, c) then (if not (isEmpty b (r - 2, c)) then y else z) else w) + (if isEmpty b (r + 2, c) then (if not (isEmpty b (r + 3, c)) then y else z) else w) + (if isEmpty b (r, c - 1) then (if not (isEmpty b (r, c - 2)) then x else v) else 0) + (if isEmpty b (r, c + 1) then (if not (isEmpty b (r, c + 2)) then x else v) else 0) + (if isEmpty b (r + 1, c - 1) then (if not (isEmpty b (r + 1, c - 2)) then x else v) else 0) + (if isEmpty b (r + 1, c + 1) then (if not (isEmpty b (r + 1, c + 2)) then x else v) else 0) computeScore (Game b H) (r, c) = computeScore (Game (transpose b) V) (c, r) {-TTEW-} {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play = undefined -- generates infinite list of values between (0,1) -- genRandomZeroOne :: Gen [Double] -- genRandomZeroOne = mapM (const $ choose (0::Double,1)) [1..] -- plays a game and prints it to the console {-playAndPrint :: Int -> Strategy -> Strategy -> IO () playAndPrint dim sh sv = do rss <- generate $ mapM (const $ genRandomZeroOne) [1..] let (bs, w) = play rss dim sh sv putStr $ (unlines $ map prettyShowBoard bs) ++ "\nWinner: " ++ show w ++ "\n"-}