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) = aux x ++ "\n" ++ prettyShowBoard xs where aux [] = "" aux (x:xs) = printField x ++ aux xs printField :: Field -> String printField (P H) = "H" printField (E) = "+" printField (P V) = "V" {-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 b H) (r, c) = if (r >= 0 && r < (height b) && c >= 0 && c < ((width b)-1)) then ((row b r) !! c) == E && ((row b r) !! (c + 1)) == E else False isValidMove (Game b V) (r, c) = if (r >= 0 &&r < ((height b)-1) && c >= 0 && c < (width b)) then ((column b c) !! r) == E && ((column b c) !! (r+1)) == E else False {-H10.1.3-} canMove :: Game -> Bool canMove (Game [] _) = False canMove g = aux g 0 0 where aux (Game b H) r c | r == (height b-1) && c == (width b-1) = False | c == (width b)-1 = aux (Game b H) (r+1) 0 | otherwise = (isValidMove (Game b H) (r, c)) || (aux (Game b H) r (c+1)) aux (Game b V) r c | r == (height b-1) && c == (width b-1) = False | r == (height b)-1 = aux (Game b V) 0 (c+1) | otherwise = (isValidMove (Game b V) (r, c)) || (aux (Game b V) (r+1) c) {-H10.1.4-} updateBoard :: Board -> Pos -> Field -> Board updateBoard xs (r, c) f = aux xs r c f 0 where aux [] _ _ _ _= [] aux (x:xs) r c f n = (if n == r then [setField x c 0 f] else [x]) ++ aux xs r c f (n+1) setField :: Row -> Int -> Int -> Field -> Row setField [] _ _ _ = [] setField (x:xs) c n f = (if n == c then [f] else [x]) ++ setField xs c (n+1) f {-H10.1.5-} playMove :: Game -> Pos -> Game playMove (Game b H) (r, c) = (Game (updateBoard (updateBoard b (r, c) (P H)) (r, (c+1)) (P H)) V) playMove (Game b V) (r, c) = (Game (updateBoard (updateBoard b (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 --christmasAI (z:zs) (Game b V) = -- let -- myThis = validPos (Game b V) -- -- enThis = getNumOfMoves (Game b V) myThis -- in -- aux (Game b V) myThis (findIndices (== minimum enThis) enThis) -- try to minimize the possible moves of the enemy -- where -- aux (Game b V) xs ys = xs !! (ys !! ((round (z*10))`mod`(length ys))) christmasAI _ g = --christmasAI _ (Game b H) = let -- myThis = validPos (Game b H) myThis = validPos g -- enThis = getNumOfMoves (Game b H) myThis enThis = getNumOfMoves g myThis in -- aux (Game b H) myThis (findIndex (== minimum enThis) enThis) -- try to minimize the possible moves of the enemy aux g myThis (findIndex (== minimum enThis) enThis) -- try to minimize the possible moves of the enemy where -- aux (Game b H) xs (Just p) = xs !! p aux g xs (Just p) = xs !! p validPos :: Game -> [Pos] validPos (Game b p) = [(x, y)|x<-[0..height b],y<-[0..width b], isValidMove (Game b p) (x,y)] --validCounterMoves :: Game -> [Pos] -> [[Pos]] --validCounterMoves _ [] = [] --validCounterMoves (Game b H) (x:xs) = [validPos (playMove (Game b V) x)] ++ validCounterMoves (Game b H) xs --validCounterMoves (Game b V) (x:xs) = [validPos (playMove (Game b H) x)] ++ validCounterMoves (Game b V) xs -- --myNextMoves :: Game -> [Pos] -> [[Pos]] -> [Int] --myNextMoves g [] [] = [] --myNextMoves g (x:xs) (y:ys) = [sum [length $ validPos(playMove(playMove g x) a)|a <- y]] ++ myNextMoves g xs ys getNumOfMoves :: Game -> [Pos] -> [Int] getNumOfMoves _ [] = [] getNumOfMoves (Game b H) (x:xs) = [length $ validPos (playMove (Game b H) x)] ++ getNumOfMoves (Game b H) xs getNumOfMoves (Game b V) (x:xs) = [length $ validPos (playMove (Game b V) x)] ++ getNumOfMoves (Game b V) xs {-TTEW-} -- b = [[E, E, E, P H, P H], [P V, P H, P H, E, E],[P V, E, E, P H, P H],[P V, P V, E, E, E],[P V, P V, E, P H, P H]] {-H10.1.7-} play :: [[Double]] -> Int -> Strategy -> Strategy -> ([Board],Player) --play = undefined play rss dim sv sh = let hist = tail (aux rss (Game (emptyBoard dim) V) sv sh) in (hist, if length hist `mod` 2 > 0 then V else H) where aux (x:xs) (Game b V) sv sh = if canMove (Game b V) && isValidMove (Game b V) (sv x (Game b V)) then [b] ++ aux xs (playMove (Game b V) (sv x (Game b V))) sv sh else [b] aux (x:xs) (Game b H) sv sh = if canMove (Game b H) && isValidMove (Game b H) (sh x (Game b H)) then [b] ++ aux xs (playMove (Game b H) (sh x (Game b H))) sv sh else [b] emptyBoard :: Int -> Board emptyBoard 0 = [] emptyBoard n = take n (repeat (take n (repeat E))) -- 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"