HTicTacToe (empty) → 0.1
raw patch · 16 files changed
+935/−0 lines, 16 filesdep +SDLdep +SDL-imagedep +SDL-ttfsetup-changedbinary-added
Dependencies added: SDL, SDL-image, SDL-ttf, array, base, mtl, random
Files
- Alice_in_WonderLand_3.ttf binary
- Draw.hs +135/−0
- FontReadMe.txt +47/−0
- GameEnv.hs +64/−0
- GameState.hs +182/−0
- Grid.hs +109/−0
- HTicTacToe.cabal +66/−0
- HTicTacToe.hs +188/−0
- LICENSE +21/−0
- README +31/−0
- Setup.hs +2/−0
- Surface.hs +90/−0
- o.png binary
- oWin.png binary
- x.png binary
- xWin.png binary
+ Alice_in_WonderLand_3.ttf view
binary file changed (absent → 81252 bytes)
+ Draw.hs view
@@ -0,0 +1,135 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+module Draw where++import Control.Monad+import Control.Monad.Trans++import Graphics.UI.SDL hiding (flip)+import qualified Graphics.UI.SDL as SDL (flip)++--import Graphics.UI.SDL.Image++import Graphics.UI.SDL.TTF++import Grid+import GameState hiding (font, screen, grid, winner, stats)+import GameEnv+import Surface++textColor :: Color+textColor = Color 0x00 0xFF 0x00++drawPlayersKey :: AppEnv ()+drawPlayersKey = do+ playerPiece <- getPlayerType+ let aiPiece = swapCell playerPiece+ + font <- getFont+ screen <- getScreen+ + liftIO $ do + playerKey <- renderTextSolid font ("Player: " ++ show playerPiece) textColor+ aiKey <- renderTextSolid font ("CPU: " ++ show aiPiece) textColor+ applySurface 10 10 playerKey screen Nothing+ applySurface 10 (10 + surfaceGetHeight playerKey) aiKey screen Nothing++drawBoard :: AppEnv ()+drawBoard = do++ Rect x y gw gh <- getGridBounds+ winIndices <- snd `liftM` getWinner+ grid <- getGrid+ screen <- getScreen++ let (w,h) = (gw `div` 3, gh `div` 3)+ let (halfW, halfH) = (w `div` 4, h `div` 4)+ + let applySurf r c = applySurface' (r * w + x + halfW) (c * h + y + halfH) + -- draw pieces+ forM_ [(r,c) | r <- [0..2], c <- [0..2]] $ \(r,c) -> do+ case cellAt grid r c of+ Nought -> do+ src <- if (r,c) `elem` winIndices+ then getOWinSurf+ else getNought+ applySurf r c src screen Nothing+ Cross -> do+ src <- if (r,c) `elem` winIndices+ then getXWinSurf+ else getCross + applySurf r c src screen Nothing+ _ -> return ()+ + -- draw/overlay grid+ let drawLine' u hh l d = liftIO . drawLine screen u hh l d + drawLine' (x + w, y) gh thickness Vertical gridColor+ drawLine' (x + w * 2, y) gh thickness Vertical gridColor+ + drawLine' (x, y + h) gw thickness Horizontal gridColor+ drawLine' (x, y + h * 2) gw thickness Horizontal gridColor+ + where gridColor = Pixel 0xFF00FF+ thickness = 5++drawEndGame :: AppEnv ()+drawEndGame = do+ screen <- getScreen+ font <- getFont+ winner <- fst `liftM` getWinner+ playerResultStr <- winOrLose winner `liftM` getPlayerType+ unless (null playerResultStr) $ liftIO $ do+ textSurface <- renderTextSolid font playerResultStr textColor+ let x = (screenWidth `div` 2) - (surfaceGetWidth textSurface `div` 2)+ applySurface x (screenHeight - 35) textSurface screen Nothing++ where winOrLose Noughts Nought = "YOU WON!"+ winOrLose Crosses Cross = "YOU WON!"+ winOrLose Noughts Cross = "YOU LOST!"+ winOrLose Crosses Nought = "YOU LOST!"+ winOrLose Draw _ = "DRAW GAME!"+ winOrLose _ _ = []++drawScore :: AppEnv ()+drawScore = do+ Rect _ ry _ h <- getGridBounds+ font <- getFont+ screen <- getScreen+ stats <- getStats + + let y = ry + h + 20+ liftIO $ do+ scores <- renderTextSolid font ("Player: " ++ (show $ playerCount stats) ++ " CPU: " ++ (show $ aiCount stats) ++ " Ties: " ++ (show $ tieCount stats)) textColor+ let x = (screenWidth `div` 2) - (surfaceGetWidth scores `div` 2)+ applySurface x y scores screen Nothing++render :: AppEnv ()+render = do+ screen <- getScreen+ liftIO $ clear screen 0x00 0x00 0x00+ drawPlayersKey+ drawBoard+ drawScore+ drawEndGame+ liftIO $ SDL.flip screen
+ FontReadMe.txt view
@@ -0,0 +1,47 @@+ ENGLISH +Hello, thanks for downloading the font "Alice in Wonderland" from one of the next sites: + - www.por-que-tan-serio.deviantart.com (My DeviantART account) + - www.fontspace.com/marcotl (My FontSpace account) + +The rules are the following: + -Do not use this font for commercial purposes. + -Do not claim this font as yours (I beg you xD). + -This font is currently under development, so go to my gallery often to make sure + you have the latest version. + -You can distribute this font as long as you keep this Read-me file with it and intact. + -Do not rename this font. + -You can edit the font for personal purposes, but please do not distribute edited + versions of the font. + -You can request for special characters if they're not included in the current version + of the font, just let me know and make sure I answered your request. + -Feel free to ask whatever you want to know. + -PLEASE, SHOW ME YOUR WORK, I'd love to see your creativity and the way my + font is being used. + +Enjoy this font and have fun! +-Marco T.L. + +______________________________________________________________________________________________ + ESPAÑOL +Hola, gracias por descargar la fuente "Alice in Wonderland" (Alicia en el País de las +Maravillas) de alguno de los siguientes sitios: + - www.por-que-tan-serio.deviantart.com (Mi cuenta en DeviantART) + - www.fontspace.com/marcotl (Mi cuenta en Fontspace) + +Las reglas son las siguientes: + -No uses la fuente para propósitos comerciales. + -No digas que la fuente es tuya (te lo ruego xD). + -Esta fuente está bajo desarrollo, así que ve seguido a mi galería para que + te asegures de tener la última versión. + -Puedes distribuir la fuente mientras mantengas este texto con ella e intacto. + -No renombres la fuente. + -Puedes editar la fuente para propósitos personales, pero por favor no distribuyas + una versión editada de ésta. + -Puedes solicitar caracteres especiales si no están incluidos en la versión actual + de la fuente, sólo hazmelo saber y asegúrate de que respondí a tu petición. + -Siéntete libre para preguntar lo que quieras. + -POR FAVOR, MUÉSTRAME TU TRABAJO, me encantaría ver tu creatividad y la manera + en la que mi fuente está siendo usada. + +¡Disfruta la fuente y diviértete! +-Marco T.L.
+ GameEnv.hs view
@@ -0,0 +1,64 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module GameEnv+(+ AppEnv(),+ runApp,+ getPTurnCell,+ modifyPlayerCount,+ screenWidth,+ screenHeight,+ sceenBpp+) where++import Control.Monad.State+import Control.Monad.Reader++import Grid+import GameState++screenWidth, screenHeight, sceenBpp :: Int+screenWidth = 640+screenHeight = 480+sceenBpp = 32++type AppState = StateT AppData IO+newtype AppEnv a = AppEnv { runEnv :: ReaderT AppConfig AppState a }+ deriving (Monad, MonadIO, MonadReader AppConfig, MonadState AppData)++runApp :: AppConfig -> AppData -> AppEnv a -> IO (a, AppData)+runApp config state (AppEnv env) = (runStateT . runReaderT env) config state++getPTurnCell :: AppEnv Cell+getPTurnCell = getTurn >>= mapTurn+ where mapTurn :: PlayerTurn -> AppEnv TileType+ mapTurn Player = getPlayerType+ mapTurn Ai = swapCell `liftM` getPlayerType++modifyPlayerCount :: (Integer -> Integer) -> AppEnv ()+modifyPlayerCount fn = liftM mapTurn getTurn >>= modifyStats+ where mapTurn :: PlayerTurn -> Stats -> Stats+ mapTurn Player = \s@Stats { playerCount=pc } -> s { playerCount=fn pc }+ mapTurn Ai = \s@Stats { aiCount=pc } -> s { aiCount=fn pc }
+ GameState.hs view
@@ -0,0 +1,182 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+{-# LANGUAGE FlexibleContexts #-}+module GameState where+import System.Random++import Control.Monad.State+import Control.Monad.Reader++import Graphics.UI.SDL (Surface)+import Graphics.UI.SDL.Rect+import Graphics.UI.SDL.TTF (Font)++import Surface hiding (isInside)+import qualified Surface (isInside)++import Grid++data PlayerTurn = Player | Ai+ deriving (Eq, Show, Read)++type TileType = Cell++data Stats = Stats {+ playerCount :: Integer,+ aiCount :: Integer,+ tieCount :: Integer+} deriving (Eq, Show)++data AppData = AppData {+ --gameState :: GameState,+ --tiles :: [Tile],+ grid :: Grid,+ stats :: Stats,+ turn :: PlayerTurn,+ acRand :: StdGen,+ --gameOver :: Bool,+ winner :: (Winner, [(Int,Int)])+}++data AppConfig = AppConfig {+ screen :: Surface,+ font :: Font,+ oSurface :: Surface,+ xSurface :: Surface,+ oWinSurf :: Surface,+ xWinSurf :: Surface,+ gridBounds :: Rect,+ playerStart :: PlayerTurn,+ playerType :: TileType+}++-- Player Cross+appData :: Int -> PlayerTurn -> AppData+appData seed t = AppData newGrid (Stats 0 0 0) t (mkStdGen seed) (Nobody,[])++getTurn :: MonadState AppData m => m PlayerTurn+getTurn = liftM turn get++putTurn :: MonadState AppData m => PlayerTurn -> m ()+putTurn t = modify $ \s -> s { turn = t }++modifyTurn :: MonadState AppData m => (PlayerTurn -> PlayerTurn) -> m ()+modifyTurn fn = fn `liftM` getTurn >>= putTurn++modifyTurnM :: MonadState AppData m => (PlayerTurn -> m PlayerTurn) -> m ()+modifyTurnM act = getTurn >>= act >>= putTurn++getStats :: MonadState AppData m => m Stats+getStats = liftM stats get++putStats :: MonadState AppData m => Stats -> m ()+putStats t = modify $ \s -> s { stats = t }++modifyStats :: MonadState AppData m => (Stats -> Stats) -> m ()+modifyStats fn = fn `liftM` getStats >>= putStats++modifyStatsM :: MonadState AppData m => (Stats -> m Stats) -> m ()+modifyStatsM act = getStats >>= act >>= putStats++getRGen :: MonadState AppData m => m StdGen+getRGen = liftM acRand get++putRGen :: MonadState AppData m => StdGen -> m ()+putRGen t = modify $ \s -> s { acRand = t }++modifyRGen :: MonadState AppData m => (StdGen -> StdGen) -> m ()+modifyRGen fn = fn `liftM` getRGen >>= putRGen++modifyRGenM :: MonadState AppData m => (StdGen -> m StdGen) -> m ()+modifyRGenM act = getRGen >>= act >>= putRGen++-- utility function to hide plumbing of random generator types.+rand :: MonadState AppData m => Int -> Int -> m Int+rand lo hi = do+ r <- getRGen+ let (val, r') = randomR (lo, hi) r+ putRGen r'+ return val++getGrid :: MonadState AppData m => m Grid+getGrid = liftM grid get++putGrid :: MonadState AppData m => Grid -> m ()+putGrid t = modify $ \s -> s { grid = t }++modifyGrid :: MonadState AppData m => (Grid -> Grid) -> m ()+modifyGrid fn = fn `liftM` getGrid >>= putGrid++modifyGridM :: MonadState AppData m => (Grid -> m Grid) -> m ()+modifyGridM act = getGrid >>= act >>= putGrid++isGameOver :: MonadState AppData m => m Bool+isGameOver = liftM (gameOver . fst . winner) get+ where gameOver Nobody = False+ gameOver _ = True++getWinner :: MonadState AppData m => m (Winner, [(Int,Int)])+getWinner = liftM winner get++putWinner :: MonadState AppData m => (Winner, [(Int,Int)]) -> m ()+putWinner t = modify $ \s -> s { winner = t }++getScreen :: MonadReader AppConfig m => m Surface+getScreen = liftM screen ask++getFont :: MonadReader AppConfig m => m Font+getFont = liftM font ask++getNought :: MonadReader AppConfig m => m Surface+getNought = liftM oSurface ask++getCross :: MonadReader AppConfig m => m Surface+getCross = liftM xSurface ask++getXWinSurf :: MonadReader AppConfig m => m Surface+getXWinSurf = liftM xWinSurf ask++getOWinSurf :: MonadReader AppConfig m => m Surface+getOWinSurf = liftM oWinSurf ask++getPlayerType :: MonadReader AppConfig m => m TileType+getPlayerType = liftM playerType ask++getPlayerStart :: MonadReader AppConfig m => m PlayerTurn+getPlayerStart = liftM playerStart ask++getGridBounds :: MonadReader AppConfig m => m Rect+getGridBounds = liftM gridBounds ask++inGridBounds :: MonadReader AppConfig m => Int -> Int -> m Bool+inGridBounds x y = getGridBounds >>= \r -> return $ Surface.isInside r x y++nextTurn :: MonadState AppData m => m ()+nextTurn = modifyTurn k+ where k Player = Ai+ k Ai = Player++toTileType :: MonadReader AppConfig m => PlayerTurn -> m TileType+toTileType Player = getPlayerType+toTileType Ai = swapCell `liftM` getPlayerType
+ Grid.hs view
@@ -0,0 +1,109 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+module Grid+(+ Cell(..),+ swapCell,+ Winner(..),+ Grid(),+ newGrid,+ toArray,+ cellAt,+ isEmpty,+ isComplete,+ takeTurn,+ whoWon+) where++import Data.List+import Data.Array hiding (indices)++data Cell = Nought | Cross | Empty+ deriving Eq+-- deriving (Eq, Show)+instance Show Cell where+ show Nought = "O"+ show Cross = "X"+ show _ = ""++swapCell :: Cell -> Cell+swapCell Nought = Cross+swapCell Cross = Nought+swapCell Empty = Empty++data Winner = Noughts | Crosses | Draw | Nobody+ deriving Show++type CellArray = Array (Int,Int) Cell+newtype Grid = Grid { cells :: CellArray }++rowSize :: Int+rowSize = 3+--gridSize = rowSize * rowSize++newGrid :: Grid+newGrid = Grid $ listArray ((0,0), (rowSize - 1,rowSize - 1)) $ repeat Empty++toArray :: Grid -> CellArray+toArray (Grid t) = t++cellAt :: Grid -> Int -> Int -> Cell+cellAt (Grid cs) row col = cs ! (row, col)++isEmpty :: Grid -> Int -> Int -> Bool+isEmpty g row col =+ case cellAt g row col of+ Empty -> True+ _ -> False++isComplete :: Grid -> Bool+isComplete g = all (\(r,c) -> not $ isEmpty g r c) [(r,c) | r <- indices, c <- indices]+ where end = rowSize - 1+ indices = [0..end]++takeTurn :: Grid -> Cell -> Int -> Int -> Grid+takeTurn g Empty _ _ = g+takeTurn g@(Grid cs) c row col =+ if isEmpty g row col then+ Grid $ cs // [((row,col), c)]+ else g++whoWon :: Grid -> (Winner, [(Int,Int)])+whoWon g@(Grid cs) =+ case findWinner Nought of+ Just x -> (Noughts, x)+ _ -> case findWinner Cross of+ Just x -> (Crosses, x)+ _ -> if isDraw g then (Draw, []) else (Nobody, [])+ where end = rowSize - 1+ indices = [0..end]+ rows = [zip (repeat r) indices | r <- indices]+ cols = [zip indices (repeat c) | c <- indices]+ diagonal = zip indices indices+ invDiagonal = zip [end,end-1..0] indices+ searchSpace = rows ++ cols ++ [diagonal] ++ [invDiagonal]+ allCells cell = all $ \idx -> cs ! idx == cell + findWinner cell = find (allCells cell) searchSpace+ + isDraw grid = not $ any (uncurry $ isEmpty grid) [(r,c) | r <- indices, c <- indices]
+ HTicTacToe.cabal view
@@ -0,0 +1,66 @@+-- HTicTacToe.cabal auto-generated by cabal init. For additional+-- options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name: HTicTacToe++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.1++-- A short (one-line) description of the package.+Synopsis: A tic-tac-toe game.++-- A longer description of the package.+Description: Another tic-tac-toe game in Haskell using the SDL bindings.++-- URL for the project homepage or repository.+Homepage: http://github.com/snkkid/HTicTacToe++-- The license under which the package is released.+License: MIT++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Korcan Hussein++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: korcan_h@hotmail.com++-- A copyright notice.+-- Copyright: ++Category: Game,Graphics++Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+-- Extra-source-files: ++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.4++Extra-Source-Files: README, Surface.hs, Grid.hs, GameState.hs, GameEnv.hs, Draw.hs, HTicTacToe.hs++data-files: "Alice_in_WonderLand_3.ttf",+ "FontReadMe.txt",+ "o.png", "x.png", "oWin.png", "xWin.png"++Executable HTicTacToe+ -- .hs or .lhs file containing the Main module.+ Main-is: HTicTacToe.hs+ + -- Packages needed in order to build this package.+ Build-depends: base >= 4 && < 5, random, array, mtl, SDL, SDL-image, SDL-ttf++ -- Modules not exported by this package.+ -- Other-modules: + + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+ -- Build-tools: + -- GHC-Options: -O2 -Wall
+ HTicTacToe.hs view
@@ -0,0 +1,188 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein.+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+module Main where++import Prelude hiding (init)++import System.Environment++import System.Console.GetOpt++--import Data.Word+import Data.List hiding (init)++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Graphics.UI.SDL hiding (init, quit, flip)+--import qualified Graphics.UI.SDL as SDL (init, quit, flip)++import Graphics.UI.SDL.TTF hiding (init, quit)+import qualified Graphics.UI.SDL.TTF as TTFG (init, quit)++import Surface+import Grid+import GameState hiding (playerType, screen, font, winner, playerStart)+import GameEnv+import Draw++import Paths_HTicTacToe++type ProgramOptions = (PlayerTurn,TileType)++parseArgs :: IO (ProgramOptions, [String])+parseArgs = do+ argv <- getArgs+ case getOpt Permute options argv of+ (o,n,[] ) -> return (foldl' (flip id) defaultOptions o, n)+ (_,_,errs) -> ioError $ userError $ concat errs ++ usageInfo header options+ where header = "Usage: start player"+ defaultOptions = (Player, Cross)+ options :: [OptDescr (ProgramOptions -> ProgramOptions)]+ options = [Option ['s'] ["start"] (ReqArg (\str (_,p) -> (pStart str, p)) "PlayerStart") "which players starts \"Player\" or \"Ai\" (default is \"Player\")",+ Option ['p'] ["player"] (ReqArg (\str (s,_) -> (s, pType str)) "PlayerType") "player is \"x\" or \"o\" (default is \"x\")"]+ --playerStart = Player+ pType "o" = Nought+ pType "O" = Nought+ pType _ = Cross -- default playerType.+ pStart "Ai" = Ai+ pStart _ = Player -- default playerStart.++init :: IO (AppConfig, AppData)+init = do+ + (playerStart, playerType) <- fst `liftM` parseArgs+ + xFileName <- getDataFileName "x.png"+ oFileName <- getDataFileName "o.png"+ xWinFileName <- getDataFileName "xWin.png"+ oWinFileName <- getDataFileName "oWin.png"+ fontFileName <- getDataFileName "Alice_in_WonderLand_3.ttf"+ + screen <- setVideoMode screenWidth screenHeight sceenBpp [HWSurface, DoubleBuf]+ setCaption "tic-tac-toe" []+ enableUnicode True+ + font <- openFont fontFileName 24+ xSprite <- loadImage xFileName colorKey+ oSprite <- loadImage oFileName colorKey+ xWinSprite <- loadImage xWinFileName colorKey+ oWinSprite <- loadImage oWinFileName colorKey+ + seed <- fromIntegral `liftM` getTicks+ + let wh = surfaceGetWidth xSprite * 6+ + let gridX = (screenWidth `div` 2) - (wh `div` 2) + return (AppConfig screen font oSprite xSprite oWinSprite xWinSprite (Rect gridX 5 wh wh) playerStart playerType, appData seed playerStart)+ where colorKey = Just (0, 0, 0)++initGame :: AppEnv ()+initGame = do+ putGrid newGrid+ putWinner (Nobody, [])+ playerTurn <- getTurn+ case playerTurn of+ Ai -> aiPlayGame+ _ -> return ()++aiPlayGame :: AppEnv ()+aiPlayGame = do+ playerTurn <- getTurn+ case playerTurn of+ Ai -> randomCell >>= uncurry playGame+ _ -> return ()+ where randomCell = do+ r <- rand 0 2+ c <- rand 0 2+ g <- getGrid+ if isEmpty g r c+ then return (r,c)+ else randomCell++playGame :: Int -> Int -> AppEnv ()+playGame row col = do + g <- getGrid+ when (isEmpty g row col) $ do + playerCell <- getPTurnCell+ modifyGrid $ \gr -> takeTurn gr playerCell row col + winner <- whoWon `liftM` getGrid+ putWinner winner + case fst winner of+ Draw -> modifyStats $ \s@Stats{ tieCount=tc } -> s { tieCount=tc + 1 }+ Nobody -> nextTurn+ _ -> modifyPlayerCount (+1)+ + isOver <- isGameOver+ unless isOver aiPlayGame++handleEvent :: Event -> AppEnv ()+handleEvent VideoExpose = render+handleEvent (MouseButtonDown mx my ButtonLeft) = do+ gridBox@(Rect gx gy w h) <- getGridBounds+ when (isInside gridBox x y) $ do+ finished <- isGameOver+ if finished+ then do+ initGame+ pushEvent_ VideoExpose+ else do+ playerTurn <- getTurn+ case playerTurn of+ Player -> do+ let (row, col) = ((x - gx) `div` (w `div` 3), (y - gy) `div` (h `div` 3))+ playGame row col+ pushEvent_ VideoExpose+ _ -> return ()+ where x = fromIntegral mx+ y = fromIntegral my+ pushEvent_ = liftIO . pushEvent++handleEvent _ = return ()++loop :: AppEnv ()+loop = do+ event <- liftIO waitEvent+ case event of+ Quit -> return ()+ _ -> do+ handleEvent event+ loop++main :: IO ()+main = withInit [InitEverything] $ -- withInit calls quit for us.+ withTtfInit $ do+ (config, state) <- init+ runApp config state $ do+ aiPlayGame -- if Ai was choosen to start first.+ render -- initial render+ loop+ return ()++withTtfInit :: IO () -> IO ()+withTtfInit = bracket_ ttfInit TTFG.quit+ where ttfInit :: IO ()+ ttfInit = TTFG.init >>= \success -> when (not success) $ throwIO $ userError "Failed to init ttf\n"+
+ LICENSE view
@@ -0,0 +1,21 @@+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.
+ README view
@@ -0,0 +1,31 @@+HTicTacToe+==========+Author: Korcan Hussein++Install+-------++runhaskell Setup.hs configure --user+runhaskell Setup.hs build+runhaskell Setup.hs install++How to Play+-----------++You are either X or O, select an empty area in the grid you want to pick, get 3 in a row or+column or across and you win!++Game Options+------------++Pick Who starts:++HTicTacToe --start=Ai/Player++Pick your type:++HTicTacToe --player=o/x++Both:++HTicTacToe --start=Ai/Player --player=o/x
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Surface.hs view
@@ -0,0 +1,90 @@+{-+ The MIT License+ + Copyright (c) 2010 Korcan Hussein+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+-}+module Surface where++import Foreign++import Control.Monad+import Control.Monad.Trans++import Graphics.UI.SDL+import Graphics.UI.SDL.Image++isInside :: Rect -> Int -> Int -> Bool+isInside (Rect sx sy w h) x y = x > sx && x < (sx + w) && y > sy && y < (sy + h)++loadImage :: String -> Maybe (Word8, Word8, Word8) -> IO Surface+loadImage filename colorKey = load filename >>= displayFormat >>= setColorKey' colorKey++setColorKey' :: Maybe (Word8,Word8,Word8) -> Surface -> IO Surface+setColorKey' Nothing s = return s+setColorKey' (Just (r, g, b)) surface = mapRGB' surface r g b >>= setColorKey surface [SrcColorKey] >> return surface++applySurface :: Int -> Int -> Surface -> Surface -> Maybe Rect -> IO ()+applySurface x y src dst clip = blitSurface src clip dst offset >> return ()+ where offset = Just Rect { rectX = x, rectY = y, rectW = 0, rectH = 0 }++applySurface' :: MonadIO m => Int -> Int -> Surface -> Surface -> Maybe Rect -> m ()+applySurface' x y src dst = liftIO . applySurface x y src dst++mapRGB' :: Surface -> Word8 -> Word8 -> Word8 -> IO Pixel+mapRGB' = mapRGB . surfaceGetPixelFormat++clear :: Surface -> Word8 -> Word8 -> Word8 -> IO ()+clear surf r g b = do+ jrect <- Just `liftM` getClipRect surf+ clearColor <- mapRGB' surf r g b+ fillRect surf jrect clearColor+ return ()++getPixel32 :: Surface -> Int -> Int -> IO Pixel+getPixel32 s x y = do+ ps <- surfaceGetPixels s+ Pixel `liftM` peekElemOff (castPtr ps :: Ptr Word32) offset+ where offset = y * (fromIntegral $ surfaceGetPitch s `div` 4) + x++setPixel32 :: Surface -> Int -> Int -> Pixel -> IO ()+setPixel32 s x y (Pixel pixel) = do+ ps <- surfaceGetPixels s+ pokeElemOff (castPtr ps :: Ptr Word32) offset pixel+ where offset = y * (fromIntegral $ surfaceGetPitch s `div` 4) + x++type Point2 = (Int,Int)++data LineDir = Vertical | Horizontal++drawLine :: Surface -> Point2 -> Int -> Int -> LineDir -> Pixel -> IO ()+drawLine dst (x,y) len thickness Horizontal colour =+ forM_ [(r,c) | c <- [yStart..yEnd], r <- [x..(x + len)]] $ \(r,c) ->+ setPixel32 dst r c colour+ where halfLen = thickness `div` 2+ yStart = max (y - halfLen) 0+ yEnd = min (yStart + thickness) $ surfaceGetHeight dst++drawLine dst (x,y) len thickness Vertical colour =+ forM_ [(r,c) | r <- [xStart..xEnd], c <- [y..(y + len)]] $ \(r,c) ->+ setPixel32 dst r c colour+ where halfLen = thickness `div` 2+ xStart = max (x - halfLen) 0+ xEnd = min (xStart + thickness) $ surfaceGetWidth dst
+ o.png view
binary file changed (absent → 1326 bytes)
+ oWin.png view
binary file changed (absent → 1269 bytes)
+ x.png view
binary file changed (absent → 1344 bytes)
+ xWin.png view
binary file changed (absent → 1288 bytes)