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:_) = length x -- height of a board height :: Board -> Int height = length {-H10.1.1-} toChar ::Field->String toChar E = "+" toChar (P p) = show p toString :: Row -> String toString r = foldl (++) "" (map toChar r) ++ "\n" prettyShowBoard :: Board -> String prettyShowBoard b = foldl (++) "" $ map toString b {-H10.1.2-} -- position on a board (row, column) -- (0,0) corresponds to the top left corner type Pos = (Int, Int) getBoard :: Game -> Board getBoard (Game b _) = b getPlayer :: Game -> Player getPlayer (Game _ p) = p isValidMove :: Game -> Pos -> Bool isValidMove g (x,y) | x<0 || x >= height b || y<0 || y>=width b = False | getPlayer g == H && y+1>=width b = False | getPlayer g == V && x+1 >= height b = False | getPlayer g == H = b !! x !! y == E && if y< (width b)-1 then b !! x !! (y+1) == E else True | otherwise = b !! x !! y == E && if x< (height b)-1 then b !! (x+1) !! y == E else True where b = getBoard g {-H10.1.3-} canMove :: Game -> Bool canMove g | getPlayer g == H = width (getBoard g) >= 2 && or (map (\r->or $ zipWith (\a b->a==E && b ==E) r ((P V):r)) (getBoard g)) | otherwise = height (getBoard g) >= 2 && or ( map (\r->or $ zipWith (\a b->a==E && b ==E) r ((P V):r)) (transpose $ getBoard g) ) {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard b (x,y) f = zipWith (\r n -> if n/=x then r else (zipWith (\t m -> if m/=y then t else f) r [0..] )) b [0..] {-H10.1.5-} playMove :: Game -> Pos -> Game playMove g (x,y) | getPlayer g == H = Game ( updateBoard (updateBoard b (x,y) (P H)) (x,y+1) (P H) ) V | otherwise = Game ( updateBoard (updateBoard b (x,y) (P V)) (x+1,y) (P V) ) H where b = getBoard g {-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-} countPos :: Row ->Int countPos r = length ( filter (==(E,E)) (zip r ((P H):r))) heuristic :: Game -> Int heuristic g | getPlayer g == H = (sum $ map countPos b ) - (sum $ map countPos (transpose b)) | otherwise = (sum $ map countPos (transpose b))- (sum $ map countPos b) where b = getBoard g alphabeta :: Game -> Int -> Int -> Int -> (Pos,Int) alphabeta g _ _ 0 = ((-1,-1),heuristic g) alphabeta g a b depth | depth `mod` 2 == 0 = foldl maxFun ((-1,-1),a) [(x,y)|x<-[0..11],y<-[0..11],isValidMove g (x,y)] | otherwise = foldl minFun ((-1,-1),b) [(x,y)|x<-[0..11],y<-[0..11],isValidMove g (x,y)] where minFun :: (Pos,Int) -> Pos -> (Pos,Int) minFun (p,beta) np | beta < a = (p,beta) | x Pos -> (Pos,Int) maxFun (p,alpha) np | alpha > b = (p,alpha) | x>alpha = (np,x) | otherwise = (p,alpha) where (_,x) = alphabeta (playMove g np) alpha b (depth-1) christmasAI :: Strategy -- receives a game and plays a move for the next player christmasAI _ g = fst $ alphabeta g (-200) 200 2 {-TTEW-} {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) play rss dim sv sh = (map getBoard list, if player == H then V else H ) where player = getPlayer(head(reverse list)) list = (map fst $ takeWhile (\(g,_) -> getBoard g /= []) $ iterate playStrat ((Game startBoard H),0)) startBoard = take dim (repeat (take dim $ repeat E)) playStrat ::(Game,Int)->(Game,Int) playStrat (g,index) | getPlayer g == V = if canMove g then (playMove g (sv (rss!!index) g),index+1) else (Game [] H,index+1) | otherwise = if canMove g then (playMove g (sh (rss!!index) g),index+1) else (Game [] V,index+1) -- 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"