import Graphics.Gloss import Graphics.Gloss.Interface.IO.Game import System.Random import System.Random.Shuffle (shuffle') import Data.Fixed (mod') -- Geeft de richting van een zet aan. type Direction = (Int, Int) -- De status van een kaart. Een kaart kan ofwel zijn kleur -- tonen, ofwel zijn achterkant tonen. data CardStatus = Hidden | Shown deriving(Show, Eq) -- Een positie op het speelveld. type Coordinate = (Int, Int) -- Representatie van een kaart. Een kaart heeft een status, -- een kleur en een positie. type Card = (Coordinate, Color, CardStatus) -- Representatie van het speelveld. data Board = Board { -- Het speelveld is een lijst van kaarten. cards :: [Card], -- Hou de omgedraaide kaarten tijdens een beurt bij (maximaal 2). turned :: [Card], -- Hou de huidige geselecteerde kaart bij. selector :: Coordinate } -- Aantal kaarten op de x-as. width :: Int width = 3 -- Aantal kaarten op de y-as. height :: Int height = 3 -- De grootte van een kaart. scaling :: Int scaling = 150 -- De grootte van de ruimte tussen de kaarten. cardInset :: Int cardInset = 10 -- Initiele positie van het Gloss venster. windowPosition :: (Int, Int) windowPosition = (200, 200) -- Seed voor de random generator. seed :: Int seed = 45 -- Framerate van het spel. fps :: Int fps = 60 -- Bereken het aantal kaarten op het speelveld. amountOfCards :: Int amountOfCards | even n = n | otherwise = n - 1 where n = width * height -- Het Gloss venster window :: Display window = InWindow "Memory" (width * scaling, height * scaling) windowPosition -- Het initiele speelveld. initBoard :: Board initBoard = Board { cards = generateShuffledCards amountOfCards, turned = [], selector = (0, 0) } ---------------------------------------------------------------------- -- Vanaf hier zal het nodig zijn om de functies aan te vullen. -- De functies die je moet aanvullen zijn steeds gemarkeerd met -- undefined. ---------------------------------------------------------------------- -- De mogelijke richtingen van de selector. left, right, up, down :: Direction left = ((-1), 0 ) right = ( 1 , 0 ) up = ( 0 , 1 ) down = ( 0 ,(-1)) -- Controleer of twee kaarten dezelfde kleur hebben. match :: Card -> Card -> Bool match (_, c1, _) (_, c2, _) = c1 == c2 -- Zoek een kaart binnen een lijst van kaarten op basis van een positie. -- Wanneer een kaart gevonden is, wordt deze teruggegeven. Anders wordt -- een error teruggegeven. find :: Coordinate -> [Card] -> Card find c0 cards = head $ filter ((c0 ==).firstOfThree) cards -- Geef een permutatie van een gegeven lijst terug. -- Hint: Kijk zeker eens naar de System.Random en -- System.Random.Shuffle bibliotheken. shuffleList :: [a] -> [a] shuffleList l = shuffle' l (length l) (mkStdGen seed) -- Genereer een lijst met n verschillende kleuren. -- Hint: Je kan gebruikmaken van de generateColor-functie. generateColors :: Int -> [Color] generateColors n = map (generateColor.fromIntegral.(div 360 n *)) [1..n] -- Genereer een lijst van n kaarten (n/2 kleurenparen). generateShuffledCards :: Int -> [Card] -- TODO Dit moet nog verder uitgeklaard worden !! -- TODO Hoe bepalen we de coördinaten van de kaarten? generateShuffledCards n = map (\(x,y) -> (x,y,Hidden)) $ zip (zip [1..n] [1..n]) colors where colors = shuffleList $ colorsOnce ++ colorsOnce colorsOnce = generateColors (n `div` 2) -- Controleer of een positie op het spelbord een kaart bevat. -- Om de kaarten van het huidige speelbord op te vragen, gebruik -- 'cards initBoard' hasCard :: Coordinate -> Bool hasCard c0 = any ((c0 ==).firstOfThree) $ cards initBoard -- Controleer of de selector vanaf een gegeven locatie in een -- gegeven richting kan bewegen. canMove :: Coordinate -> Direction -> Bool canMove (w, h) (dw, dh) = hasCard $ (w + dw, h + dh) -- Beweeg de selector in een gegeven richting. -- TODO unfinished move :: Board -> Direction -> Board move board (dw, dh) | canMove (selector board) (dw, dh) = newBoard -- where newBoard = (cards board, turned board, selector board) :: Board where newBoard = initBoard -- Verander de status van een kaart op een gegeven positie -- wanneer de posities overeenkomen. changeCard :: Coordinate -> CardStatus -> Card -> Card changeCard c0 s0 (c1, color, s1) | c0 == c1 = (c0, color, s0) | otherwise = (c1, color, s1) -- Verander de status van een enkele kaart in een reeks van -- kaarten. Deze functie geeft een lijst terug waar de status -- van de kaart is aangepast naar `Shown`. showCard :: Coordinate -> [Card] -> [Card] showCard target = map (changeCard target Shown) -- Verander de status van een enkele kaart in een reeks van -- kaarten. Deze functie geeft een lijst terug waar de status -- van de kaart is aangepast naar `Hidden`. hideCard :: Coordinate -> [Card] -> [Card] hideCard target = map (changeCard target Hidden) -- Draai de kaart op een gegeven positie op het bord om -- als deze nog niet eerder werd omgedraaid. flipCard :: Coordinate -> Board -> Board flipCard target board | target `elem` (map firstOfThree (turned board)) = board -- Controleer al omgedraaid? | otherwise = board { cards = newCards -- Toon de kaart in de lijst van kaarten. , turned = flipped ++ turned board -- Voeg toe aan 'omgedraaide kaarten'. } where newCards = showCard target $ cards board flipped = filter ((target ==).firstOfThree) newCards -- Reset de laatste omgedraaide kaarten terug naar de `Hidden` status. resetTurned :: Board -> Board resetTurned board | turned board == [] = board | otherwise = resetTurned board { cards = hideCard target (cards board) , turned = tail $ turned board } where target = (firstOfThree) $ head $ turned board -- Bereken het volgende bord op basis van de omgedraaide kaarten. -- Hint: We hebben de drie gevallen voor deze functie al voorzien. nextBoard :: Board -> Board nextBoard b@Board{ turned = [] } = undefined nextBoard b@Board{ turned = [c1] } = undefined nextBoard b@Board{ turned = [c1, c2] } = undefined -- Zet een positie op het bord om naar een positie op het scherm. -- Hint: hou zeker rekening met het coordinatensysteem van Gloss. convert :: Int -> Int -> Float convert location axis = undefined -- Render een vierkant met een gegeven kleur en grootte. renderColoredSquare :: Int -> Color -> Picture renderColoredSquare size c = undefined -- Render de selector. renderSelector :: Coordinate -> Picture renderSelector coord = undefined -- Render een kaart. renderCard :: Card -> Picture renderCard card = undefined -- Render alle kaarten. renderCards :: [Card] -> Picture renderCards = undefined -- Render het speelveld. render :: Board -> Picture render board = undefined -- Hulpfunctie die nagaat of een bepaalde toets is ingedrukt. isKey :: SpecialKey -> Event -> Bool isKey k1 (EventKey (SpecialKey k2) Down _ _) = k1 == k2 isKey _ _ = False -- Handel alle toetsaanslagen af. -- Hint: Je kan gebruikmaken van de isKey hulpfunctie. handleInput :: Event -> Board -> Board handleInput ev board = undefined -- Startpunt main :: IO () main = play window white fps initBoard render handleInput step ---------------------------------------------------------------------- -- Hieronder staan een aantal hulpfuncties die je kan gebruiken. ---------------------------------------------------------------------- -- Representatie van een HSL-kleur. type HSL = (Float, Float, Float) -- Representatie van een RGB-kleur. type RGB = (Float, Float, Float) -- Omzetting van de HSL-kleurenruimte naar de RGB-kleurenruimte. hslToRgb :: HSL -> RGB hslToRgb (h, s, l) = (r + m, g + m, b + m) where h' = h / 60 c = (1 - abs (2 * l - 1)) * s x = c * (1 - abs (h' `mod'` 2 - 1)) m = l - c / 2 getRGB h | h < 1 = (c, x, 0) | h < 2 = (x, c, 0) | h < 3 = (0, c, x) | h < 4 = (0, x, c) | h < 5 = (x, 0, c) | otherwise = (c, 0, x) (r, g, b) = getRGB h' -- Genereer een kleur op basis van een hue-waarde [0 - 360]. generateColor :: Float -> Color generateColor hue = makeColor r g b 1 where (r, g, b) = hslToRgb (hue, 0.5, 0.5) -- Update het bord in elke stap. step :: Float -> Board -> Board step _ b = b -- Haal het eerste element uit een drietupel, bijvoorbeeld om de -- coördinaten uit een kaart-object te halen. firstOfThree :: (a,b,c) -> a firstOfThree (a,_,_) = a