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 board = addLineBreaks $ map (\row -> concat $ map (\field -> toString field) row) board where toString (P H) = "H" toString (P V) = "V" toString E = "+" addLineBreaks [] = "" addLineBreaks (x:xs) = x ++ "\n"++ addLineBreaks 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 board p) (r, c) | p == H = lookupPos board (r, c) == Just E && lookupPos board (r, c+1) == Just E | p == V = lookupPos board (r, c) == Just E && lookupPos board (r+1, c) == Just E lookupPos::Board -> Pos -> Maybe Field lookupPos board (r,c) = if r < 0 || c < 0 || null rowRest then Nothing else Just (head rowRest) where row = take 1 $ drop r board rowRest = if null row then [] else drop c (head row) {-H10.1.3-} canMove :: Game -> Bool canMove (Game [] p) = False canMove (Game board p) = or [isValidMove (Game board p) (r,c)|r<-[0..length board -1],c<-[0..length (head board)-1]] {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard [] p f = [] updateBoard (row:rows) (r,c) f | r>0 = row:(updateBoard rows (r-1,c) f) | otherwise = ((take c row) ++ [f] ++ (drop (c+1) row)):rows {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game board H) (r,c) = Game (updateBoard (updateBoard board (r,c) (P H)) (r,c+1) (P H)) V playMove (Game board V) (r,c) = Game (updateBoard (updateBoard board (r,c) (P V)) (r+1,c) (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-} --christmasAI :: Strategy -- receives a game and plays a move for the next player -- simple strat: wähle den Spielzug, der dem Gegner in seinem nächsten Zug die geringste Anzahl an möglichen Spielzügen übrig lässt christmasAI d (Game board p) = getMin [((r,c),length (getValidMoves (playMove (Game board p) (r,c))))|r<-[0..length board -1],c<-[0..length (head board)-1],isValidMove (Game board p) (r,c)] where getMin xs = aux xs (minimum $ map (\((r,c),numPossibleMoves)->numPossibleMoves) xs) where aux ((((r,c),num)):xs) min | num == min = (r,c) | otherwise = aux xs min {-TTEW-} invalStrat d g = (-1,1) getValidMoves::Game -> [Pos] --returns list of all possible moves getValidMoves (Game board p) = [(r,c)|r<-[0..length board -1],c<-[0..length (head board)-1],isValidMove (Game board p) (r,c)] generateBoard::Int->Board -- generates empty board generateBoard dim = [[E|c<-[1..dim]]|r<-[1..dim]] {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play rss dim sv sh = playing rss ([generateBoard dim],H) sv sh where playing (rs:rss) (boards,lastPlayer) sv sh | null nextBoard = (drop 1 boards,lastPlayer) --drop empty board | otherwise = playing rss (boards++[nextBoard],p) sv sh where curGame@(Game b p) = Game (last boards) (opposite lastPlayer) strat = if p==H then sh else sv nextMove = strat rs curGame Game nextBoard _ = if canMove curGame && isValidMove curGame nextMove then playMove curGame nextMove else Game [] V opposite :: Player -> Player opposite H = V opposite V = H -- 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"