CodinGame/MarsLander/MarsLander.hs

82 lines
3.3 KiB
Haskell

import System.IO
import Control.Monad
import Data.Function
import Data.List
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
n <- liftM read getLine :: IO Int -- the number of points used to draw the surface of Mars.
pts <- replicateM n $ do
-- land_x - X coordinate of a surface point. (0 to 6999)
-- land_y - Y coordinate of a surface point.
-- By linking all the points together in a sequential fashion, you form the surface of Mars.
(land_x:land_y:_) <- liftM (map read . words) getLine :: IO [Double]
return (land_x, land_y)
let flats = [ ((x0, x1), y0) | ((x0,y0),(x1,y1)) <- zip pts (tail pts), y1 == y0 ]
let ((lzX0, lzX1), lzY) = maximumBy (compare `on` (uncurry (-) . fst)) flats
interact $ unlines . map (unwords . map show)
. calculateOutput (lzX0, lzX1, lzY)
. map (map read . words) . lines
calculateOutput :: (Double, Double, Double) -> [[Double]] -> [[Int]]
calculateOutput (lzX0, lzX1, lzY) = snd . refoldl step (0, 0)
where
targetX = (lzX0 + lzX1) / 2.0
targetY = lzY + 50
step :: (Double, Double) -> [Double] -> ((Double, Double), Maybe [Int])
step (lostX, lostY) (x:y:hs:vs:f:r:p:_) = ((lostX', lostY'), Just [r', p'])
-- x - the current horizontal position (0 to 6999).
-- y - the current vertical position.
-- hs - the horizontal speed (in m/s), can be negative.
-- vs - the vertical speed (in m/s), can be negative.
-- f - the quantity of remaining fuel in liters.
-- r - the rotation angle in degrees (-90 to 90).
-- p - the thrust power (0 to 4).
where
angle = (r + 90) * (pi/180)
(accelX, accelY) = fromPolar (p, angle)
targetVS = (-38)
accelY' = max 0 $
if y < targetY then 4
else if abs (targetX - x) > (y - targetY) then 3.711 - 0.2*vs
else if vs > targetVS then 3
else 3.711 + 0.5*(targetVS^2 - vs^2)/(targetY - y)
rangeX = if accelY' >= 4 then 0 else sqrt (4^2 - accelY'^2)
decelTimeX = (abs hs + 0.0001) / rangeX
projectedX = x + hs * decelTimeX - 0.5 * (signum hs * rangeX) * decelTimeX^2
accelX' = max (-rangeX) $ min rangeX $ 0.2 * (-hs) + 0.02 * (targetX - projectedX)
(power', angle') = toPolar (accelX' + lostX, accelY' + lostY)
landing = (x >= lzX0 && x <= lzX1) && (y <= lzY + 100)
rightDirection = max 0 $ cos (angle - angle')
p' = max 0 $ min 4 $ round (rightDirection * power')
r' = if landing then 0 else max (-90) $ min 90 $ truncate $ ((angle' * (180/pi)) - 90)
(accelX'', accelY'') = fromPolar (fromIntegral p', fromIntegral (r'+90) * (pi/180))
lostX' = 0.8*(accelX'-accelX'')
lostY' = 0.8*(accelY'-accelY'')
fromPolar :: Floating a => (a, a) -> (a, a)
fromPolar (r, th) = (r * cos th, r * sin th)
toPolar :: RealFloat a => (a, a) -> (a, a)
toPolar (x, y) = (sqrt (x^2 + y^2), atan2 y x)
-- Like `unfoldl` combined with `foldl`, or a stateful `map`.
refoldl :: (a -> b -> (a, Maybe c)) -> a -> [b] -> (a, [c])
refoldl _ a [] = (a, [])
refoldl f a (b:bs) = case f a b of
(a', Nothing) -> (a', [])
(a', Just c ) -> let ~(a'', cs) = refoldl f a' bs in (a'', c:cs)