module Exercise_10 where import Data.List import Test.QuickCheck {-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 : rs) = psbr r ++ "\n" ++ prettyShowBoard rs psbr :: [Field] -> String psbr [] = "" psbr ((E) : fs) = "+" ++ psbr fs psbr ((P V) : fs) = "V" ++ psbr fs psbr ((P H) : fs) = "H" ++ psbr fs {-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 [] a) (x,y) = False isValidMove (Game b H) (x, y) = if x < height b && x >= 0 && y < (width b) - 1 && y >= 0 then b!!x!!y == E && b!!x!!(y+1) == E else False isValidMove (Game b V) (x, y) = if x < (height b)-1 && x >= 0 && y < (width b) && y >= 0 then b!!x!!y == E && b!!(x+1)!!y == E else False {-H10.1.3-} canMove :: Game -> Bool canMove (Game [] a) = False canMove (Game b a) = testMoves (Game b a) [(x, y) | x <- [0..(height b)], y <- [0..(width b)]] testMoves :: Game -> [(Int, Int)] -> Bool testMoves g [] = False testMoves g (p : ps) | isValidMove g p = True | otherwise = testMoves g ps {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard [] p f = [] updateBoard b (x, y) f = take x b ++ [(take y (b!!x) ++ [f] ++ drop (y+1) (b!!x))] ++ drop (x+1) b {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game [] a) p = (Game [] a) playMove (Game b H) (x, y) = (Game (updateBoard (updateBoard b (x, y) (P H)) (x, y+1) (P H)) V) playMove (Game b V) (x, y) = (Game (updateBoard (updateBoard b (x, y) (P V)) (x+1, y) (P V)) H) {-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-} getPossibleMoves :: Game -> [Pos] getPossibleMoves (Game b H) = [(x, y) | x <- [0..11], y <- [0..10], isValidMove (Game b H) (x, y)] getPossibleMoves (Game b V) = [(x, y) | x <- [0..10], y <- [0..11], isValidMove (Game b V) (x, y)] getNumberofEnemyPossibleMoves :: Game -> [Pos] -> [(Pos, Int)] getNumberofEnemyPossibleMoves (Game b a) ps = [(p, length (getPossibleMoves (playMove (Game b a) p))) | p <- ps] getMin :: [(Pos, Int)] -> (Pos, Int) -> Pos getMin [] p = fst p getMin (r : rs) (p, n) | snd r == 0 = fst r | snd r < n = getMin rs r | otherwise = getMin rs (p, n) christmasAI :: Strategy -- receives a game and plays a move for the next player christmasAI (r:rs) (Game b a) = getMin (getNumberofEnemyPossibleMoves (Game b a) (getPossibleMoves (Game b a))) ((0,0), 1000) {-TTEW-} generateBoard :: Int -> Int-> Board generateBoard 0 z = [] generateBoard x z | x < 0 || z < 0 = [] | otherwise = [generateRow z] ++ generateBoard (x-1) z generateRow :: Int -> Row generateRow 0 = [] generateRow x = [E] ++ generateRow (x-1) {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play [] dim sv sh = ([], H) play rss dim sv sh = let x = (playhelp (Game (generateBoard dim dim) V) rss sv sh) in (getBoardList x, getWinner x) getBoardList :: [(Board, Player)] -> [Board] getBoardList [] = [] getBoardList (x : xs) = [fst x] ++ getBoardList xs getWinner :: [(Board, Player)] -> Player getWinner [] = H getWinner [a] = snd a getWinner (a : as) = getWinner as getPossibleMoves2 :: Game -> [Pos] getPossibleMoves2 (Game b H) = [(x, y) | x <- [0..(width b)], y <- [0..(length b)], isValidMove (Game b H) (x, y)] getPossibleMoves2 (Game b V) = [(x, y) | x <- [0..(width b)], y <- [0..(length b)], isValidMove (Game b V) (x, y)] playhelp :: Game -> [[Double]] -> Strategy -> Strategy -> [(Board, Player)] playhelp (Game b V) (r : rs) sv sh | getPossibleMoves2 (Game b V) == [] = [] | isValidMove (Game b V) (sv r (Game b V)) = [(getBoard (playMove (Game b V) (sv r (Game b V))), V)] ++ playhelp (playMove (Game b V) (sv r (Game b V))) rs sv sh | otherwise = [] playhelp (Game b H) (r : rs) sv sh | getPossibleMoves2 (Game b H) == [] = [] | isValidMove (Game b H) (sh r (Game b H)) = [(getBoard (playMove (Game b H) (sh r (Game b H))), H)] ++ playhelp (playMove (Game b H) (sh r (Game b H))) rs sv sh | otherwise = [] getBoard :: Game -> Board getBoard (Game b a) = b -- 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"