diff --git a/code_royal/spago.dhall b/code_royal/spago.dhall index ab63ad2..39d7b45 100644 --- a/code_royal/spago.dhall +++ b/code_royal/spago.dhall @@ -3,7 +3,8 @@ Welcome to a Spago project! You can edit this file as you like. -} { name = "code_royal" -, dependencies = [ "arrays", "console", "effect", "integers", "math", "random" ] +, dependencies = + [ "arrays", "console", "effect", "integers", "js-date", "math", "random" ] , packages = ./packages.dhall , sources = [ "src/**/*.purs", "test/**/*.purs" ] } diff --git a/code_royal/src/Main.purs b/code_royal/src/Main.purs index ab45cd4..b2195c7 100644 --- a/code_royal/src/Main.purs +++ b/code_royal/src/Main.purs @@ -3,10 +3,11 @@ module Main where import Prelude import Control.MonadZero (guard) -import Data.Array (any, filter, foldl, head, length, reverse, sortBy, (!!)) +import Data.Array (any, filter, foldl, head, length, sortBy, (!!)) +import Data.JSDate (now, getTime) import Data.Maybe (Maybe(..), fromJust) import Effect (Effect) -import Effect.Console (log, error) +import Effect.Console (error, log) import Effect.Random (randomInt) import GameInput (Minion, Site, SiteInfo, ProtoSite, parseInitInput, parseInput) import Lib (dist) @@ -16,18 +17,26 @@ main :: Effect Unit main = do initInput <- parseInitInput error $ show initInput - loop initInput.numSites initInput.sites + loop initInput.numSites initInput.sites Nothing -loop :: Int -> Array SiteInfo -> Effect Unit -loop numSites siteInfo = do +loop :: Int -> Array SiteInfo -> Maybe Boolean -> Effect Unit +loop numSites siteInfo leftSide = do input <- parseInput numSites + + -- do we start on the left side of the map? + let leftSide' = case leftSide of + Just ls -> ls + Nothing -> (queen input.units).x < 500 let touchedSite = if input.touchedSite == -1 then Nothing else Just input.touchedSite - loop' numSites input.gold touchedSite (combinedSites input.sites) input.units + loop' numSites input.gold touchedSite (combinedSites input.sites) input.units leftSide' where + -- queenPos :: Array Minion -> { x :: Int, y :: Int } + -- queenPos minions = queen minions + -- combine sites with siteInfo combinedSites :: Array ProtoSite -> Array Site combinedSites sites = do @@ -35,6 +44,8 @@ loop numSites siteInfo = do infoS <- siteInfo guard $ protoS.id == infoS.id pure { id: protoS.id + , gold: protoS.gold + , maxMineSize: protoS.maxMineSize , structureType: protoS.structureType , owner: protoS.owner , param1: protoS.param1 @@ -44,25 +55,46 @@ loop numSites siteInfo = do , radius: infoS.radius } -loop' :: Int -> Int -> Maybe Int -> Array Site -> Array Minion -> Effect Unit -loop' numSites gold touchedSite sites units = do - -- error $ "Free sites: " <> (show $ map (\s -> s.id) freeSites) - -- error $ "Near sites: " <> (show $ map (\s -> s.id) nearSites) - - if (length $ friendlySites sites) > 3 - then log avoid - else case head $ nearSites queen sites of - Just sInfo -> do - let typ = if hasKnightsBarrack sites then 1 else 0 - log $ build sInfo typ - Nothing -> do - log avoid +loop' :: Int -> Int -> Maybe Int -> Array Site -> Array Minion -> Boolean -> Effect Unit +loop' numSites gold touchedSite sites units leftSide = do + n <- now + let t0 = getTime n + log buildAll t <- trainAll gold sites log $ t - loop numSites (toSiteInfo <$> sites) + n' <- now + let t1 = getTime n + -- error $ show t0 <> "Execution time: " <> (show $ t1 - t0) + loop numSites (toSiteInfo <$> sites) (Just leftSide) where + buildAll :: String + buildAll = + if (length $ friendlySites sites) == 0 + then buildTowers + else if (length $ friendlyMines sites) < 3 + then buildMines + else if (length $ friendlySites sites) > 5 && (length $ friendlySites sites) < 11 + then buildTowers + else if (length $ friendlySites sites) <= 5 + then case head $ nearSites (queen units) sites of + Just site -> do + let typ = if hasKnightsBarrack sites then 1 else 0 + build site typ + Nothing -> avoid + else avoid + + buildTowers :: String + buildTowers = case head $ nearSites (corner leftSide) sites of + Just site -> "BUILD " <> show site.id <> " TOWER" + Nothing -> avoid + + buildMines :: String + buildMines = case head $ nearNonEmptyMines (queen units) sites of + Just site -> "BUILD " <> show site.id <> " MINE" + Nothing -> avoid + avoid :: String avoid = case nearestEnemy of Just enemy -> "MOVE " <> show (site enemy).x <> " " <> show (site enemy).y @@ -70,12 +102,6 @@ loop' numSites gold touchedSite sites units = do where site enemy = unsafePartial $ fromJust $ head $ sortBy (\s1 s2 -> compare (dist enemy s2) (dist enemy s1)) (friendlySites sites) - queen :: Minion - queen = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 0) units - - enemyQueen :: Minion - enemyQueen = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 1) units - -- nearest non-queen enemy nearestEnemy :: Maybe Minion nearestEnemy = head $ filter (\u -> u.unitType /= -1 && isEnemy u) units @@ -85,13 +111,18 @@ trainAll :: Int -> Array Site -> Effect String trainAll gold sites = do randBarrack <- randomBarrack choose <- randomInt 1 100 - let barrack = if gold > 100 && choose < 23 then knightBarrack else randBarrack + let barrack = if gold > 140 + then if choose < 42 then knightBarrack else archerBarrack + else [] pure $ foldl siteToIds "TRAIN" barrack where siteToIds acc site = acc <> " " <> show site.id knightBarrack = case head $ knightBarracks sites of Just barrack -> [barrack] Nothing -> [] + archerBarrack = case head $ archerBarracks sites of + Just barrack -> [barrack] + Nothing -> [] randomBarrack = do let ownBarracks = filter isOwn $ barracks sites rand <- randomInt 0 $ length ownBarracks @@ -99,14 +130,26 @@ trainAll gold sites = do Just barrack -> pure [barrack] Nothing -> pure [] +queen :: Array Minion -> Minion +queen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 0) units + +enemyQueen :: Array Minion -> Minion +enemyQueen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 1) units + freeSites :: Array Site -> Array Site freeSites = filter (\s -> s.owner == -1) friendlySites :: Array Site -> Array Site friendlySites = filter (\s -> s.owner == 0) -nearSites :: Minion -> Array Site -> Array Site -nearSites unit sites = sortBy (compareSiteInfoDist unit) (freeSites sites) +friendlyMines :: Array Site -> Array Site +friendlyMines sites = filter (\s -> s.structureType == 0) $ friendlySites sites + +nearSites :: forall x. { x :: Int, y :: Int | x } -> Array Site -> Array Site +nearSites unit sites = sortBy (compareSiteDist unit) (freeSites sites) + +nearNonEmptyMines :: forall x. { x :: Int, y :: Int | x } -> Array Site -> Array Site +nearNonEmptyMines unit sites = filter (\s -> s.gold > 0) $ nearSites unit sites hasKnightsBarrack :: Array Site -> Boolean hasKnightsBarrack sites = any (\s -> s.param2 == 0) (friendlySites sites) @@ -114,11 +157,17 @@ hasKnightsBarrack sites = any (\s -> s.param2 == 0) (friendlySites sites) knightBarracks :: Array Site -> Array Site knightBarracks sites = filter (\s -> s.param2 == 0) (friendlySites sites) +archerBarracks :: Array Site -> Array Site +archerBarracks sites = filter (\s -> s.param2 == 1) (friendlySites sites) + toSiteInfo :: Site -> SiteInfo toSiteInfo s = { id: s.id, x: s.x, y: s.y, radius: s.radius } -compareSiteInfoDist :: Minion -> Site -> Site -> Ordering -compareSiteInfoDist u s1 s2 = compare (dist s1 u) (dist s2 u) +compareSiteDist :: forall x. { x :: Int, y :: Int | x } -> Site -> Site -> Ordering +compareSiteDist u s1 s2 = compare (dist s1 u) (dist s2 u) + +corner :: Boolean -> { x :: Int, y :: Int } +corner leftSide = if leftSide then { x: 0, y: 0 } else { x: 1920, y: 1000 } build :: forall e. { id :: Int | e } -> Int -> String build s typ = "BUILD " <> show s.id <> " BARRACKS-" <> t @@ -138,15 +187,3 @@ barracks sites = filter (\b -> b.structureType == 2) sites moveToPos :: forall e. { x :: Int, y :: Int | e } -> String moveToPos p = "MOVE " <> show p.x <> " " <> show p.y - --- Phase 1 --- Queen takes as much buildings as possible --- Build Archers only --- Phase 2 --- Queen destroys enemy buildings --- Build Archers only --- Phase 3 --- Queen avoids Knights --- All Buildings but one train Archers - --- nearestBuilding :: Pos \ No newline at end of file diff --git a/code_royal/src/ffi/GameInput.js b/code_royal/src/ffi/GameInput.js index 91fd518..162b028 100644 --- a/code_royal/src/ffi/GameInput.js +++ b/code_royal/src/ffi/GameInput.js @@ -32,14 +32,16 @@ exports.parseInput = function(numSites) { for (let i = 0; i < numSites; i++) { var inputs = readline().split(' '); var siteId = parseInt(inputs[0]); - var ignore1 = parseInt(inputs[1]); // used in future leagues - var ignore2 = parseInt(inputs[2]); // used in future leagues + var mineGold = parseInt(inputs[1]); // The total number of gold remaining to be mined from this site (-1 if unknown) + var maxMineSize = parseInt(inputs[2]); // The maximum rate that a mine can extract gold from this site (-1 if unknown) var structureType = parseInt(inputs[3]); // -1 = No structure, 2 = Barracks var owner = parseInt(inputs[4]); // -1 = No structure, 0 = Friendly, 1 = Enemy var param1 = parseInt(inputs[5]); var param2 = parseInt(inputs[6]); sites.push({ id: siteId, + gold: mineGold, + maxMineSize, structureType, owner, param1, diff --git a/code_royal/src/ffi/GameInput.purs b/code_royal/src/ffi/GameInput.purs index f581141..b8f22fa 100644 --- a/code_royal/src/ffi/GameInput.purs +++ b/code_royal/src/ffi/GameInput.purs @@ -23,7 +23,9 @@ type SiteInfo = type ProtoSite = { id :: Int - , structureType :: Int -- -1 No structure, 2 Barracks + , gold :: Int -- The total number of gold remaining to be mined from this site (-1 if unknown) + , maxMineSize :: Int -- The maximum rate that a mine can extract gold from this site (-1 if unknown) + , structureType :: Int -- -1 No structure, 0 Goldmine, 1 Tower, 2 Barracks , owner :: Int -- -1 No structure, 0 friendly, 1 enemy , param1 :: Int -- -1 No structure, else turns till training , param2 :: Int -- -1 No structure, barracks: 0 knight 1 archer @@ -34,6 +36,8 @@ type Site = , x :: Int , y :: Int , radius :: Int + , gold :: Int + , maxMineSize :: Int , structureType :: Int , owner :: Int , param1 :: Int