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 (x:xs) = (showField x) ++ prettyShowBoard xs where showField [] = "\n" showField ((P H):xs) = "H" ++ (showField xs) showField ((P V):xs) = "V" ++ (showField xs) showField ((E):xs) = "+" ++ (showField xs) {-H10.1.2-} -- position on a board (row, column) -- (0,0) corresponds to the top left corner type Pos = (Int, Int) getField :: Board -> (Int, Int) -> Field getField board (r, c) = (row board r) !! c isValidMove :: Game -> Pos -> Bool isValidMove (Game [] _) _ = False isValidMove (Game board H) (r, c) | r < 0 || c < 0 || r >= (width board) || c >= (height board) || c + 1 >= (height board) = False | ((getField board (r,c)) == E) && ((getField board (r,c + 1)) == E) = True | otherwise = False isValidMove (Game board V) (r, c) | r < 0 || c < 0 || r >= (width board) || c >= (height board) || (r+1) >= (width board)= False | ((getField board (r,c)) == E) && ((getField board (r+1, c)) == E) = True | otherwise = False {-H10.1.3-} canMove :: Game -> Bool canMove (Game board p) = or [ isValidMove (Game board p) (i, j) | i <- [0..((width board) - 1)], j <- [0..((height board) - 1)]] {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard board (r, c) field = let row_to_replace = row board r modified_row = replace c field row_to_replace in replace r modified_row board replace :: Int -> a -> [a] -> [a] replace i elem (x:xs) | i == 0 = elem:xs | otherwise = x:replace (i-1) elem xs {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game board V) (r, c) = Game (updateBoard (updateBoard board (r,c) (P V)) (r+1, c) (P V)) H playMove (Game board H) (r, c) = Game (updateBoard (updateBoard board (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 xs game = search' minVal l where minVal = findMin l pos = [ (r, c) | r <- [0..12], c <- [0..12], isValidMove game (r, c)] l = [(length [(r, c) | r <- [0..12], c <- [0..12], isValidMove (playMove game position) (r, c)] , position) | position <- pos] findMin :: Ord a => Ord b => [(a, b)] -> a findMin l = minimum (fmap fst l) search' :: Int -> [(Int, Pos)] -> Pos search' _ [] = (0, 0) search' val (x:xs) = if val == fst x then snd x else search' val xs {-TTEW-} {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play xs dim sv sh = (boards, winner) where (boards, winner) = playStrategies xs (Game (create dim) V) sv sh playStrategies :: [[Double]] -> Game -> Strategy -> Strategy -> ([Board], Player) playStrategies (x:xs) game sv sh | (canMove game) && getPlayer game == V && isValidMove game posV = (((getBoard (playMove game posV)):(fst (playStrategies xs (playMove game posV) sv sh))), (snd (playStrategies xs (playMove game posV) sv sh))) | (canMove game) && getPlayer game == H && isValidMove game posH= (((getBoard (playMove game posH)):(fst (playStrategies xs (playMove game posH) sv sh))), (snd (playStrategies xs (playMove game posV) sv sh))) | otherwise = ([], winner) where posV = sv x game posH = sh x game winner = if getPlayer game == H then V else H getBoard :: Game -> Board getBoard (Game b _) = b getPlayer :: Game -> Player getPlayer (Game _ p) = p create :: Int -> Board create dim = [[E | x <- [1..dim]] | xs <- [1..dim]] -- 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"