Hypersonic contest -- Final version
This commit is contained in:
parent
399eeff0cf
commit
05ba371d66
|
|
@ -0,0 +1,481 @@
|
||||||
|
{-# OPTIONS_GHC -fno-warn-deprecated-flags #-}
|
||||||
|
{-# LANGUAGE LambdaCase, TupleSections, ImplicitParams, ConstraintKinds #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances, OverlappingInstances, ExplicitForAll #-}
|
||||||
|
{-# LANGUAGE RankNTypes, TypeFamilies #-}
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Arrow (first, second)
|
||||||
|
import Control.DeepSeq
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Writer
|
||||||
|
import Data.Array (Array,Ix,(//),(!))
|
||||||
|
import Data.Function
|
||||||
|
import Data.Functor.Identity
|
||||||
|
import Data.IORef
|
||||||
|
import Data.List
|
||||||
|
import Data.Map (Map,(\\))
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Monoid
|
||||||
|
import Data.Set (Set)
|
||||||
|
import Debug.Trace
|
||||||
|
import Prelude hiding (fst, snd)
|
||||||
|
import System.IO
|
||||||
|
import System.Random
|
||||||
|
|
||||||
|
import qualified Data.Array as A
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import qualified Data.Set as S
|
||||||
|
|
||||||
|
data Cell = EmptyC | BoxC (Maybe ItemType) | WallC
|
||||||
|
deriving (Eq,Show)
|
||||||
|
|
||||||
|
type Grid a = Array (Int, Int) a
|
||||||
|
|
||||||
|
type PlayerID = Int
|
||||||
|
type Position = (Int, Int)
|
||||||
|
|
||||||
|
data Player = Player { playerId :: PlayerID
|
||||||
|
, playerPos :: Position
|
||||||
|
, bombsLeft :: Int
|
||||||
|
, playerRange :: Int
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
|
playerPosL :: Lens' Player Position
|
||||||
|
playerPosL f p = (\pos' -> p { playerPos = pos' }) <$> f (playerPos p)
|
||||||
|
|
||||||
|
playerRangeL :: Lens' Player Int
|
||||||
|
playerRangeL f p = (\r' -> p { playerRange = r' }) <$> f (playerRange p)
|
||||||
|
|
||||||
|
bombsLeftL :: Lens' Player Int
|
||||||
|
bombsLeftL f p = (\b' -> p { bombsLeft = b' }) <$> f (bombsLeft p)
|
||||||
|
|
||||||
|
data Bomb = Bomb { bombOwner :: PlayerID
|
||||||
|
, bombPos :: Position
|
||||||
|
, bombCountdown :: Int
|
||||||
|
, bombRange :: Int
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
|
data ItemType = ExtraRange | ExtraBomb
|
||||||
|
deriving (Eq,Ord,Enum,Bounded,Read,Show)
|
||||||
|
|
||||||
|
data Item = Item { itemPos :: Position
|
||||||
|
, itemType :: ItemType
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
|
data Entity = P Player | B Bomb | I Item deriving Show
|
||||||
|
|
||||||
|
data GameData = GameData { _gridWidth :: Int
|
||||||
|
, _gridHeight :: Int
|
||||||
|
, _myId :: PlayerID
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
|
type HasGameData = (?gameData :: GameData)
|
||||||
|
|
||||||
|
data GameState = GameState { gameGrid :: Grid Cell
|
||||||
|
, gamePlayers :: [Player]
|
||||||
|
, gameBombs :: [Bomb]
|
||||||
|
, gameItems :: [Item]
|
||||||
|
, gamePaths :: Map Position [Position]
|
||||||
|
} deriving Show
|
||||||
|
|
||||||
|
gamePlayersL :: Lens' GameState [Player]
|
||||||
|
gamePlayersL f gs = (\ps' -> gs { gamePlayers = ps' }) <$> f (gamePlayers gs)
|
||||||
|
|
||||||
|
gameBombsL :: Lens' GameState [Bomb]
|
||||||
|
gameBombsL f gs = (\bs' -> gs { gameBombs = bs' }) <$> f (gameBombs gs)
|
||||||
|
|
||||||
|
gameItemsL :: Lens' GameState [Item]
|
||||||
|
gameItemsL f gs = (\is' -> gs { gameItems = is' }) <$> f (gameItems gs)
|
||||||
|
|
||||||
|
gamePathsL :: Lens' GameState (Map Position [Position])
|
||||||
|
gamePathsL f gs = (\ps' -> gs { gamePaths = ps' }) <$> f (gamePaths gs)
|
||||||
|
|
||||||
|
data Action = MOVE | BOMB deriving (Eq,Ord,Enum,Bounded,Read,Show)
|
||||||
|
|
||||||
|
gridWidth, gridHeight :: HasGameData => Int
|
||||||
|
gridWidth = _gridWidth ?gameData
|
||||||
|
gridHeight = _gridHeight ?gameData
|
||||||
|
|
||||||
|
myId :: HasGameData => PlayerID
|
||||||
|
myId = _myId ?gameData
|
||||||
|
|
||||||
|
main = do
|
||||||
|
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
|
||||||
|
|
||||||
|
gameData <- (\[w,h,myId] -> GameData w h myId)
|
||||||
|
. map read . words
|
||||||
|
<$> getLine
|
||||||
|
|
||||||
|
let ?gameData = gameData in loop
|
||||||
|
|
||||||
|
loop :: HasGameData => IO ()
|
||||||
|
loop = do
|
||||||
|
target <- newIORef (0, 0)
|
||||||
|
best <- newIORef []
|
||||||
|
forever $ do
|
||||||
|
grid <- getGrid
|
||||||
|
nEntities <- readLn
|
||||||
|
entities <- replicateM nEntities getEntity
|
||||||
|
|
||||||
|
let gs = GameState { gameGrid = grid
|
||||||
|
, gamePlayers = [ p | P p <- entities ]
|
||||||
|
, gameBombs = [ b | B b <- entities ]
|
||||||
|
, gameItems = [ i | I i <- entities ]
|
||||||
|
, gamePaths = pathsFrom gs myId
|
||||||
|
}
|
||||||
|
|
||||||
|
chooseAction target best gs 0
|
||||||
|
|
||||||
|
chooseAction :: HasGameData => IORef Position
|
||||||
|
-> IORef [Position]
|
||||||
|
-> GameState
|
||||||
|
-> Int
|
||||||
|
-> IO ()
|
||||||
|
chooseAction target best gs = fix $ \redo retries -> do
|
||||||
|
let Just me = find (\p -> playerId p == myId) (gamePlayers gs)
|
||||||
|
let iSurvive = any (\p -> playerId p == myId) . gamePlayers
|
||||||
|
|
||||||
|
targetPos <- readIORef target
|
||||||
|
bestPos <- readIORef best
|
||||||
|
|
||||||
|
let score (act, trg, acts) = do
|
||||||
|
guard $ act /= BOMB || bombsLeft me > 0
|
||||||
|
path <- M.lookup trg (gamePaths gs)
|
||||||
|
let (ptsMap, gs'') = simulate gs $ zip (act : acts ++ repeat MOVE) (tail path)
|
||||||
|
guard $ iSurvive gs''
|
||||||
|
let Just me' = find (\p -> playerId p == myId) (gamePlayers gs'')
|
||||||
|
let boxes = getSum $ M.findWithDefault 0 myId ptsMap
|
||||||
|
let kills = length (gamePlayers gs) - length (gamePlayers gs'')
|
||||||
|
let total = getSum . execWriter $ do
|
||||||
|
tell . Sum $ (12 * boxes)^2
|
||||||
|
tell . Sum $ 10 * kills
|
||||||
|
when (trg /= playerPos me) $ tell (Sum 20)
|
||||||
|
when (trg == targetPos) $ tell (Sum 15)
|
||||||
|
tell . Sum $ 5 * playerRange me
|
||||||
|
tell . Sum $ 3 * bombsLeft me
|
||||||
|
tell . Sum $ max 0 (5 - length path `div` 2)
|
||||||
|
pure (total, (act, trg, path, gs''))
|
||||||
|
|
||||||
|
targets <- fmap S.toList . execWriterT $ do
|
||||||
|
tell $ S.singleton targetPos
|
||||||
|
tell $ S.fromList bestPos
|
||||||
|
tell $ S.fromList $ do
|
||||||
|
let (x0,y0) = playerPos me
|
||||||
|
x <- [x0-2..x0+2]
|
||||||
|
guard $ 0 <= x && x < gridWidth
|
||||||
|
y <- [y0-2..y0+2]
|
||||||
|
guard $ 0 <= y && y < gridHeight
|
||||||
|
pure (x,y)
|
||||||
|
tell . S.fromList =<< liftIO (replicateM 25 $ randomTarget gs)
|
||||||
|
|
||||||
|
actions' <- (fmap (,) [MOVE,BOMB] <*>) <$> shuffleIO targets
|
||||||
|
actions <- forM actions' $ \(act,trg) -> do
|
||||||
|
delay <- randomRIO (0,8)
|
||||||
|
pure (act, trg, replicate delay MOVE ++ [BOMB])
|
||||||
|
let scores = sortBy (flip compare `on` fst) $ mapMaybe score actions
|
||||||
|
|
||||||
|
-- forM_ scores $ \(sc, (act, trg, path, _)) -> do
|
||||||
|
-- traceM $ fromWords "SCORE:" sc act path
|
||||||
|
|
||||||
|
writeIORef best . map (snd . snd) . take 15 $ scores
|
||||||
|
|
||||||
|
case scores of
|
||||||
|
((_, (action, trg, path, gs')):_) -> do
|
||||||
|
when (trg /= targetPos) $ target .= pure trg
|
||||||
|
let (myX, myY) = playerPos me
|
||||||
|
let (nx, ny) = case path of { (_:(x,y):_) -> (x,y); _ -> (myX,myY) }
|
||||||
|
putStrLn $ fromWords action nx ny
|
||||||
|
_ -> do
|
||||||
|
target .= randomTarget gs
|
||||||
|
if retries < 5 then
|
||||||
|
redo (retries + 1)
|
||||||
|
else
|
||||||
|
let (myX, myY) = playerPos me
|
||||||
|
in trace "Stuck!" $ putStrLn $ fromWords MOVE myX myY
|
||||||
|
|
||||||
|
mwhen :: Monoid m => m -> Bool -> m
|
||||||
|
mwhen x False = mempty
|
||||||
|
mwhen x True = x
|
||||||
|
|
||||||
|
shuffleIO :: [a] -> IO [a]
|
||||||
|
shuffleIO [] = pure []
|
||||||
|
shuffleIO xs = do
|
||||||
|
n <- randomRIO (0, length xs - 1)
|
||||||
|
let (as, x:bs) = splitAt n xs
|
||||||
|
(x:) <$> shuffleIO (as ++ bs)
|
||||||
|
|
||||||
|
getGrid :: HasGameData => IO (Grid Cell)
|
||||||
|
getGrid = A.array ((0,0),(gridWidth-1,gridHeight-1))
|
||||||
|
. concat . zipWith (\y -> map (\(x,v) -> ((x,y),v))) [0..]
|
||||||
|
<$> replicateM gridHeight (zip [0..] . map readCell <$> getLine)
|
||||||
|
|
||||||
|
readCell '.' = EmptyC
|
||||||
|
readCell '0' = BoxC Nothing
|
||||||
|
readCell '1' = BoxC (Just ExtraRange)
|
||||||
|
readCell '2' = BoxC (Just ExtraBomb)
|
||||||
|
readCell 'X' = WallC
|
||||||
|
|
||||||
|
getEntity = flip fmap (map read . words <$> getLine) $
|
||||||
|
\[entityType,owner,x,y,p1,p2] -> case entityType of
|
||||||
|
0 -> P $ Player owner (x,y) p1 p2
|
||||||
|
1 -> B $ Bomb owner (x,y) p1 p2
|
||||||
|
2 -> I $ Item (x,y) (decodeItemType p1)
|
||||||
|
|
||||||
|
decodeItemType 1 = ExtraRange
|
||||||
|
decodeItemType 2 = ExtraBomb
|
||||||
|
|
||||||
|
randomTarget :: HasGameData => GameState -> IO Position
|
||||||
|
randomTarget gs = join $ maybe randomPosIO pure <$> randomItemIO (nubOrd targets)
|
||||||
|
where
|
||||||
|
randomPosIO = (,) <$> randomRIO (0, gridWidth - 1)
|
||||||
|
<*> randomRIO (0, gridHeight - 1)
|
||||||
|
targets = [ p' | (p, BoxC _) <- A.assocs (gameGrid gs)
|
||||||
|
, p' <- neighbours p
|
||||||
|
, (gameGrid gs)!p' == EmptyC
|
||||||
|
, not (any (\b -> bombPos b == p') (gameBombs gs))
|
||||||
|
] ++ map itemPos (gameItems gs)
|
||||||
|
++ map playerPos (gamePlayers gs)
|
||||||
|
nubOrd = S.toList . S.fromList
|
||||||
|
|
||||||
|
neighbours :: HasGameData => Position -> [Position]
|
||||||
|
neighbours (x0,y0) =
|
||||||
|
[ (x,y) | (x,y) <- [(x0-1,y0),(x0+1,y0),(x0,y0-1),(x0,y0+1)]
|
||||||
|
, x >= 0, y >= 0, x < gridWidth, y < gridHeight ]
|
||||||
|
|
||||||
|
randomItemIO :: [a] -> IO (Maybe a)
|
||||||
|
randomItemIO xs
|
||||||
|
| null xs = pure Nothing
|
||||||
|
| otherwise = Just . (xs!!) <$> randomRIO (0, length xs - 1)
|
||||||
|
|
||||||
|
gamePlayerById :: PlayerID -> GameState -> Maybe Player
|
||||||
|
gamePlayerById plId = find (\p -> playerId p == plId) . gamePlayers
|
||||||
|
|
||||||
|
pathsFrom :: HasGameData => GameState -> PlayerID -> Map Position [Position]
|
||||||
|
pathsFrom gs plId = go M.empty $ M.singleton plPos [plPos]
|
||||||
|
where
|
||||||
|
Just plPos = playerPos <$> gamePlayerById plId gs
|
||||||
|
go visited fringe
|
||||||
|
| M.null fringe' = visited'
|
||||||
|
| otherwise = go visited' fringe'
|
||||||
|
where
|
||||||
|
visited' = visited <> fringe
|
||||||
|
fringe' = M.fromList
|
||||||
|
[ (pos', path ++ [pos'])
|
||||||
|
| (pos@(x,y), path) <- M.toList fringe
|
||||||
|
, pos'@(nx, ny) <- [ (x-1,y),(x+1,y),(x,y-1),(x,y+1) ]
|
||||||
|
, nx >= 0, ny >= 0, nx < gridWidth, ny < gridHeight
|
||||||
|
, not (M.member pos' visited')
|
||||||
|
, not (blocked pos') || pos' == plPos
|
||||||
|
]
|
||||||
|
blocked p = (gameGrid gs)!p /= EmptyC || S.member p obstacles
|
||||||
|
obstacles = S.fromList $ bombPos <$> gameBombs gs
|
||||||
|
|
||||||
|
simulate :: HasGameData => GameState -> [(Action, Position)] -> (Map PlayerID (Sum Int), GameState)
|
||||||
|
simulate gs0 path
|
||||||
|
| null path && null (gameBombs gs0) = (M.empty, gs0)
|
||||||
|
| otherwise = (ptsMap1 <> ptsMap2, gs3)
|
||||||
|
where
|
||||||
|
(ptsMap1, gs1) = step gs0
|
||||||
|
maybeMe = find (\p -> playerId p == myId) (gamePlayers gs1)
|
||||||
|
(gs2, path') = case path of
|
||||||
|
((act,pos):path') | (gameGrid gs0)!pos == EmptyC
|
||||||
|
, not (any (\b -> bombPos b == pos) $ gameBombs gs1)
|
||||||
|
-> let gs' = over gamePlayersL (map (updatePlayer pos)) gs1
|
||||||
|
gs'' = set gamePathsL (pathsFrom gs' myId) gs'
|
||||||
|
in (,path') . ($ gs'') $ case act of
|
||||||
|
BOMB | Just me <- maybeMe, bombsLeft me > 0
|
||||||
|
-> over gameBombsL (newBomb me :)
|
||||||
|
_ -> id
|
||||||
|
_ -> (gs1, [])
|
||||||
|
(ptsMap2, gs3) = simulate gs2 path'
|
||||||
|
updatePlayer pos p
|
||||||
|
| playerId p == myId = set playerPosL pos p
|
||||||
|
| otherwise = p
|
||||||
|
newBomb p = Bomb { bombOwner = playerId p
|
||||||
|
, bombPos = playerPos p
|
||||||
|
, bombCountdown = 8
|
||||||
|
, bombRange = playerRange p
|
||||||
|
}
|
||||||
|
|
||||||
|
step :: HasGameData => GameState -> (Map PlayerID (Sum Int), GameState)
|
||||||
|
step gs = ptsMap `deepseq` newGrid `deepseq` (ptsMap, gs')
|
||||||
|
where
|
||||||
|
bombs = map timeStep $ gameBombs gs
|
||||||
|
timeStep b = b { bombCountdown = bombCountdown b - 1 }
|
||||||
|
expired b = bombCountdown b < 1
|
||||||
|
(expiredBombs, remainingBombs0) = partition expired bombs
|
||||||
|
(explodedBombs, remainingBombs) = explodeBombs gs expiredBombs remainingBombs0
|
||||||
|
boxesDestroyed = [ ((pos, itm), (bombOwner b, Sum 1))
|
||||||
|
| (pos, BoxC itm) <- A.assocs (gameGrid gs)
|
||||||
|
, b <- explodedBombs
|
||||||
|
, bombInRange gs pos b
|
||||||
|
]
|
||||||
|
itemsCollected = M.fromListWith (<>)
|
||||||
|
$ [ (playerId p, [itemType i])
|
||||||
|
| p <- gamePlayers gs
|
||||||
|
, i <- gameItems gs
|
||||||
|
, itemPos i == playerPos p
|
||||||
|
]
|
||||||
|
items' = filter (not . \i -> any (bombInRange gs (itemPos i)) explodedBombs
|
||||||
|
|| any (\p -> playerPos p == itemPos i) (gamePlayers gs))
|
||||||
|
(gameItems gs)
|
||||||
|
++ [ Item { itemPos = p, itemType = i } | ((p, Just i), _) <- boxesDestroyed ]
|
||||||
|
players' = map (\p -> foldl' applyItem p $ M.findWithDefault [] (playerId p) itemsCollected)
|
||||||
|
$ map (\p -> over bombsLeftL (+ countBombs (playerId p)) p)
|
||||||
|
$ filter (not . \p -> any (bombInRange gs (playerPos p)) explodedBombs)
|
||||||
|
(gamePlayers gs)
|
||||||
|
countBombs plId = length $ filter (\b -> bombOwner b == plId) explodedBombs
|
||||||
|
applyItem = flip $ \case
|
||||||
|
ExtraRange -> over playerRangeL (+1)
|
||||||
|
ExtraBomb -> over bombsLeftL (+1)
|
||||||
|
ptsMap = M.map getSum $ M.fromListWith (<>) $ map snd boxesDestroyed
|
||||||
|
newGrid = gameGrid gs // [ (p, EmptyC) | ((p, _), _) <- boxesDestroyed ]
|
||||||
|
gs' = gs { gameGrid = newGrid
|
||||||
|
, gamePlayers = players'
|
||||||
|
, gameBombs = remainingBombs
|
||||||
|
, gameItems = items'
|
||||||
|
}
|
||||||
|
|
||||||
|
explodeBombs :: GameState -> [Bomb] -> [Bomb] -> ([Bomb], [Bomb])
|
||||||
|
explodeBombs gs triggered remaining
|
||||||
|
| null triggered' = (triggered, remaining)
|
||||||
|
| otherwise = first (triggered++) $
|
||||||
|
explodeBombs gs triggered' remaining'
|
||||||
|
where
|
||||||
|
isTriggered b = any (bombInRange gs (bombPos b)) triggered
|
||||||
|
(triggered', remaining') = partition isTriggered remaining
|
||||||
|
|
||||||
|
bombInRange :: GameState -> Position -> Bomb -> Bool
|
||||||
|
bombInRange gs (x,y) b
|
||||||
|
= (x == bx && y > (by - r) && y <= by && allEmpty [(x,y')|y'<-[y+1..by-1]])
|
||||||
|
|| (x == bx && y >= by && y < (by + r) && allEmpty [(x,y')|y'<-[by+1..y-1]])
|
||||||
|
|| (y == by && x > (bx - r) && x <= bx && allEmpty [(x',y)|x'<-[x+1..bx-1]])
|
||||||
|
|| (y == by && x >= bx && x < (bx + r) && allEmpty [(x',y)|x'<-[bx+1..x-1]])
|
||||||
|
where
|
||||||
|
allEmpty ps =
|
||||||
|
and [ all (\p -> (gameGrid gs)!p == EmptyC) ps
|
||||||
|
, all (\b -> not (bombPos b `elem` ps)) (gameBombs gs)
|
||||||
|
, all (\i -> not (itemPos i `elem` ps)) (gameItems gs)
|
||||||
|
]
|
||||||
|
(bx, by) = bombPos b
|
||||||
|
r = bombRange b
|
||||||
|
|
||||||
|
inGrid :: HasGameData => Position -> Bool
|
||||||
|
inGrid (x, y) = x >= 0 && y >= 0 && x < gridWidth && y < gridHeight
|
||||||
|
|
||||||
|
(.=) :: IORef a -> IO a -> IO ()
|
||||||
|
r .= m = m >>= writeIORef r
|
||||||
|
infixr 1 .=
|
||||||
|
|
||||||
|
(%=) :: IORef a -> (a -> IO a) -> IO ()
|
||||||
|
r %= f = readIORef r >>= f >>= writeIORef r
|
||||||
|
infixr 1 %=
|
||||||
|
|
||||||
|
instance NFData Cell where rnf x = x `seq` ()
|
||||||
|
|
||||||
|
class IsWord a where
|
||||||
|
fromWord :: a -> String
|
||||||
|
|
||||||
|
instance IsWord String where
|
||||||
|
fromWord = id
|
||||||
|
{-# INLINABLE fromWord #-}
|
||||||
|
|
||||||
|
instance {-# OVERLAPPABLE #-} Show a => IsWord a where
|
||||||
|
fromWord = show
|
||||||
|
{-# INLINABLE fromWord #-}
|
||||||
|
|
||||||
|
class Words a r where
|
||||||
|
fromWords' :: (String -> String) -> a -> r
|
||||||
|
|
||||||
|
instance (IsWord a, Words b r) => Words a ((->) b r) where
|
||||||
|
fromWords' s a = fromWords' (s . (fromWord a ++) . (" " ++))
|
||||||
|
{-# INLINABLE fromWords' #-}
|
||||||
|
|
||||||
|
instance IsWord a => Words a String where
|
||||||
|
fromWords' s a = s (fromWord a)
|
||||||
|
{-# INLINABLE fromWords' #-}
|
||||||
|
|
||||||
|
fromWords :: Words a r => a -> r
|
||||||
|
fromWords = fromWords' id
|
||||||
|
|
||||||
|
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
|
||||||
|
type Lens' s a = Lens s s a a
|
||||||
|
|
||||||
|
view :: Lens s t a b -> s -> a
|
||||||
|
view l = getConst . l Const
|
||||||
|
|
||||||
|
over :: Lens s t a b -> (a -> b) -> s -> t
|
||||||
|
over l f = runIdentity . l (Identity . f)
|
||||||
|
|
||||||
|
set :: Lens s t a b -> b -> s -> t
|
||||||
|
set l x = over l (const x)
|
||||||
|
|
||||||
|
fst :: Tuple1 s => s -> Elem1 s
|
||||||
|
fst = view _1
|
||||||
|
|
||||||
|
snd :: Tuple2 s => s -> Elem2 s
|
||||||
|
snd = view _2
|
||||||
|
|
||||||
|
class Tuple1 s where
|
||||||
|
type Elem1 s
|
||||||
|
type Repl1 s b
|
||||||
|
_1 :: Lens s (Repl1 s b) (Elem1 s) b
|
||||||
|
|
||||||
|
class Tuple1 s => Tuple2 s where
|
||||||
|
type Elem2 s
|
||||||
|
type Repl2 s b
|
||||||
|
_2 :: Lens s (Repl2 s b) (Elem2 s) b
|
||||||
|
|
||||||
|
class Tuple2 s => Tuple3 s where
|
||||||
|
type Elem3 s
|
||||||
|
type Repl3 s b
|
||||||
|
_3 :: Lens s (Repl3 s b) (Elem3 s) b
|
||||||
|
|
||||||
|
class Tuple3 s => Tuple4 s where
|
||||||
|
type Elem4 s
|
||||||
|
type Repl4 s b
|
||||||
|
_4 :: Lens s (Repl4 s b) (Elem4 s) b
|
||||||
|
|
||||||
|
instance Tuple1 (a,b) where
|
||||||
|
type Elem1 (a,b) = a
|
||||||
|
type Repl1 (a,b) a' = (a',b)
|
||||||
|
_1 f (a,b) = fmap (,b) (f a)
|
||||||
|
instance Tuple2 (a,b) where
|
||||||
|
type Elem2 (a,b) = b
|
||||||
|
type Repl2 (a,b) b' = (a,b')
|
||||||
|
_2 f (a,b) = fmap (a,) (f b)
|
||||||
|
|
||||||
|
instance Tuple1 (a,b,c) where
|
||||||
|
type Elem1 (a,b,c) = a
|
||||||
|
type Repl1 (a,b,c) a' = (a',b,c)
|
||||||
|
_1 f (a,b,c) = fmap (,b,c) (f a)
|
||||||
|
instance Tuple2 (a,b,c) where
|
||||||
|
type Elem2 (a,b,c) = b
|
||||||
|
type Repl2 (a,b,c) b' = (a,b',c)
|
||||||
|
_2 f (a,b,c) = fmap (a,,c) (f b)
|
||||||
|
instance Tuple3 (a,b,c) where
|
||||||
|
type Elem3 (a,b,c) = c
|
||||||
|
type Repl3 (a,b,c) c' = (a,b,c')
|
||||||
|
_3 f (a,b,c) = fmap (a,b,) (f c)
|
||||||
|
|
||||||
|
instance Tuple1 (a,b,c,d) where
|
||||||
|
type Elem1 (a,b,c,d) = a
|
||||||
|
type Repl1 (a,b,c,d) a' = (a',b,c,d)
|
||||||
|
_1 f (a,b,c,d) = fmap (,b,c,d) (f a)
|
||||||
|
instance Tuple2 (a,b,c,d) where
|
||||||
|
type Elem2 (a,b,c,d) = b
|
||||||
|
type Repl2 (a,b,c,d) b' = (a,b',c,d)
|
||||||
|
_2 f (a,b,c,d) = fmap (a,,c,d) (f b)
|
||||||
|
instance Tuple3 (a,b,c,d) where
|
||||||
|
type Elem3 (a,b,c,d) = c
|
||||||
|
type Repl3 (a,b,c,d) c' = (a,b,c',d)
|
||||||
|
_3 f (a,b,c,d) = fmap (a,b,,d) (f c)
|
||||||
|
instance Tuple4 (a,b,c,d) where
|
||||||
|
type Elem4 (a,b,c,d) = d
|
||||||
|
type Repl4 (a,b,c,d) d' = (a,b,c,d')
|
||||||
|
_4 f (a,b,c,d) = fmap (a,b,c,) (f d)
|
||||||
|
|
||||||
|
instance NFData a => NFData (Sum a) where rnf (Sum x) = rnf x
|
||||||
|
|
||||||
|
-- vim:set sw=4 et:
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
13 11 0
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
.............
|
||||||
|
1
|
||||||
|
0 0 0 0 0 3
|
||||||
Loading…
Reference in New Issue