182 lines
7.0 KiB
Haskell
182 lines
7.0 KiB
Haskell
{-# LANGUAGE ScopedTypeVariables, LambdaCase, ViewPatterns, TupleSections #-}
|
|
{-# LANGUAGE BangPatterns #-}
|
|
|
|
import Control.Applicative
|
|
import Control.Arrow
|
|
import Control.Monad
|
|
import Data.Array.Unboxed
|
|
import Data.Function
|
|
import Data.List
|
|
import Data.Maybe
|
|
import Data.Monoid
|
|
import Data.IORef
|
|
import Data.STRef
|
|
import Debug.Trace
|
|
import System.CPUTime
|
|
import System.IO
|
|
import System.IO.Unsafe
|
|
import System.Random
|
|
import System.Timeout
|
|
|
|
import qualified Data.Array.ST as STA
|
|
import qualified Data.Foldable as F
|
|
import qualified Data.Traversable as T
|
|
|
|
main :: IO ()
|
|
main = do
|
|
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
|
|
opponentCount <- readLn
|
|
forever $ do
|
|
(gameRound :: Int) <- readLn
|
|
[read -> (myX :: Int), read -> (myY :: Int), (/= 0) . read -> myBackInTimeLeft]
|
|
<- words <$> getLine
|
|
|
|
opponents <- replicateM opponentCount $ do
|
|
[read -> (x :: Int), read -> (y :: Int), (/= 0) . read -> backInTimeLeft]
|
|
<- words <$> getLine
|
|
return ((x, y), backInTimeLeft)
|
|
|
|
-- '.' for empty, '0' for me, otherwise ID of opponent
|
|
grid <- fmap (array ((0,0),(34,19)) . concat) $ forM [0..19] $ \y ->
|
|
zipWith (\x c -> ((x,y),c)) [0..] <$> getLine
|
|
|
|
let claim c pt grid = if grid!pt == '.' then grid // [(pt,c)] else grid
|
|
|
|
startTime <- getCPUTime
|
|
|
|
gen <- newStdGen
|
|
let (action, nPts) = findTarget (myX, myY) (claim 'X' (myX,myY) grid) opponents myBackInTimeLeft gen
|
|
|
|
-- action: "x y" to move or "BACK rounds" to go back in time
|
|
case action of
|
|
Left n -> putStrLn $ "BACK " ++ show n
|
|
Right (tx, ty) -> putStrLn $ unwords $ map show [tx, ty]
|
|
|
|
stopTime <- getCPUTime
|
|
let diff = stopTime - startTime
|
|
|
|
hPutStrLn stderr $ show (diff `div` 1000000000) ++ " " ++ show nPts
|
|
|
|
findTarget :: RandomGen g
|
|
=> (Int, Int)
|
|
-> UArray (Int, Int) Char
|
|
-> [((Int, Int), Bool)]
|
|
-> Bool
|
|
-> g
|
|
-> (Either Int (Int, Int), Int)
|
|
findTarget myPt@(myX,myY) grid opponents myBackInTimeLeft gen =
|
|
(Right $ fromMaybe myPt $ fmap fst $ safeMaximumBy (compare `on` snd) scoredPts, length scoredPts)
|
|
where
|
|
scoredPts = unsafeTimeoutList 90000
|
|
. map (\pt -> let rt = bestRoute grid myPt pt in (head rt,) $! score rt)
|
|
. map fst . sortBy (compare `on` snd)
|
|
. map (\(r, (x,y)) -> ((x,y), r + dist myPt (x,y)))
|
|
. zip (randomRs (0, 2.0::Double) gen)
|
|
. filter (\p -> inRange (bounds grid) p && grid!p == '.')
|
|
$ indices grid
|
|
baseScore = scoreGrid' myPt grid
|
|
score rt = (scoreGrid' (last rt) (updateGrid grid rt) - baseScore) / (fromIntegral (length rt) ** 2)
|
|
scoreGrid' pt grid = scoreGrid grid
|
|
+ 3 * sum (map (sqrt . dist pt . fst) opponents)
|
|
dist (x0,y0) (x1,y1) = fromIntegral (abs (x1-x0) + abs (y1-y0))
|
|
|
|
neighbours :: (Int,Int) -> Int -> [(Int,Int)]
|
|
neighbours (x0,y0) n =
|
|
[0..2*n-1] >>= \i ->
|
|
[ (x0-n+i,y0-n)
|
|
, (x0+n,y0-n+i)
|
|
, (x0+n-i,y0+n)
|
|
, (x0-n,y0+n-i)
|
|
]
|
|
|
|
bestRoute :: UArray (Int,Int) Char -> (Int,Int) -> (Int,Int) -> [(Int,Int)]
|
|
bestRoute grid from@(x0,y0) to@(x1,y1) =
|
|
if freeCells rt1 < freeCells rt2 then rt2 else rt1
|
|
where
|
|
freeCells = count (\p -> grid!p == '.')
|
|
rt1 = map (,y0) (x0 `to` x1) ++ map (x1,) (y0 `to` y1)
|
|
rt2 = map (x0,) (y0 `to` y1) ++ map (,y1) (x0 `to` x1)
|
|
x `to` y | y >= x = [x+1..y]
|
|
| otherwise = [x-1,x-2..y]
|
|
|
|
updateGrid :: UArray (Int,Int) Char -> [(Int, Int)] -> UArray (Int,Int) Char
|
|
updateGrid grid route = STA.runSTUArray $ do
|
|
let valid = inRange (bounds grid)
|
|
grid' <- STA.thaw grid
|
|
forM_ route $ \p -> when (grid!p == '.') $ STA.writeArray grid' p '0'
|
|
doWhileM_ . fmap getAny . flip foldMapA (indices grid) $ \p -> do
|
|
g <- STA.readArray grid' p
|
|
if g /= '.' then pure (Any False) else do
|
|
gs <- mapM (\p' -> if valid p' then STA.readArray grid' p' else pure 'X')
|
|
(neighbours p 1)
|
|
if any (not . (`elem` ['.','0'])) gs
|
|
then Any True <$ STA.writeArray grid' p 'M'
|
|
else pure (Any False)
|
|
forM_ (indices grid) $ \p -> do
|
|
g <- STA.readArray grid' p
|
|
if g == '.' then STA.writeArray grid' p '0'
|
|
else when (g == 'M') $ STA.writeArray grid' p '.'
|
|
return grid'
|
|
|
|
filterA :: Applicative f => (a -> f Bool) -> [a] -> f [a]
|
|
filterA f [] = pure []
|
|
filterA f (x:xs) = (\c xs' -> if c then x:xs' else xs') <$> f x <*> filterA f xs
|
|
|
|
safeMaximumBy :: (a -> a -> Ordering) -> [a] -> Maybe a
|
|
safeMaximumBy _ [] = Nothing
|
|
safeMaximumBy f xs = Just $ maximumBy f xs
|
|
|
|
safeMinimumBy :: (a -> a -> Ordering) -> [a] -> Maybe a
|
|
safeMinimumBy _ [] = Nothing
|
|
safeMinimumBy f xs = Just $ minimumBy f xs
|
|
|
|
whileM_ :: Monad m => m Bool -> m a -> m ()
|
|
whileM_ mc m = mc >>= \c -> when c (m >> whileM_ mc m)
|
|
|
|
doWhileM_ :: Monad m => m Bool -> m ()
|
|
doWhileM_ mc = mc >>= \c -> when c (doWhileM_ mc)
|
|
|
|
count :: (a -> Bool) -> [a] -> Int
|
|
count f xs = go xs 0
|
|
where
|
|
go [] !n = n
|
|
go (x:xs) !n = go xs $ if f x then n+1 else n
|
|
|
|
-- Compute elements of the list to WHNF for `t` microseconds.
|
|
-- After `t` microseconds, abandon the calculation and terminate
|
|
-- the list. Note that this causes the length of the result to depend
|
|
-- on timing and system load. Marked "unsafe" for a reason!
|
|
unsafeTimeoutList :: Integer -> [a] -> [a]
|
|
unsafeTimeoutList t xs = unsafePerformIO $ do
|
|
start <- getCPUTime
|
|
return $ evalUntil (start + (1000000 * t)) xs
|
|
where
|
|
evalUntil end xs = unsafePerformIO $ do
|
|
now <- getCPUTime
|
|
r <- timeout (fromIntegral $ max 0 (end - now) `div` 1000000) $ return $! case xs of
|
|
[] -> []
|
|
(a:as) -> a `seq` (a:as)
|
|
return $ case r of
|
|
Nothing -> []
|
|
Just [] -> []
|
|
Just (a:as) -> (a : evalUntil end as)
|
|
|
|
foldMapA :: (Applicative f, F.Foldable t, Monoid v) => (a -> f v) -> t a -> f v
|
|
foldMapA f t = F.foldr (\x v -> mappend <$> v <*> f x) (pure mempty) t
|
|
|
|
scoreGrid :: UArray (Int, Int) Char -> Double
|
|
scoreGrid grid = sum $ map (sqrt . fromIntegral) $ elems $ scoreCells grid
|
|
|
|
scoreCells :: UArray (Int, Int) Char -> UArray (Int, Int) Int
|
|
scoreCells grid = array (bounds grid) $
|
|
[ (p,v) | (p, g) <- assocs grid
|
|
, let valid = inRange (bounds grid)
|
|
, let ns = map (\p' -> if valid p' then grid!p' else 'X')
|
|
(neighbours p 1 ++ neighbours p 2)
|
|
, let free = count (=='.') ns
|
|
, let mine = count (=='0') ns
|
|
, let other = count (not . (`elem` ['.','0'])) ns
|
|
, let v | g == '0' = 200 + 8*mine + 12*free + 15*other
|
|
| g == '.' = max 0 $ (5 * mine - 2 * other)
|
|
| otherwise = 0
|
|
] |