packages feed

starter-snake-haskell (empty) → 1.0.0

raw patch · 17 files changed

+468/−0 lines, 17 filesdep +aesondep +basedep +containerssetup-changed

Dependencies added: aeson, base, containers, scotty, starter-snake-haskell, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,12 @@+# Changelog++All notable changes to this project since `v1.0.0` will be documented in this+file.++This project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).++## 1.0.0 (2023-08-22)++### Added++- First package release
+ LICENSE view
@@ -0,0 +1,29 @@+BSD 3-Clause License++Copyright (c) 2021, Alexander Pankoff+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+   list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice,+   this list of conditions and the following disclaimer in the documentation+   and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+   contributors may be used to endorse or promote products derived from+   this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,19 @@+# starter-snake-haskell - A Haskell Battlesnake starter++This repository contains a Haskell [Battlesnake](https://play.battlesnake.com) starter project.+It implements `v1` of the Battlesnake API and can be deployed to Heroku.++# Usage++This starter repo can be used in two different ways++1. Installed as a hackage library in your haskell project+1. As a fork that is subsequently modified to your needs++# Deployment++To deploy on Heroku you can use the provided `Procfile` when using the template+as a fork. Otherwise you will need to define your own `Procfile`.+Addtionally the+[heroku-buildpack-stack](https://github.com/mfine/heroku-buildpack-stack)+buildpack is required for the application to run on heroku
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,45 @@+module Main where++import Battlesnake.API.GameRequest+import qualified Battlesnake.API.InfoResponse as Info+import Battlesnake.API.MoveResponse+import Battlesnake.Core.Direction+import Battlesnake.Server++main :: IO ()+main =+  runBattlesnakeServer+    snakeInfo+    startHandler+    moveHandler+    endHandler++snakeInfo :: Info.InfoResponse+snakeInfo =+  Info.InfoResponse+    { Info.apiversion = "1",+      -- Optional Customization:+      Info.author = Nothing, -- TODO: Your Battlsnake username here+      Info.color = Nothing, -- TODO: pesonalize+      Info.head = Nothing, -- TODO: pesonalize+      Info.tail = Nothing, -- TODO: pesonalize+      Info.version = Nothing -- TODO: Version number or tag for your battlesnake+    }++-- TODO:+-- implement start handler if you need to allocate ressources or data at the+-- beginning of a game+startHandler :: GameRequest -> IO ()+startHandler = const $ return ()++-- TODO:+-- implement your game logic here.+moveHandler :: GameRequest -> IO MoveResponse+moveHandler g = do+  print g+  return $ MoveResponse UP (Just "always going up!")++-- TODO:+-- implement end handler if you want to clean up after a game has ended+endHandler :: GameRequest -> IO ()+endHandler = const $ return ()
+ src/Battlesnake/API/GameRequest.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.API.GameRequest where++import Battlesnake.Core.Battlesnake (Battlesnake)+import Battlesnake.Core.Board (Board)+import Battlesnake.Core.Game (Game)+import Data.Aeson (FromJSON)+import GHC.Generics++{-|+  The request sent to your battlesnake server on each turn.+  This contains all the information you need to determine your snakes next move.}+-}+data GameRequest = GameRequest+  { game :: Game, -- ^ Information about the current game. (See "Battlesnake.Core.Game")+    turn :: Integer, -- ^ The turn beeing played.+    board :: Board, -- ^ The game board. (See "Battlesnake.Core.Board")+    you :: Battlesnake -- ^ Your snake. (See "Battlesnake.Core.Battlesnake"")+  }+  deriving (Show, Generic)++instance FromJSON GameRequest
+ src/Battlesnake/API/InfoResponse.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.API.InfoResponse where++import Data.Aeson (ToJSON)+import Data.Text+import GHC.Generics++{-|+  The response to an info request.+  This can be used to modify the appearance of your snake on the game board.+-}+data InfoResponse = InfoResponse+  { apiversion :: Text, -- ^ The API version supported by the battlesnake server (currently 1).+    author :: Maybe Text, -- ^ The username of the author of the snake.+    color :: Maybe Text, -- ^ A color code used to set the color of the snake.+    head :: Maybe Text, -- ^ A custom head to use for the snake. (See https://play.battlesnake.com/customizations for options.)+    tail :: Maybe Text, -- ^ A custom tail to use for the snake. (See https://play.battlesnake.com/customizations for options.)+    version :: Maybe Text -- ^ The version of the snake.+  }+  deriving (Show, Generic)++instance ToJSON InfoResponse
+ src/Battlesnake/API/MoveResponse.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.API.MoveResponse where++import Battlesnake.Core.Direction+import Data.Aeson (ToJSON)+import Data.Text+import GHC.Generics++-- | The response to a move request.+data MoveResponse = MoveResponse+  { move :: Direction, -- ^ The direction for the next move.+    shout :: Maybe Text -- ^ An optional shout to be displayed with the move.+  }+  deriving (Show, Generic)++instance ToJSON MoveResponse
+ src/Battlesnake/Core/Battlesnake.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.Core.Battlesnake where++import Battlesnake.Core.Coordinate (Coordinate)+import Data.Aeson (FromJSON, ToJSON)+import Data.Text+import GHC.Generics+++{-|+  A snake in the game.+  See: https://docs.battlesnake.com/api/objects/battlesnake+-}+data Battlesnake = Battlesnake+  { id :: Text, -- ^ The ID of the snake.+    name :: Text, -- ^ The name of the snake.+    health :: Integer, -- ^ Current health of the snake (0-100).+    body :: [Coordinate], -- ^ A list of coordinates representing the snake's body.+    latency :: Text, -- ^ The snake's latency.+    head :: Coordinate, -- ^ The position of the snakes head+    length :: Integer, -- ^ The length of the snake.+    shout :: Text, -- ^ The snake's last moves shout.+    squad :: Maybe Text -- ^ The squad the snake is a part of (In squad mode).+  }+  deriving (Show, Generic)++instance ToJSON Battlesnake++instance FromJSON Battlesnake
+ src/Battlesnake/Core/Board.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.Core.Board where++import Battlesnake.Core.Battlesnake (Battlesnake)+import Battlesnake.Core.Coordinate (Coordinate)+import Data.Aeson (FromJSON, ToJSON)+import GHC.Generics++{-|+  The game board.+  See: https://docs.battlesnake.com/api/objects/board+-}+data Board = Board+  { height :: Integer, -- ^ The height of the board+    width :: Integer, -- ^ The width of the board+    food :: [Coordinate], -- ^ A list of coordinates representing food locations+    hazards :: [Coordinate], -- ^ A list of coordinates representing hazard locations+    snakes :: [Battlesnake] -- ^ A list of all snakes on the board+  }+  deriving (Show, Generic)++instance ToJSON Board++instance FromJSON Board
+ src/Battlesnake/Core/Coordinate.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.Core.Coordinate where++import Data.Aeson+  ( FromJSON (parseJSON),+    KeyValue ((.=)),+    ToJSON (toEncoding),+    pairs,+    withObject,+    (.:),+  )+import GHC.Generics++-- | Represents a coordinate on the game board.+data Coordinate = Coordinate+  { coordX :: Integer, -- ^ The X coordinate (0 indexed, from left to right)+    coordY :: Integer -- ^ The Y coordinate (0 indexed, from bottom to top)+  }+  deriving (Show, Eq, Generic)++instance ToJSON Coordinate where+  toEncoding (Coordinate x y) = pairs ("x" .= x <> "y" .= y)++instance FromJSON Coordinate where+  parseJSON = withObject "Coordinate" $ \v -> Coordinate <$> v .: "x" <*> v .: "y"
+ src/Battlesnake/Core/Direction.hs view
@@ -0,0 +1,13 @@+module Battlesnake.Core.Direction where++import Data.Aeson (ToJSON, Value (String), toJSON)+import Data.Text++{- |+  The possible directions your snake can move in.+  See: https://docs.battlesnake.com/api/requests/move+ -}+data Direction = UP | DOWN | LEFT | RIGHT deriving (Ord, Eq, Show)++instance ToJSON Direction where+  toJSON = String . toLower . pack . show
+ src/Battlesnake/Core/Game.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.Core.Game where++import Battlesnake.Core.Ruleset+import Data.Aeson (FromJSON)+import Data.Text+import GHC.Generics++{-|+  Information about the current game configuration.+  See: https://docs.battlesnake.com/api/objects/game+-}+data Game = Game+  { id :: Text, -- ^ The ID of the game.+    ruleset :: Ruleset, -- ^ The ruleset used in the game.+    timeout :: Integer -- ^ The maximum time in milliseconds the server will wait for a response from your server.+  }+  deriving (Show, Eq, Generic)++instance FromJSON Game
+ src/Battlesnake/Core/Ruleset.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DeriveGeneric #-}++module Battlesnake.Core.Ruleset where++import Data.Aeson (FromJSON)+import Data.Text+import GHC.Generics++{-|+  Information about the ruleset used in the game.+  See: https://docs.battlesnake.com/api/objects/game+-}+data Ruleset = Ruleset+  { name :: Text, -- ^ The name of the ruleset.+    version :: Text -- ^ The version of the ruleset.+  }+  deriving (Show, Eq, Generic)++instance FromJSON Ruleset
+ src/Battlesnake/Server.hs view
@@ -0,0 +1,72 @@+module Battlesnake.Server (GameRequestHandler, runBattlesnakeServer) where++import Battlesnake.API.GameRequest+import qualified Battlesnake.API.InfoResponse as Info+import Battlesnake.API.MoveResponse+import Control.Monad.IO.Class (liftIO)+import Data.Functor ((<&>))+import Data.Maybe (fromMaybe)+import System.Environment (lookupEnv)+import System.IO (BufferMode(LineBuffering), hSetBuffering, stdout)+import Text.Read (readMaybe)+import Web.Scotty+++-- | A handler for a battlesnake server request.+type GameRequestHandler a = GameRequest -> IO a++{- |+  Run a battlesnake server. Runs the server on the port specified by the environment variable "PORT" or port 3000 if the variable is not set.+  * A "Battlesnake.API.InfoResponse" to be returned when the server receives an info request.+  * A 'GameRequestHandler' to be called when the server receives a start request.+  * A 'GameRequestHandler' to be called when the server receives a move request.+  * A 'GameRequestHandler' to be called when the server receives an end request.+-}+runBattlesnakeServer ::+  Info.InfoResponse ->+  GameRequestHandler () ->+  GameRequestHandler MoveResponse ->+  GameRequestHandler () ->+  IO ()+runBattlesnakeServer info startHandler moveHandler endHandler = do+  hSetBuffering stdout LineBuffering+  envPort <- lookupEnv "PORT" <&> (readMaybe =<<)+  scotty (fromMaybe 3000 envPort) $+    routes+      info+      startHandler+      moveHandler+      endHandler++routes ::+  Info.InfoResponse ->+  GameRequestHandler () ->+  GameRequestHandler MoveResponse ->+  GameRequestHandler () ->+  ScottyM ()+routes info startHandler moveHandler endHandler = do+  get "/" $ handleInfoRequest info+  post "/start" $ handleStartRequest startHandler+  post "/move" $ handleMoveRequest moveHandler+  post "/end" $ handleEndRequest endHandler++handleInfoRequest :: Info.InfoResponse -> ActionM ()+handleInfoRequest = json++handleStartRequest :: GameRequestHandler () -> ActionM ()+handleStartRequest handler = do+  gameRequest <- jsonData+  liftIO $ handler gameRequest+  return ()++handleMoveRequest :: GameRequestHandler MoveResponse -> ActionM ()+handleMoveRequest handler = do+  gameRequest <- jsonData+  nextMove <- liftIO $ handler gameRequest+  json nextMove++handleEndRequest :: GameRequestHandler () -> ActionM ()+handleEndRequest handler = do+  gameRequest <- jsonData+  liftIO $ handler gameRequest+  return ()
+ starter-snake-haskell.cabal view
@@ -0,0 +1,89 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.35.1.+--+-- see: https://github.com/sol/hpack++name:           starter-snake-haskell+version:        1.0.0+synopsis:       A Haskell Battlesnake starter+description:    See README at <https://github.com/ccntrq/starter-snake-haskell>+category:       Game, Web+homepage:       https://github.com/ccntrq/starter-snake-haskell#readme+bug-reports:    https://github.com/ccntrq/starter-snake-haskell/issues+author:         Alexander Pankoff <ccntrq@screenri.de+maintainer:     Alexander Pankoff <ccntrq@screenri.de>+copyright:      2021 Alexander Pankoff+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    CHANGELOG.md+    LICENSE++source-repository head+  type: git+  location: https://github.com/ccntrq/starter-snake-haskell++library+  exposed-modules:+      Battlesnake.API.GameRequest+      Battlesnake.API.InfoResponse+      Battlesnake.API.MoveResponse+      Battlesnake.Core.Battlesnake+      Battlesnake.Core.Board+      Battlesnake.Core.Coordinate+      Battlesnake.Core.Direction+      Battlesnake.Core.Game+      Battlesnake.Core.Ruleset+      Battlesnake.Server+  other-modules:+      Paths_starter_snake_haskell+  hs-source-dirs:+      src+  default-extensions:+      OverloadedStrings+  ghc-options: -Wall+  build-depends:+      aeson >=1.4 && <3+    , base >=4.13 && <5+    , containers >=0.6 && <1+    , scotty >=0.11 && <1+    , text >=1.2 && <2+  default-language: Haskell2010++executable starter-snake-haskell-exe+  main-is: Main.hs+  other-modules:+      Paths_starter_snake_haskell+  hs-source-dirs:+      app+  default-extensions:+      OverloadedStrings+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall+  build-depends:+      aeson >=1.4 && <3+    , base >=4.13 && <5+    , containers >=0.6 && <1+    , scotty >=0.11 && <1+    , starter-snake-haskell+    , text >=1.2 && <2+  default-language: Haskell2010++test-suite starter-snake-haskell+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_starter_snake_haskell+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall+  build-depends:+      aeson >=1.4 && <3+    , base >=4.13 && <5+    , containers >=0.6 && <1+    , scotty >=0.11 && <1+    , starter-snake-haskell+    , text >=1.2 && <2+  default-language: Haskell2010
+ test/Spec.hs view
@@ -0,0 +1,3 @@+main :: IO ()+main = do+    putStrLn "Not implemented"