CodeBusters contest -- Silver League, rank 61/709
This commit is contained in:
parent
26f5532646
commit
f86959d9b2
|
|
@ -0,0 +1,158 @@
|
||||||
|
{-# LANGUAGE ScopedTypeVariables, LambdaCase, UnicodeSyntax #-}
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Data.Array
|
||||||
|
import Data.Either
|
||||||
|
import Data.Function
|
||||||
|
import Data.Functor.Identity
|
||||||
|
import Data.IORef
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Debug.Trace
|
||||||
|
import System.IO
|
||||||
|
|
||||||
|
type Team = Int
|
||||||
|
type BusterId = Int
|
||||||
|
type GhostId = Int
|
||||||
|
type Position = (Int, Int)
|
||||||
|
|
||||||
|
data Buster = Buster { busterId ∷ Int
|
||||||
|
, busterPosition ∷ Position
|
||||||
|
, busterTeam ∷ Team
|
||||||
|
, busterGhostId ∷ Maybe Int
|
||||||
|
, busterStunned ∷ Maybe Int
|
||||||
|
} deriving (Show)
|
||||||
|
|
||||||
|
data Ghost = Ghost { ghostId ∷ Int
|
||||||
|
, ghostPosition ∷ Position
|
||||||
|
, ghostBustersTied ∷ Int
|
||||||
|
, ghostStamina ∷ Int
|
||||||
|
} deriving (Show)
|
||||||
|
|
||||||
|
readEntity ∷ (Buster → a) → (Ghost → a) → String → a
|
||||||
|
readEntity buster ghost str
|
||||||
|
| entityType == -1 = ghost $ Ghost
|
||||||
|
{ ghostId = entityId
|
||||||
|
, ghostPosition = (x, y)
|
||||||
|
, ghostBustersTied = value
|
||||||
|
, ghostStamina = state
|
||||||
|
}
|
||||||
|
| otherwise = buster $ Buster
|
||||||
|
{ busterId = entityId
|
||||||
|
, busterPosition = (x, y)
|
||||||
|
, busterTeam = entityType
|
||||||
|
, busterGhostId = if state == 1 then Just value else Nothing
|
||||||
|
, busterStunned = if state == 2 then Just value else Nothing
|
||||||
|
}
|
||||||
|
where [entityId, x, y, entityType, state, value] = map read (words str)
|
||||||
|
|
||||||
|
main ∷ IO ()
|
||||||
|
main = do
|
||||||
|
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
|
||||||
|
|
||||||
|
-- Send your busters out into the fog to trap ghosts and bring them home!
|
||||||
|
-- Team 0 is top left, team 1 is bottom right.
|
||||||
|
bustersPerPlayer ∷ Int ← readLn
|
||||||
|
ghostCount ∷ Int ← readLn
|
||||||
|
myTeamId ∷ Int ← readLn
|
||||||
|
|
||||||
|
let theCenter = (8000, 4500)
|
||||||
|
let myBase = case myTeamId of { 0 → (0, 0); 1 → (16000, 9000) }
|
||||||
|
let theirBase = case myTeamId of { 1 → (1500, 1500); 0 → (14500, 7500) }
|
||||||
|
|
||||||
|
memory ← newIORef $ array ((0,0),(15,8)) [ ((x,y),(0,0)) | x ← [0..15], y ← [0..8] ]
|
||||||
|
let ptCell (x, y) = (max 0 (min 15 (x `div` 1000)), max 0 (min 8 (y `div` 1000)))
|
||||||
|
let cellCenter (x, y) = (1000 * x + 500, 1000 * y + 500)
|
||||||
|
|
||||||
|
ambusherId <- newIORef 0
|
||||||
|
|
||||||
|
forM_ [1..] $ \currentStep → do
|
||||||
|
numEntities ∷ Int ← readLn
|
||||||
|
|
||||||
|
entities ← replicateM numEntities (readEntity Left Right <$> getLine)
|
||||||
|
|
||||||
|
let myBusters = filter ((== myTeamId) . busterTeam) $ lefts entities
|
||||||
|
let theirBusters = filter ((/= myTeamId) . busterTeam) $ lefts entities
|
||||||
|
let theGhosts' = rights entities
|
||||||
|
let theGhosts = filter ((<= (10 + currentStep `div` 5)) . ghostStamina) theGhosts'
|
||||||
|
|
||||||
|
let countGhosts cell =
|
||||||
|
length $ flip filter theGhosts $ \g →
|
||||||
|
ptCell (ghostPosition g) == cell
|
||||||
|
|
||||||
|
modifyIORef' memory $ \arr → runIdentity $ do
|
||||||
|
let allCells = indices arr
|
||||||
|
let coveredCells =
|
||||||
|
flip filter allCells $ \c →
|
||||||
|
flip any myBusters $ \b →
|
||||||
|
dist (cellCenter c) (busterPosition b) < 1490
|
||||||
|
let knownCells = [ (c, (currentStep, 0)) | c ← coveredCells ]
|
||||||
|
return $! accum (\(t,n) m → (t, max n m)) (arr // knownCells) $
|
||||||
|
map (\c → (c, countGhosts c)) allCells
|
||||||
|
|
||||||
|
-- MOVE x y | BUST id | RELEASE
|
||||||
|
forM_ (zip [0..] myBusters) $ \(i, buster) → do
|
||||||
|
theAmbusherId ← readIORef ambusherId
|
||||||
|
let ambusher = bustersPerPlayer >= 3 && i == theAmbusherId
|
||||||
|
let here = busterPosition buster
|
||||||
|
let distTo = dist here
|
||||||
|
if busterHasGhost buster && distTo myBase < 1590 then
|
||||||
|
putStrLn "RELEASE"
|
||||||
|
else if busterHasGhost buster then do
|
||||||
|
when (i == theAmbusherId) $ modifyIORef ambusherId ((`mod` bustersPerPlayer) . (+1))
|
||||||
|
putStrLn $ unwords ["MOVE", show (fst myBase), show (snd myBase)]
|
||||||
|
else do
|
||||||
|
let isStunnable b = busterHasGhost b && distTo (busterPosition b) < 1760
|
||||||
|
case filter isStunnable theirBusters of
|
||||||
|
(b:_) → putStrLn $ unwords ["STUN", show (busterId b)]
|
||||||
|
[] → do
|
||||||
|
let nearGhosts' = filter ((< 1760) . distTo . ghostPosition) theGhosts
|
||||||
|
let nearGhosts = if not ambusher then nearGhosts'
|
||||||
|
else filter ((<= 2) . ghostStamina) nearGhosts'
|
||||||
|
let eligible = filter ((> 900) . distTo . ghostPosition) nearGhosts
|
||||||
|
if null nearGhosts then
|
||||||
|
if ambusher then
|
||||||
|
putStrLn $ unwords ["MOVE", show (fst theirBase), show (snd theirBase)]
|
||||||
|
else if null theGhosts then do
|
||||||
|
arr ← readIORef memory
|
||||||
|
let ghostCells = filter (\c → snd (arr!c) > 0) (indices arr)
|
||||||
|
if null ghostCells then do
|
||||||
|
let sumAges c = sum $ [ fromIntegral age / fromIntegral (d + 500)
|
||||||
|
| (c',(t,_)) ← assocs arr
|
||||||
|
, let age = currentStep - t
|
||||||
|
, let d = dist (cellCenter c) (cellCenter c')
|
||||||
|
, d < 1490
|
||||||
|
]
|
||||||
|
let score c = sumAges c / fromIntegral (1000 + distTo (cellCenter c))
|
||||||
|
let (x, y) = cellCenter $ maximumBy (compare `on` score) (indices arr)
|
||||||
|
putStrLn $ unwords ["MOVE", show x, show y]
|
||||||
|
else do
|
||||||
|
let (x, y) = minimumBy (compare `on` distTo) $ map cellCenter ghostCells
|
||||||
|
putStrLn $ unwords ["MOVE", show x, show y]
|
||||||
|
-- let θ = atan2 (fromIntegral $ snd theCenter - snd here)
|
||||||
|
-- (fromIntegral $ fst here - fst theCenter)
|
||||||
|
-- let θ' = if even i then θ + (pi / 12) else θ - (pi / 12)
|
||||||
|
-- let rStep = case bustersPerPlayer of { 2 → 4800; 3 → 2400; 4 → 1600; 5 → 1200 }
|
||||||
|
-- let r' = 2200 + rStep * fromIntegral i
|
||||||
|
-- let x' = max 1800 $ min (16000-1800) $ fst theCenter + round (r' * cos θ')
|
||||||
|
-- let y' = max 1800 $ min (9000-1800) $ snd theCenter - round (r' * sin θ')
|
||||||
|
-- putStrLn $ unwords ["MOVE", show x', show y']
|
||||||
|
else do
|
||||||
|
let closest = minimumBy (compare `on` (\g -> 800 * ghostStamina g + distTo (ghostPosition g))) theGhosts
|
||||||
|
let there = ghostPosition closest
|
||||||
|
if distTo there < 900 then
|
||||||
|
putStrLn $ unwords ["MOVE", show (2 * fst here - fst there), show (2 * snd here - snd there)]
|
||||||
|
else
|
||||||
|
putStrLn $ unwords ["MOVE", show (fst there), show (snd there)]
|
||||||
|
else if null eligible then do
|
||||||
|
let farthest = maximumBy (compare `on` distTo . ghostPosition) nearGhosts
|
||||||
|
let there = ghostPosition farthest
|
||||||
|
putStrLn $ unwords ["MOVE", show (2 * fst here - fst there), show (2 * snd here - snd there)]
|
||||||
|
else
|
||||||
|
putStrLn $ unwords ["BUST", show (ghostId (head eligible))]
|
||||||
|
|
||||||
|
dist ∷ Position → Position → Int
|
||||||
|
dist (x1, y1) (x2, y2) = round $ sqrt $ fromIntegral ((x2-x1)^2 + (y2-y1)^2)
|
||||||
|
|
||||||
|
busterHasGhost = isJust . busterGhostId
|
||||||
Loading…
Reference in New Issue