diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f94316 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.hi diff --git a/O.bmp b/O.bmp new file mode 100644 index 0000000..e71f594 Binary files /dev/null and b/O.bmp differ diff --git a/RockPaperScissors.exe b/RockPaperScissors.exe new file mode 100644 index 0000000..a359130 Binary files /dev/null and b/RockPaperScissors.exe differ diff --git a/RockPaperScissors.hs b/RockPaperScissors.hs new file mode 100644 index 0000000..5ebdad0 --- /dev/null +++ b/RockPaperScissors.hs @@ -0,0 +1,113 @@ +-- Putting it all together: Tic Tac Toe +-- cabal update +-- cabal install gloss +-- ghc tictactoe.hs +-- tictactoe.exe + +-- import is just like Java, but you can give modules (akin to Java packages) their own local name +import qualified Graphics.Gloss.Interface.Pure.Game as Gloss +import qualified Graphics.Gloss.Data.Bitmap as Bitmap +import qualified Data.Map as Map +import Data.List + +-- A tic-tac-toe cell is either blank, X, or O +data Cell = Blank | X | O deriving (Eq, Ord) + +-- To encode game state: +-- we have the board (a map from a pair of ints to a Cell) +-- and the current "player" (either X or O) +data Game = Game { + board :: Map.Map (Int, Int) Cell, + player :: Cell +} + +-- Initially, the board is a 3x3 grid of blank cells +initialBoard = Map.fromList [((i,j), Blank) | i <- [0..2], j <- [0..2]] + +-- Given a game board, and two coordinates, is the move legit? +validMove :: Map.Map (Int, Int) Cell -> Int -> Int -> Bool +validMove board i j + | i < 0 || i >= 3 || j < 0 || j >= 3 = False + | otherwise = Blank == board Map.! (i,j) + +-- X starts the game +initialGame = Game initialBoard X + +-- List the rows in the game board +rows :: Map.Map (Int, Int) Cell -> [[Cell]] +rows board = map (map snd) $ groupBy (\((i,j),_) ((k,l),_) -> i == k) $ Map.assocs board + +-- List the diagonals in the game board +diagonals :: Map.Map (Int, Int) Cell -> [[Cell]] +diagonals board = [[board Map.! (0,0), board Map.! (1,1), board Map.! (2,2)], + [board Map.! (2,0), board Map.! (1,1), board Map.! (0,2)]] + + +-- Players switch when taking turns +switch :: Cell -> Cell +switch O = X +switch X = O + +-- Given a game board, and a move, get the new game board state +takeTurn :: Game -> (Int, Int) -> Game +takeTurn game@Game { board=b, player=p } (i, j) + | validMove b i j = newGame + | otherwise = game + where + newGame = Game { + board = Map.adjust (\_ -> p) (i,j) b, + player = switch p + } + +-- Did anybody win? +winner :: Map.Map (Int, Int) Cell -> Bool +winner board = any win slices + where + slices = ((rows board) ++ (diagonals board) ++ (transpose $ rows board)) + +-- Given a slice (row, column, diagonal), are they all the same? +win slice = foldl1 (\x y -> if x == y then x else Blank) slice /= Blank + +-- is the game over? either somebody won, or there's no blanks left +gameOver :: Map.Map (Int, Int) Cell -> Bool +gameOver board = winner board || (not $ any (== Blank) $ Map.elems board) + +-- So, who won, if anybody? +outcome (Game board player) + | winner board = switch player + | otherwise = Blank + +-- Translate mouse clicks (Gloss.Event) into turns +handleMouse :: Gloss.Event -> Game -> Game +handleMouse (Gloss.EventKey (Gloss.MouseButton Gloss.LeftButton) Gloss.Down _ (x, y)) game@(Game board player) + | gameOver board = game + | otherwise = takeTurn game (i, j) + where i = round $ (y+160) / 160 + j = round $ (x+240) / 160 + +-- don't care about the other events +handleMouse _ game = game + +main = do + oPicture <- Bitmap.loadBMP "O.bmp" + xPicture <- Bitmap.loadBMP "X.bmp" + _Picture <- Bitmap.loadBMP "_.bmp" + xwin <- Bitmap.loadBMP "xwin.bmp" + owin <- Bitmap.loadBMP "owin.bmp" + tie <- Bitmap.loadBMP "tie.bmp" + + let cellPicture = Map.fromList [(O,oPicture), (X, xPicture), (Blank, _Picture)] + let outcomePicture = Map.fromList [(O,owin), (X, xwin), (Blank, tie)] + let displayCell (i,j) cell = Gloss.translate ((fromIntegral j :: Float)*160-240) ((fromIntegral i :: Float)*160-160) $ cellPicture Map.! cell + let displayBoard (Game board _) = Gloss.Pictures $ Map.elems $ Map.mapWithKey displayCell board + let displayOutcome game = Gloss.translate (230) (0) (outcomePicture Map.! (outcome game)) + let display game@(Game board player) = if gameOver board then Gloss.Pictures [displayOutcome game, displayBoard game] else displayBoard game + + Gloss.play + (Gloss.InWindow "Rock Paper Scissors" (1920,1080) (0,0)) + Gloss.black + 10 + initialGame + display + handleMouse + (\f g -> g) diff --git a/X.bmp b/X.bmp new file mode 100644 index 0000000..72b1b2f Binary files /dev/null and b/X.bmp differ diff --git a/_.bmp b/_.bmp new file mode 100644 index 0000000..311270e Binary files /dev/null and b/_.bmp differ diff --git a/owin.bmp b/owin.bmp new file mode 100644 index 0000000..23e6d5e Binary files /dev/null and b/owin.bmp differ diff --git a/tie.bmp b/tie.bmp new file mode 100644 index 0000000..54ae2fe Binary files /dev/null and b/tie.bmp differ diff --git a/xwin.bmp b/xwin.bmp new file mode 100644 index 0000000..8183120 Binary files /dev/null and b/xwin.bmp differ