snake-hs/app/Main.hs

91 lines
3.1 KiB
Haskell
Raw Normal View History

2026-03-24 15:40:22 +03:00
module Main where
2026-03-28 18:38:00 +03:00
import SDL
2026-03-30 00:01:17 +03:00
import qualified SDL.Font as FONT
2026-03-28 18:38:00 +03:00
import qualified Data.Text as Text
import Control.Monad (unless)
import SDL.Raw (getTicks)
import Data.Word (Word32)
2026-03-30 00:01:17 +03:00
import Foreign.C (CInt)
import Snake
import Assets
import Graphics
2026-03-24 15:40:22 +03:00
main :: IO ()
main = do
2026-03-28 18:38:00 +03:00
initializeAll
2026-03-30 00:01:17 +03:00
FONT.initialize
window <- createWindow (Text.pack "Haskell Snake") defaultWindow {windowInitialSize = snakeWindowSize}
2026-03-28 18:38:00 +03:00
renderer <- createRenderer window (-1) defaultRenderer
2026-03-30 00:01:17 +03:00
assets <- loadAssets renderer
2026-03-30 01:32:52 +03:00
appLoop renderer assets (startState, False)
2026-03-30 00:01:17 +03:00
freeAssets assets
2026-03-28 18:38:00 +03:00
destroyRenderer renderer
destroyWindow window
targetFps :: Word32
2026-03-29 21:07:11 +03:00
targetFps = 5
2026-03-28 18:38:00 +03:00
targetFrameMs :: Word32
targetFrameMs = 1000 `div` targetFps
2026-03-30 00:01:17 +03:00
boardSize :: (Int, Int)
boardSize = (12, 8)
2026-03-30 01:32:52 +03:00
startState :: Game
startState = initialState boardSize (2, 2)
2026-03-30 00:01:17 +03:00
boardWindowSize :: (Int, Int) -> V2 CInt
boardWindowSize (w, h) = V2 (fromIntegral w * tileSize) (fromIntegral h * tileSize)
snakeWindowSize :: V2 CInt
snakeWindowSize = boardWindowSize boardSize
2026-03-30 01:32:52 +03:00
appLoop :: Renderer -> Assets -> (Game, Bool) -> IO ()
appLoop renderer assets (game, isEnd) = do
2026-03-28 18:38:00 +03:00
frameStart <- getTicks
events <- pollEvents
let eventIsExitPress event = case eventPayload event of
2026-03-30 00:01:17 +03:00
KeyboardEvent keyboardEvent ->
keyboardEventKeyMotion keyboardEvent == Pressed &&
keysymKeycode (keyboardEventKeysym keyboardEvent) == KeycodeQ
WindowClosedEvent _ -> True
_ -> False
2026-03-28 18:38:00 +03:00
exitPressed = any eventIsExitPress events
2026-03-30 00:01:17 +03:00
let eventIsRestartPress event = case eventPayload event of
KeyboardEvent keyboardEvent ->
keyboardEventKeyMotion keyboardEvent == Pressed &&
keysymKeycode (keyboardEventKeysym keyboardEvent) == KeycodeR
_ -> False
2026-03-30 01:32:52 +03:00
restartPressed = any eventIsRestartPress events
2026-03-30 00:01:17 +03:00
let eventDirectionPress event = case eventPayload event of
2026-03-29 18:39:27 +03:00
KeyboardEvent keyboardEvent ->
if keyboardEventKeyMotion keyboardEvent == Pressed
then getInputByKeycode $ keysymKeycode (keyboardEventKeysym keyboardEvent)
else NothingPressed
_ -> NothingPressed
2026-03-30 00:01:17 +03:00
input = foldMap eventDirectionPress events
2026-03-30 01:32:52 +03:00
(movementResult, updatedGame) <- if restartPressed then pure (IntoEmpty, startState) else if isEnd then pure (IntoEmpty, game) else runTick game input
let updatedIsEnd = (isEnd || movementResult == IntoSnake) && not restartPressed
renderFrame renderer assets (updatedGame, updatedIsEnd)
2026-03-29 18:39:27 +03:00
unless exitPressed $ do
2026-03-30 00:01:17 +03:00
frameEnd <- getTicks
let elapsed = frameEnd - frameStart
2026-03-29 18:39:27 +03:00
delay (targetFrameMs - elapsed)
2026-03-30 01:32:52 +03:00
appLoop renderer assets (updatedGame, updatedIsEnd)
2026-03-28 18:38:00 +03:00
2026-03-29 18:39:27 +03:00
getInputByKeycode :: Keycode -> MovementInput
getInputByKeycode KeycodeW = UpPressed
getInputByKeycode KeycodeUp = UpPressed
getInputByKeycode KeycodeD = RightPressed
getInputByKeycode KeycodeRight = RightPressed
getInputByKeycode KeycodeS = DownPressed
getInputByKeycode KeycodeDown = DownPressed
getInputByKeycode KeycodeA = LeftPressed
getInputByKeycode KeycodeLeft = LeftPressed
getInputByKeycode _ = NothingPressed