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) = (showLine r) ++ "\n" ++ prettyShowBoard rs where showLine [] = "" showLine ((P player):xs) = (show player) ++ showLine xs showLine ((E):xs) = "+" ++ showLine 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) (x,y) |board == [] = False |length board == 1 = False |length board <= x = False |length board <= (x+1) && p == V = False |length board <= (y+1) && p == H = False |length board <= y = False |x < 0 || y < 0 = False |p == H = (board !! x) !! y == E && (board !! x) !! (y+1) == E |p == V = (board !! x) !! y == E && (board !! (x+1)) !! y == E {-H10.1.3-} canMove :: Game -> Bool canMove (Game [] _) = False canMove (Game (r:rs) H) = checkRow r rs where checkRow [] rs = canMove (Game rs H) checkRow [x] rs = canMove (Game rs H) checkRow (x:y:xs) rs = if x == E && y == E then True else checkRow (y:xs) rs canMove (Game (r:rs) V) = canMove (Game (transpose (r:rs)) H) {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard [] _ _ = [] updateBoard (r:rs) (x,y) val |x > 0 = r : updateBoard rs ((x-1),y) val |otherwise = (change r y val) : rs where change [] _ _ = [] change (x:xs) 0 val = val:xs change (x:xs) y val = x : (change xs (y-1) val) {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game board V) (x,y) = (Game (updateBoard (updateBoard board (x,y) (P V)) ((x+1),y) (P V)) H) playMove (Game board H) (x,y) = (Game (updateBoard (updateBoard board (x,y) (P H)) (x,(y+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 ds (Game board p) = picker (Game board p) (1,0) 4 where picker (Game board p) (x,y) val |val == (-1) = (0,0) --Abbruch, falls kein freies Feld gefunden wird |x == (height board) = picker (Game board p) (0,0) (val - 1) --wurde das board durchlaufen, beginne Suche erneut mit geringerem Anspruch |sum (map (\x -> if x then 1 else 0) (adjs (Game board p) (x,y))) == val && isValidMove (Game board p) (x,y) = (x,y) --blockiert der Zug möglichst viele gegnerische Züge, wäähle diesen Zug |(y+1) < (height board) = picker (Game board p) (x,(y+1)) val--gewählter Zug war nicht gut genug, rechts ist noch ein Feld frei -> probioere rechten Platz |otherwise = picker (Game board p) ((x+1),0) val --ist rechts kein Platz mehr, beginne Suche in neuer Zeile adjs (Game board H) (x,y) = [isValidMove (Game board V) (a ,b) | a <- [(x-1), x] , b <- [y,(y+1)]] adjs (Game board V) (x,y) = [isValidMove (Game board H) (a ,b) | a <- [x, (x+1)] , b <- [(y-1),y]] --prüft, wieviele Plätze Spieler H genom,men werden mit diesem Zug {-TTEW-} getBoard :: Game -> Board getBoard (Game board _) = board {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play (xs:xss) size sv sh = play2 (xs:xss) (Game (replicate size (replicate size E)) V) sv sh [] where play2 (xs:xss) (Game board p) sv sh states |not (canMove (Game board p)) = if p == H then ((reverse states), V) else ((reverse states), H) --kann ein Spieler keinen Zug mehr spielen, hat der andere gewonnen |p == V && isValidMove (Game board p) (sv xs (Game board p)) = play2 xss (playMove (Game board p) (sv xs (Game board p))) sv sh ((getBoard(playMove (Game board p) (sv xs (Game board p)))):states) --korrekter Zug, update gameboard und gib Zugrecht an anderen SPieler |p == H && isValidMove (Game board p) (sh xs (Game board p)) = play2 xss (playMove (Game board p) (sh xs (Game board p))) sv sh ((getBoard(playMove (Game board p) (sh xs (Game board p)))):states) |otherwise = if p == H then ((reverse states), V) else ((reverse states), H) --invalid Move, gegner gewinnt -- 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"