From 1849375d7d9565228931aae8d6548ed967af41c5 Mon Sep 17 00:00:00 2001 From: Magic_RB Date: Mon, 25 Sep 2023 15:25:46 +0200 Subject: [PATCH] Snake in Haskell --- .envrc | 1 + .gitignore | 3 + flake.lock | 27 +++ flake.nix | 53 +++++ hie.yaml | 7 + snake/app/Main.hs | 405 +++++++++++++++++++++++++++++++++++++++ snake/lib/Snake.hs | 1 + snake/package.yaml | 55 ++++++ snake/snake.cabal | 58 ++++++ snake/textures/atlas.png | Bin 0 -> 36465 bytes snake/textures/atlas.svg | 278 +++++++++++++++++++++++++++ stack.yaml | 66 +++++++ stack.yaml.lock | 12 ++ 13 files changed, 966 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 hie.yaml create mode 100644 snake/app/Main.hs create mode 100644 snake/lib/Snake.hs create mode 100644 snake/package.yaml create mode 100644 snake/snake.cabal create mode 100644 snake/textures/atlas.png create mode 100644 snake/textures/atlas.svg create mode 100644 stack.yaml create mode 100644 stack.yaml.lock diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98af07a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.direnv +result* +.stack-work diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d3c081f --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1695145219, + "narHash": "sha256-Eoe9IHbvmo5wEDeJXKFOpKUwxYJIOxKUesounVccNYk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5ba549eafcf3e33405e5f66decd1a72356632b96", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..96d5d3b --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + + outputs = { self, nixpkgs, ... }@inputs: + let + supportedSystems = [ "x86_64-linux" ]; + forAllSystems' = nixpkgs.lib.genAttrs; + forAllSystems = forAllSystems' supportedSystems; + + pkgsForSystem = + system: + import nixpkgs { inherit system; overlays = [ ]; }; + in + { + devShells = forAllSystems (system: + let + pkgs = pkgsForSystem system; + hPkgs = pkgs.haskell.packages."ghc962"; + stack-wrapper = pkgs.symlinkJoin { + name = "stack"; + paths = [ pkgs.stack ]; + buildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/stack \ + --add-flags "\ + --no-nix \ + --system-ghc \ + --no-install-ghc \ + " + ''; + }; + in + { + default = + pkgs.mkShell rec { + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; + buildInputs = with pkgs; [ + stdenv.cc + stack-wrapper + hPkgs.ghc + hPkgs.implicit-hie + hPkgs.haskell-language-server + SDL2 + SDL2_image + libtiff + libwebp + pkg-config + ]; + }; + } + ); + }; +} diff --git a/hie.yaml b/hie.yaml new file mode 100644 index 0000000..8ecd6c1 --- /dev/null +++ b/hie.yaml @@ -0,0 +1,7 @@ +cradle: + stack: + - path: "snake/lib" + component: "snake:lib" + + - path: "snake/app/Main.hs" + component: "snake:exe:snake" diff --git a/snake/app/Main.hs b/snake/app/Main.hs new file mode 100644 index 0000000..defcdb5 --- /dev/null +++ b/snake/app/Main.hs @@ -0,0 +1,405 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE BlockArguments #-} +{-# LANGUAGE OverloadedRecordDot #-} +{-# LANGUAGE NoFieldSelectors #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE LambdaCase #-} + +module Main where + +import Data.Text qualified as T +import Data.Text (Text) +import Snake +import SDL.Video qualified as SDL +import SDL.Video.Renderer qualified as SDL +import SDL.Input.Keyboard qualified as SDL +import SDL.Init qualified as SDL +import SDL.Vect qualified as SDL +import SDL.Event qualified as SDL +import Control.Exception +import Control.Monad +import Control.Monad.Extra (whileM, whenM) +import Data.IORef +import Effectful +import Effectful.State.Static.Local +import SDL (($=)) +import SDL.Image qualified as SDLI +import Control.Lens hiding ((.=), (%=)) +import Foreign.C +import Effectful.Reader.Static +import Data.Vector.Storable qualified as SV +import System.Random + +data Point = Point Int Int + deriving (Show, Eq) +data Square = Square Point Point + deriving (Show, Eq) + +scalePoint :: Int -> Point -> Point +scalePoint scale (Point x y) = Point (x * scale) (y * scale) + +addPoint :: Point -> Point -> Point +addPoint (Point x1 y1) (Point x2 y2) = Point (x1 + x2) (y1 + y2) + +directionOpposite :: Direction -> Direction +directionOpposite direction = case direction of + East -> West + West -> East + North -> South + South -> North + +directionToPoint :: Direction -> Point +directionToPoint direction = + case direction of + East -> Point 1 0 + West -> Point (-1) 0 + North -> Point 0 (-1) + South -> Point 0 1 + +clampPoint :: (Int, Int) -> Point -> Point +clampPoint (width, height) (Point x y) = Point x' y' + where + x' = clamp x width + y' = clamp y height + clamp coord size = + case coord of + coord | coord < 0 -> size + coord + coord -> coord `rem` size + +pointToSquare :: Int -> Point -> Square +pointToSquare scale point@(Point x y) = Square (scalePoint scale point) (Point scale scale) + +pointToV2 :: Integral i => Point -> SDL.V2 i +pointToV2 (Point x y) = SDL.V2 (fromIntegral x) (fromIntegral y) + +squareToRectangle :: Integral i => Square -> SDL.Rectangle i +squareToRectangle (Square (Point x y) (Point width height)) = SDL.Rectangle (SDL.P (SDL.V2 (fromIntegral x) (fromIntegral y))) (SDL.V2 (fromIntegral width) (fromIntegral height)) + +data Direction + = East + | West + | North + | South + deriving (Eq, Show) + +data Snake + = Snake + { head :: Point + , body :: [Point] + , direction :: Direction + , growth :: Int + } + deriving Show +makeLensesFor + [ ("head", "head") + , ("body", "body") + , ("growth", "growth") + , ("direction", "direction") + ] ''Snake + +data WorldState + = WorldState + { snake :: Snake + , apple :: Maybe Point + , gameOver :: Bool + } + deriving Show +makeLensesFor + [ ("snake", "snake") + , ("apple", "apple") + , ("gameOver", "gameOver") + ] ''WorldState + +data GameState + = GameState + { run :: Bool + , pastWorldStates :: [WorldState] + , worldState :: WorldState + , frameNumber :: Int + , reverseTime :: Bool + } + deriving Show +makeLensesFor + [ ("run", "run") + , ("pastWorldStates", "pastWorldStates") + , ("frameNumber", "frameNumber") + , ("worldState", "worldState") + , ("reverseTime", "reverseTime") + ] ''GameState + +initialWorldState :: WorldState +initialWorldState + = WorldState + { snake + = Snake + { head = Point 2 0 + , body = [ Point 1 0, Point 0 0 ] + , growth = 0 + , direction = East + } + , apple = Nothing + , gameOver = False + } + +initialGameState :: GameState +initialGameState + = GameState + { run = True + , pastWorldStates = [] + , worldState = initialWorldState + , frameNumber = 0 + , reverseTime = False + } + +data GameConfig + = GameConfig + { gridDimensions :: (Int, Int) + , updatesPerSecond :: Int + } + deriving Show +makeLensesFor + [ ("gridDimensions", "gridDimensions") + , ("updatesPerSecond", "updatesPerSecond") + ] ''GameConfig + +(.=) :: State s :> es => ASetter s s a b -> b -> Eff es () +l .= b = modify (l .~ b) +{-# INLINE (.=) #-} + +(%=) :: State s :> es => ASetter s s a b -> (a -> b) -> Eff es () +l %= f = modify (l %~ f) +{-# INLINE (%=) #-} + +(%.=) :: State s :> es => Lens s s a b -> (s -> b) -> Eff es () +l %.= f = modify (\s -> l %~ const (f s) $ s) +{-# INLINE (%.=) #-} + +renderHead + :: Snake + -> Eff es (Point, SDL.Rectangle CInt) +renderHead snake = do + let scale = 20 + let texture = case snake.direction of + East -> Point 0 0 + West -> Point 1 1 + South -> Point 0 2 + North -> Point 1 2 + + pure $ ((texture,) . squareToRectangle . pointToSquare scale) snake.head + +renderSnake + :: (Reader GameConfig :> es) + => Snake + -> Eff es [(Point, SDL.Rectangle CInt)] +renderSnake snake = do + let scale = 20 + pure $ map ((Point 1 0,) . squareToRectangle . pointToSquare scale) snake.body + +renderApple + :: () + => Point + -> Eff es (Point, SDL.Rectangle CInt) +renderApple apple = do + let scale = 20 + pure $ ((Point 0 1,) . squareToRectangle . pointToSquare scale) apple + +moveSnake + :: (Reader GameConfig :> es, State Snake :> es) + => Direction + -> Eff es () +moveSnake direction = do + dims <- asks @GameConfig (\s -> s.gridDimensions) + newLength <- gets @Snake \snake -> + if snake.growth > 0 then + length snake.body + else + length snake.body - 1 + + body %.= \snake -> snake.head : take newLength snake.body + Main.head %= (clampPoint dims . addPoint (directionToPoint direction)) + growth %= \growth -> + if growth > 0 then + growth - 1 + else + growth + + pure () + +changeDirection + :: (State GameState :> es) + => Direction + -> Eff es () +changeDirection dir = do + lastDirection <- gets @GameState (\s -> (Prelude.head s.pastWorldStates).snake.direction) + when (dir /= directionOpposite lastDirection) $ (worldState . snake . direction) .= dir + +setReversed + :: (State GameState :> es) + => Bool + -> Eff es () +setReversed r = do + reverseTime .= r + +promoteWorldState + :: (State GameState :> es) + => Eff es () +promoteWorldState = + pastWorldStates %.= (\state -> state.worldState : state.pastWorldStates) + +eatApple + :: (State GameState :> es, Reader GameConfig :> es, State StdGen :> es) + => Eff es () +eatApple = do + (worldState . apple) .= Nothing + (worldState . snake . growth) %= (+) 3 + genApple + +isGameOver + :: (State GameState :> es) + => Eff es Bool +isGameOver = do + gets @GameState (\s -> s.worldState.snake.head `elem` s.worldState.snake.body) + +step + :: (State GameState :> es, Reader GameConfig :> es, State StdGen :> es, IOE :> es) + => Eff es () +step = + gets @GameState (\s -> s.reverseTime) >>= + \case + True -> do + gets @GameState (\s -> s.pastWorldStates) >>= \case + [] -> pure () + [a] -> pure () + _ -> do + worldState %.= \s -> Prelude.head . tail $ s.pastWorldStates + pastWorldStates %= \s -> tail s + False -> + gets @GameState (\s -> s.worldState.gameOver) >>= \case + False -> do + get @GameState >>= \state -> execState state.worldState.snake (moveSnake state.worldState.snake.direction) >>= (.=) (worldState . snake) + + snakeHead <- gets @GameState (\s -> s.worldState.snake.head) + gets @GameState (\s -> s.worldState.apple) >>= \case + Just apple | apple == snakeHead -> eatApple + Nothing -> genApple + _ -> pure () + + whenM isGameOver $ do + (worldState . gameOver) .= True + liftIO . putStrLn $ "Game Over" + + promoteWorldState + True -> pure () + +renderSprite + :: (IOE :> es) + => SDL.Renderer + -> SDL.Texture + -> (Point, SDL.Rectangle CInt) + -> Eff es () +renderSprite renderer texture (textureRect, rect) = SDL.copy renderer texture (Just (SDL.Rectangle (SDL.P (scalePoint 512 textureRect & pointToV2)) (SDL.V2 512 512))) (Just rect) + +render + :: (State GameState :> es, IOE :> es, Reader GameConfig :> es) + => SDL.Renderer + -> SDL.Texture + -> Eff es () +render renderer texture = do + SDL.rendererDrawColor renderer $= SDL.V4 255 255 255 0 + SDL.clear renderer + + snake <- gets @GameState (\s -> s.worldState.snake) + snakeSprites <- renderSnake snake + mapM_ (renderSprite renderer texture) snakeSprites + + gets @GameState (\s -> s.worldState.apple) >>= \case + Just apple -> renderApple apple >>= renderSprite renderer texture + Nothing -> pure () + + renderHead snake >>= renderSprite renderer texture + + whenM (gets @GameState (\s -> s.worldState.gameOver)) $ pure () -- show text, but sdl_ttf sucks + + SDL.present renderer + +randomRange + :: (State StdGen :> es) + => (Int, Int) + -> Eff es Int +randomRange range = + get @StdGen <&> uniformR range >>= \(a, g) -> put g >> pure a + +getFreeSpaces + :: (State GameState :> es, Reader GameConfig :> es) + => Eff es [Point] +getFreeSpaces = do + snakeBody <- gets @GameState (\s -> s.worldState.snake.body) + snakeHead <- gets @GameState (\s -> s.worldState.snake.head) + (width, height) <- asks @GameConfig (\s -> s.gridDimensions) + + [0..(width * height)] & map (\i -> Point (i `rem` width) (i `div` width)) & filter (\point -> snakeHead /= point && point `notElem` snakeBody) & pure + +genApple + :: (State GameState :> es, Reader GameConfig :> es, State StdGen :> es) + => Eff es () +genApple = do + freeSpaces <- getFreeSpaces + appleIndex <- randomRange (0, length freeSpaces - 1) + + (worldState . apple) .= Just (freeSpaces !! appleIndex) + + pure () + +main :: IO () +main = + let + rendererConfig + = SDL.defaultRenderer + { SDL.rendererType = SDL.AcceleratedVSyncRenderer + } + windowConfig + = SDL.defaultWindow + { SDL.windowInitialSize = SDL.V2 400 400 + } + gameConfig + = GameConfig + { gridDimensions = (20, 20) + , updatesPerSecond = 1 + } + in + bracket (SDL.initialize [ SDL.InitEvents, SDL.InitVideo ]) (const SDL.quit) $ \_ -> + bracket (SDL.createWindow "Snake" windowConfig) SDL.destroyWindow $ \window -> + bracket (SDL.createRenderer window (-1) rendererConfig) SDL.destroyRenderer $ \renderer -> + initStdGen >>= \rng -> + SDLI.loadTexture renderer "textures/atlas.png" >>= \texture -> + + runEff . runReader gameConfig . evalState initialGameState . evalState rng $ genApple >> promoteWorldState >> whileM do + SDL.rendererScale renderer $= SDL.V2 1.0 1.0 + -- SDL.rendererViewport renderer $= Just (SDL.Rectangle 640 480) + events <- SDL.pollEvents + + forM_ events \(SDL.Event _ payload) -> case payload of + SDL.KeyboardEvent (SDL.KeyboardEventData _ motion repeat (SDL.Keysym _ code modifier)) -> + case code of + SDL.KeycodeQ | motion == SDL.Pressed -> modify (run .~ False) + SDL.KeycodeEscape | motion == SDL.Pressed -> modify (run .~ False) + SDL.KeycodePeriod | motion == SDL.Pressed -> changeDirection North + SDL.KeycodeA | motion == SDL.Pressed -> changeDirection South + SDL.KeycodeH | motion == SDL.Pressed -> changeDirection West + SDL.KeycodeE | motion == SDL.Pressed -> changeDirection East + SDL.KeycodeComma -> setReversed (motion == SDL.Pressed) + _ -> pure () + SDL.QuitEvent -> modify (run .~ False) + _ -> pure () + + + frameNumber %= (+) 1 + + whenM (gets @GameState (\s -> s.frameNumber `rem` 60 == 0)) step + + -- getFreeSpaces >>= liftIO . print + + render renderer texture + + gets @GameState (\s -> s.run) diff --git a/snake/lib/Snake.hs b/snake/lib/Snake.hs new file mode 100644 index 0000000..7b7917b --- /dev/null +++ b/snake/lib/Snake.hs @@ -0,0 +1 @@ +module Snake where diff --git a/snake/package.yaml b/snake/package.yaml new file mode 100644 index 0000000..56f5cdb --- /dev/null +++ b/snake/package.yaml @@ -0,0 +1,55 @@ +name: snake +version: 0.1.0.0 + +# A short (one-line) description of the package. +# synopsis: + +# A longer description of the package. +# description: + +# A URL where users can report bugs. +# bug-reports: + +# The license under which the package is released. +# license: +author: Magic_RB +maintainer: magic_rb@redalder.org + +# A copyright notice. +# copyright: +# category: + +dependencies: + - base + - unordered-containers + - bytestring + - text >= 2.0.1 + - sdl2 + - sdl2-image + - effectful-core + - extra + - lens + - vector + - random +other-extensions: + - ImportQualifiedPost + - OverloadedStrings + +library: + verbatim: + default-language: GHC2021 + source-dirs: lib + + exposed-modules: + - Snake + +executables: + snake: + ghc-options: + - -threaded + verbatim: + default-language: GHC2021 + main: Main.hs + other-modules: [] + source-dirs: app + dependencies: snake diff --git a/snake/snake.cabal b/snake/snake.cabal new file mode 100644 index 0000000..b7e6c87 --- /dev/null +++ b/snake/snake.cabal @@ -0,0 +1,58 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.35.2. +-- +-- see: https://github.com/sol/hpack + +name: snake +version: 0.1.0.0 +author: Magic_RB +maintainer: magic_rb@redalder.org +build-type: Simple + +library + exposed-modules: + Snake + other-modules: + Paths_snake + hs-source-dirs: + lib + other-extensions: + ImportQualifiedPost + OverloadedStrings + build-depends: + base + , bytestring + , effectful-core + , extra + , lens + , random + , sdl2 + , sdl2-image + , text >=2.0.1 + , unordered-containers + , vector + default-language: GHC2021 + +executable snake + main-is: Main.hs + hs-source-dirs: + app + other-extensions: + ImportQualifiedPost + OverloadedStrings + ghc-options: -threaded + build-depends: + base + , bytestring + , effectful-core + , extra + , lens + , random + , sdl2 + , sdl2-image + , snake + , text >=2.0.1 + , unordered-containers + , vector + default-language: GHC2021 diff --git a/snake/textures/atlas.png b/snake/textures/atlas.png new file mode 100644 index 0000000000000000000000000000000000000000..8767701597341f1e97ec7a41fb03630d24ed794d GIT binary patch literal 36465 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 0000000..05f9507 --- /dev/null +++ b/stack.yaml @@ -0,0 +1,66 @@ +# This file was automatically generated by 'stack init' +# +# Some commonly used options have been documented as comments in this file. +# For advanced use and comprehensive documentation of the format, please see: +# https://docs.haskellstack.org/en/stable/yaml_configuration/ + +# Resolver to choose a 'specific' stackage snapshot or a compiler version. +# A snapshot resolver dictates the compiler version and the set of packages +# to be used for project dependencies. For example: +# +# resolver: lts-3.5 +# resolver: nightly-2015-09-21 +# resolver: ghc-7.10.2 +# +# The location of a snapshot can be provided as a file or url. Stack assumes +# a snapshot provided as a file might change, whereas a url resource does not. +# +# resolver: ./custom-snapshot.yaml +# resolver: https://example.com/snapshots/2018-01-01.yaml +resolver: nightly-2023-09-22 + +# User packages to be built. +# Various formats can be used as shown in the example below. +# +# packages: +# - some-directory +# - https://example.com/foo/bar/baz-0.0.2.tar.gz +# subdirs: +# - auto-update +# - wai +packages: + - snake +# Dependency packages to be pulled from upstream that are not in the resolver. +# These entries can reference officially published versions as well as +# forks / in-progress versions pinned to a git hash. For example: +# +# extra-deps: +# - acme-missiles-0.3 +# - git: https://github.com/commercialhaskell/stack.git +# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# +# extra-deps: [] + +# Override default flag values for local packages and extra-deps +# flags: {} + +# Extra package databases containing global packages +# extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true +# +# Require a specific version of Stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: ">=2.11" +# +# Override the architecture used by Stack, especially useful on Windows +# arch: i386 +# arch: x86_64 +# +# Extra directories used by Stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] +# +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..178774f --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 93137bc0122de394fa2c43e933971b2996cd7dc600989b721ad971810b9a2f3f + size: 669537 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2023/9/22.yaml + original: nightly-2023-09-22