module Main where
import Nove
import Nove.Verse
import Nove.Draw.Ansi
import Zero ( total )
import Zero.Queue
import Data.List ( intersperse )
import Data.Foldable ( toList )
import Data.Vector as V ( find )
import Control.Monad ( join )
import Data.Bifunctor ( bimap )
import Terminal.Game
import Terminal.Game.Plane qualified as ATGP ( Cell, creaCell, colorCell, rgbColorCell, mapPlane, updatePlane )
import System.Random ( randoms, initStdGen )
-- Game IO loop
main :: IO ()
main = io 20 State { center = 0 , focus = 0 } $ verse 42
newtype Team = Team { n :: Word , color :: Color }
data Place = None | Some Place
newtype Dois = Dois Team Piece
instance Atom Dois where
void = None
mass a
| None <- a = False
| otherwise = True
move a
| Robot {} <- a , Move dir : _ <- ops a = Just dir
| Box {} <- a = push a
| otherwise = Nothing
data Mode = Dumb | Full
data State = State
{ rand :: [Int]
, center :: Int
, input :: (Maybe Char,Queue Char)
, mode :: Mode
, play :: Bool
}
-- util
me :: Unit -> Bool
me a
| Robot {} <- a , "@" <- name a = True
| otherwise = False
-- Unit Automaton
demo :: Automaton State Unit
demo = Automaton f
where
f :: Cell State Unit
f s ns u
| Robot { ops = [] } <- u , Just dir <- push = u { ops = [Move dir] } -- push
| me u = me'
| Robot {} <- u = u { ops = drop 1 $ ops u }
| Box { push = Nothing } <- u , Just dir <- push = u { push = Just dir } -- push
| Box {} <- u = Box { push = Nothing }
| otherwise = u
where
me' :: Unit
| Just 'h' <- fst $ input s = u { ops = [Move H] }
| Just 'j' <- fst $ input s = u { ops = [Move N] }
| Just 'k' <- fst $ input s = u { ops = [Move I] }
| Just 'l' <- fst $ input s = u { ops = [Move L] }
| otherwise = u { ops = [] }
push :: Maybe Dir
| [dir] <- Prelude.filter p total = Just $ opposite dir
| otherwise = Nothing
where
p :: Dir -> Bool
p dir = mass n && move n == Just (opposite dir)
where
n :: Unit
n = ns dir
instance Ansi State Unit where
-- Logic
logic :: GEnv -> (State,Verse Unit) -> Event -> Either () (State,Verse Unit)
logic g (s,v) e
| KeyPress 'q' <- e = Left ()
| KeyPress 'p' <- e = Right (s { play = True } , v)
| KeyPress 'P' <- e = Right (s { play = False } , v)
| KeyPress 'c' <- e = Right (s { mode = Dumb } , v)
| KeyPress 'C' <- e = Right (s { mode = Full } , v)
| KeyPress 'H' <- e = Right (s { center = shift v 1 H $ center s } , v)
| KeyPress 'J' <- e = Right (s { center = shift v 1 N $ center s } , v)
| KeyPress 'K' <- e = Right (s { center = shift v 1 I $ center s } , v)
| KeyPress 'L' <- e = Right (s { center = shift v 1 L $ center s } , v)
| KeyPress '.' <- e = Right (s' { play = False } , sim demo s' v)
| KeyPress k <- e = Right (s { input = input' k } , v)
| play s , Tick <- e = Right (s' , sim demo s' v)
| otherwise = Right (s , v)
where
-- prevent repeat before tick
input' :: Char -> (Maybe Char,Queue Char)
input' k
-- | Just (q,_) <- dequeue $ snd $ input s , k == q = input s
| otherwise = enqueue k <$> input s
z :: State
| Just (c,q) <- dequeue $ snd $ input s = s { input = (Just c , q) }
| otherwise = s { input = (Nothing , snd $ input s) }
s' :: State
-- | Just 'x' <- k = z { }
| otherwise = z
where
k :: Maybe Char
k = fst $ input z
-- Draw
draw :: GEnv -> (State,Verse Unit) -> Plane
draw e (s,v) = foldl (&) (blankPlaneFull e *** hex)
[ (1,1) % ui
, (1,1) %^> word "nove demo"
]
where
r :: Int
r = radius v
(mx,my) = indexToCoord v (center s)
write :: (Int,Int) -> (Coords,ATGP.Cell)
write (x,y) = (c , cellStyle a $ ATGP.creaCell $ grapheme a)
where
a :: Unit
a = get v n
-- get index of node taking scroll into account
n :: Int
n = shift v mx L . shift v my I $ coordToIndex v (mod (x - div y r * r) (3 * r) , mod y r)
-- stretch, tilt, margin, translate to library coordinate system (1-based (y,x))
c :: (Int,Int)
c = join bimap succ (r - y , 2 * (x + r) + y)
pad :: Int -> Plane -> Plane
pad n p = blankPlane (w + 4 * n) (h + 2 * n) *** p
where
(w,h) = planeSize p
hex :: Plane
hex = ATGP.updatePlane (blankPlane (succ $ 2 * r * 2) (succ $ 2 * r)) [ write (x,y) | x <- [-r..r] , y <- [-r..r] , abs (x + y) <= r ]
ui :: Plane
ui = pad 2 $ vcat $ intersperse (cell ' ')
[ key
, hcat
[ scope $ V.find (me . atom) $ nodes v
, cell ' '
, vcat [word "",info]
]
]
where
(c,q) = input s
key :: Plane
key = word (maybe id (:) c $ toList q) # color Black Vivid
info :: Plane
| Just n <- V.find (me . atom) $ nodes v = word $ show $ atom n
| otherwise = word "no robot found!" # color Red Vivid
scope :: Maybe (Node Unit) -> Plane
scope mn
| Just n <- mn , ns <- neighbour v (ix n) = foldl (&) (blankPlane 5 3)
[ (1,2) % let a = get v $ ns U in cell (grapheme a) # cellDraw a
, (1,4) % let a = get v $ ns I in cell (grapheme a) # cellDraw a
, (2,1) % let a = get v $ ns H in cell (grapheme a) # cellDraw a
, (2,3) % let a = atom n in cell (grapheme a)
, (2,5) % let a = get v $ ns L in cell (grapheme a) # cellDraw a
, (3,2) % let a = get v $ ns N in cell (grapheme a) # cellDraw a
, (3,4) % let a = get v $ ns M in cell (grapheme a) # cellDraw a
]
| otherwise = blankPlane 5 3
where
cellDraw :: Unit -> Draw
cellDraw a = ATGP.mapPlane $ cellStyle a
-- render
grapheme :: Unit -> Char
grapheme a
| None <- a , Dumb <- mode s = '.'
| None <- a , Full <- mode s = '∙'
| Wall <- a = '#'
| Box {} <- a = 'x'
| Robot {} <- a = maybe 'o' (const '@') $ move a
cellStyle :: Unit -> ATGP.Cell -> ATGP.Cell
cellStyle a
| Dumb <- mode s , me a = ATGP.colorCell White Dull
| Dumb <- mode s , not (mass a) = ATGP.colorCell Black Vivid
| Dumb <- mode s , Robot {} <- a , "t0" <- name a = ATGP.colorCell Blue Dull
| Dumb <- mode s , Robot {} <- a , "t1" <- name a = ATGP.colorCell Magenta Dull
| Dumb <- mode s , Robot {} <- a , "t2" <- name a = ATGP.colorCell Red Dull
| Dumb <- mode s , Robot {} <- a , "d0" <- name a = ATGP.colorCell Yellow Dull
| Dumb <- mode s , Robot {} <- a , "d1" <- name a = ATGP.colorCell Green Dull
| Dumb <- mode s , Robot {} <- a , 'L':_ <- name a = ATGP.colorCell Cyan Dull
| Dumb <- mode s , Robot {} <- a , "r0" <- name a = ATGP.colorCell White Vivid
| Dumb <- mode s , Robot {} <- a , "r1" <- name a = ATGP.colorCell Black Vivid
| Dumb <- mode s , Robot {} <- a , "m0" <- name a = ATGP.colorCell Green Vivid
| Dumb <- mode s , Robot {} <- a , "m1" <- name a = ATGP.colorCell Green Vivid
| Dumb <- mode s , Robot {} <- a , "p0" <- name a = ATGP.colorCell White Dull
| Dumb <- mode s , Robot {} <- a , "Tp" <- name a = ATGP.colorCell White Dull
| Dumb <- mode s , Robot {} <- a , "T0" <- name a = ATGP.colorCell Blue Dull
| Dumb <- mode s , Robot {} <- a , "T1" <- name a = ATGP.colorCell Magenta Dull
| Dumb <- mode s , Robot {} <- a , "T2" <- name a = ATGP.colorCell Red Dull
| Dumb <- mode s = ATGP.colorCell White Vivid
| Full <- mode s , me a = ATGP.rgbColorCell $ sRGB24 0x21 0xb2 0x8e -- #21b28e
| Full <- mode s , not (mass a) = ATGP.rgbColorCell $ sRGB24 0x66 0x66 0x66 -- #666666
| Full <- mode s , Robot {} <- a , "t0" <- name a = ATGP.rgbColorCell $ sRGB24 0x66 0x66 0x99 -- #666699
| Full <- mode s , Robot {} <- a , "t1" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x66 0x99 -- #996699
| Full <- mode s , Robot {} <- a , "t2" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x66 0x66 -- #996666
| Full <- mode s , Robot {} <- a , "d0" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x99 0x66 -- #999966
| Full <- mode s , Robot {} <- a , "d1" <- name a = ATGP.rgbColorCell $ sRGB24 0x66 0x99 0x66 -- #669966
| Full <- mode s , Robot {} <- a , 'L':_ <- name a = ATGP.rgbColorCell $ sRGB24 0x66 0x99 0x99 -- #669999
| Full <- mode s , Robot {} <- a , "r0" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x99 0x99 -- #999999
| Full <- mode s , Robot {} <- a , "r1" <- name a = ATGP.rgbColorCell $ sRGB24 0x66 0x66 0x66 -- #666666
| Full <- mode s , Robot {} <- a , "m0" <- name a = ATGP.rgbColorCell $ sRGB24 0x93 0xe9 0xbe -- #93e9be
| Full <- mode s , Robot {} <- a , "m1" <- name a = ATGP.rgbColorCell $ sRGB24 0x93 0xe9 0xbe -- #93e9be
| Full <- mode s , Robot {} <- a , "p0" <- name a = ATGP.rgbColorCell $ sRGB24 0xff 0xff 0xff -- #ffffff
| Full <- mode s , Robot {} <- a , "Tp" <- name a = ATGP.rgbColorCell $ sRGB24 0xff 0xff 0xff -- #ffffff
| Full <- mode s , Robot {} <- a , "T0" <- name a = ATGP.rgbColorCell $ sRGB24 0x66 0x66 0x99 -- #666699
| Full <- mode s , Robot {} <- a , "T1" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x66 0x99 -- #996699
| Full <- mode s , Robot {} <- a , "T2" <- name a = ATGP.rgbColorCell $ sRGB24 0x99 0x66 0x66 -- #996666
| Full <- mode s = ATGP.rgbColorCell $ sRGB24 0x99 0x99 0x99 -- #999999