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