build giants

This commit is contained in:
weiss
2020-04-11 17:17:27 +02:00
parent 11bafb6f60
commit c9547f5197
2 changed files with 96 additions and 43 deletions

View File

@@ -1,11 +1,13 @@
module Main where module Main where
-- mines only on side
-- build giants
import Prelude import Prelude
import Control.Monad.State (State, gets, runState) import Control.Monad.State (State, gets, modify_, runState)
import Control.MonadZero (guard) import Control.MonadZero (guard)
import Data.Array (any, filter, foldl, head, length, sortBy) import Data.Array (any, filter, foldl, head, length, sortBy)
-- import Data.JSDate (now, getTime)
import Data.Maybe (Maybe(..), fromJust) import Data.Maybe (Maybe(..), fromJust)
import Data.Tuple (fst, snd) import Data.Tuple (fst, snd)
import Effect (Effect) import Effect (Effect)
@@ -17,7 +19,7 @@ import Partial.Unsafe (unsafePartial)
type GameState = type GameState =
{ gold :: Int { gold :: Int
, numSites :: Int , numSites :: Int
, touchedSite :: Maybe Int , touchedSite :: Int
, sites :: Array Site , sites :: Array Site
, units :: Array Minion , units :: Array Minion
, leftSide :: Boolean , leftSide :: Boolean
@@ -29,44 +31,43 @@ main = do
error $ show initInput error $ show initInput
loop initInput.numSites initInput.sites Nothing loop initInput.numSites initInput.sites Nothing
loop :: Int -> Array SiteInfo -> Maybe Boolean -> Effect Unit loop :: Int -> Array SiteInfo -> Maybe GameState -> Effect Unit
loop numSites siteInfo leftSide = do loop numSites siteInfo gameState = do
input <- parseInput numSites input <- parseInput numSites
-- do we start on the left side of the map? -- do we start on the left side of the map?
let leftSide' = case leftSide of let leftSide' = case gameState of
Just ls -> ls Just gs -> gs.leftSide
Nothing -> (queen input.units).x < 500 Nothing -> (queen input.units).x < 500
let touchedSite = if input.touchedSite == -1 let gameState' =
then Nothing
else Just input.touchedSite
let gameState =
{ gold: input.gold { gold: input.gold
, numSites , numSites
, touchedSite , touchedSite: input.touchedSite
, sites: combinedSites input.sites , sites: combinedSites input.sites
, units: input.units , units: input.units
, leftSide: leftSide' , leftSide: leftSide'
} }
let res = runState loop' gameState let res = runState loop' gameState'
let state = snd res let state = snd res
let val = fst res let val = fst res
log $ val log $ val
loop state.numSites (toSiteInfo <$> state.sites) (Just state.leftSide) loop state.numSites (toSiteInfo <$> state.sites) (Just state)
where where
-- queenPos :: Array Minion -> { x :: Int, y :: Int } -- combine sites with siteInfo and old state
-- queenPos minions = queen minions
-- combine sites with siteInfo
combinedSites :: Array ProtoSite -> Array Site combinedSites :: Array ProtoSite -> Array Site
combinedSites sites = do combinedSites sites = do
protoS <- sites protoS <- sites
infoS <- siteInfo infoS <- siteInfo
guard $ protoS.id == infoS.id guard $ protoS.id == infoS.id
let prevSite = case gameState of
Just gs -> head $ filter (\s -> s.id == infoS.id) gs.sites
Nothing -> Nothing
let mineLvl = case prevSite of
Just site -> site.mineLvl
Nothing -> 0
pure { id: protoS.id pure { id: protoS.id
, gold: protoS.gold , gold: protoS.gold
, maxMineSize: protoS.maxMineSize , maxMineSize: protoS.maxMineSize
@@ -77,7 +78,7 @@ loop numSites siteInfo leftSide = do
, x: infoS.x , x: infoS.x
, y: infoS.y , y: infoS.y
, radius: infoS.radius , radius: infoS.radius
, mineLvl: -1 , mineLvl
} }
loop' :: State GameState String loop' :: State GameState String
@@ -90,18 +91,20 @@ buildAll :: State GameState String
buildAll = do buildAll = do
sites <- gets _.sites sites <- gets _.sites
units <- gets _.units units <- gets _.units
if (length $ friendlySites sites) == 0 let buildingsCnt = length $ friendlySites sites
then buildTowers let minesCnt = length $ friendlyMines sites
else if (length $ friendlyMines sites) < 3 if minesCnt < 3
then buildMines then buildMines
else if (length $ friendlySites sites) > 5 && (length $ friendlySites sites) < 11 else if buildingsCnt < 6
then buildTowers
else if (length $ friendlySites sites) <= 5
then case head $ nearFreeSites (queen units) sites of then case head $ nearFreeSites (queen units) sites of
Just site -> do Just site -> do
let typ = if hasKnightsBarrack sites then 1 else 0 let typ = if not $ hasArcherBarrack sites then 1
else if not $ hasKnightsBarrack sites then 0
else 2
pure $ build site typ pure $ build site typ
Nothing -> avoid Nothing -> avoid
else if buildingsCnt < 9
then buildTowers
else avoid else avoid
buildTowers :: State GameState String buildTowers :: State GameState String
@@ -117,9 +120,18 @@ buildMines = do
units <- gets _.units units <- gets _.units
sites <- gets _.sites sites <- gets _.sites
case head $ nearNonEmptyMines (queen units) sites of case head $ nearNonEmptyMines (queen units) sites of
Just site -> Just site -> do
touched <- gets _.touchedSite
if touched == -1 || touched /= site.id
then pure unit
else modify_ (\s -> s { sites = map (incMineLvl site.id) s.sites })
pure $ "BUILD " <> show site.id <> " MINE" pure $ "BUILD " <> show site.id <> " MINE"
Nothing -> avoid Nothing -> avoid
where
incMineLvl :: Int -> Site -> Site
incMineLvl sId site
| sId == site.id = site { mineLvl = site.mineLvl + 1 }
| otherwise = site
avoid :: State GameState String avoid :: State GameState String
avoid = do avoid = do
@@ -129,24 +141,30 @@ avoid = do
Just enemy -> do Just enemy -> do
let site = unsafePartial $ fromJust $ head $ let site = unsafePartial $ fromJust $ head $
sortBy (\s1 s2 -> compare (dist enemy s2) (dist enemy s1)) (friendlySites sites) sortBy (\s1 s2 -> compare (dist enemy s2) (dist enemy s1)) (friendlySites sites)
pure $ "MOVE " <> show site.x <> " " <> show site.y pure $ moveToPos site
Nothing -> pure $ "MOVE 0 0" Nothing -> pure $ "MOVE 0 0"
-- nearest non-queen enemy -- nearest non-queen enemy
nearestEnemy :: State GameState (Maybe Minion) nearestEnemy :: State GameState (Maybe Minion)
nearestEnemy = do nearestEnemy = do
units <- gets _.units units <- gets _.units
pure $ head $ filter (\u -> u.unitType /= -1 && isEnemy u) units pure $ head $ filter (\u -> isEnemy u) units
trainAll :: State GameState String trainAll :: State GameState String
trainAll = do trainAll = do
gold <- gets _.gold gold <- gets _.gold
sites <- gets _.sites sites <- gets _.sites
units <- gets _.units
-- choose <- randomInt 1 100 let ownArchers = ownMinions units
let choose = -1 -- TODO!!! let barrack =if gold > 140
let barrack = if gold > 140 then
then if choose < 42 then knightBarrack sites else archerBarrack sites if length ownArchers < 4 && length (enemyKnights units) /= 0
then archerBarrack sites
else if length (enemyTowers sites) > 2
then giantBarrack sites
else
knightBarrack sites
else [] else []
pure $ foldl siteToIds "TRAIN" barrack pure $ foldl siteToIds "TRAIN" barrack
where where
@@ -157,6 +175,15 @@ trainAll = do
archerBarrack sites = case head $ archerBarracks sites of archerBarrack sites = case head $ archerBarracks sites of
Just barrack -> [barrack] Just barrack -> [barrack]
Nothing -> [] Nothing -> []
giantBarrack sites = case head $ giantBarracks sites of
Just barrack -> [barrack]
Nothing -> []
build :: forall e. { id :: Int | e } -> Int -> String
build s typ = "BUILD " <> show s.id <> " BARRACKS-" <> t
where t | typ == 0 = "KNIGHT"
| typ == 1 = "ARCHER"
| otherwise = "GIANT"
queen :: Array Minion -> Minion queen :: Array Minion -> Minion
queen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 0) units queen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 0) units
@@ -164,6 +191,12 @@ queen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 &
enemyQueen :: Array Minion -> Minion enemyQueen :: Array Minion -> Minion
enemyQueen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 1) units enemyQueen units = unsafePartial $ fromJust $ head $ filter (\u -> u.unitType == -1 && u.owner == 1) units
ownMinions :: Array Minion -> Array Minion
ownMinions = filter isOwn
enemyKnights :: Array Minion -> Array Minion
enemyKnights = filter isEnemy <<< filter isKnight
freeSites :: Array Site -> Array Site freeSites :: Array Site -> Array Site
freeSites = filter (\s -> s.owner == -1) freeSites = filter (\s -> s.owner == -1)
@@ -173,24 +206,36 @@ friendlySites = filter (\s -> s.owner == 0)
friendlyMines :: Array Site -> Array Site friendlyMines :: Array Site -> Array Site
friendlyMines sites = filter (\s -> s.structureType == 0) $ friendlySites sites friendlyMines sites = filter (\s -> s.structureType == 0) $ friendlySites sites
enemyTowers :: Array Site -> Array Site
enemyTowers = filter isEnemy <<< filter isTower
nearSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site nearSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site
nearSites unit sites = sortBy (compareSiteDist unit) sites nearSites minion sites = sortBy (compareSiteDist minion) sites
nearFreeSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site nearFreeSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site
nearFreeSites unit sites = sortBy (compareSiteDist unit) (freeSites sites) nearFreeSites minion sites = sortBy (compareSiteDist minion) (freeSites sites)
nearNonEmptyMines :: forall x. { x :: Int, y :: Int | x } -> Array Site -> Array Site nearNonEmptyMines :: forall x. { x :: Int, y :: Int | x } -> Array Site -> Array Site
nearNonEmptyMines unit sites = filter (\s -> s.gold > 0 && s.mineLvl < 5 && s.owner /= 1) $ nearSites unit sites nearNonEmptyMines minion sites = filter (\s -> s.gold > 20 && s.mineLvl < 5 && s.owner /= 1) $ nearSites minion sites
hasKnightsBarrack :: Array Site -> Boolean hasKnightsBarrack :: Array Site -> Boolean
hasKnightsBarrack sites = any (\s -> s.param2 == 0) (friendlySites sites) hasKnightsBarrack sites = any (\s -> s.param2 == 0) (friendlySites sites)
hasArcherBarrack :: Array Site -> Boolean
hasArcherBarrack sites = any (\s -> s.param2 == 1) (friendlySites sites)
hasGiantsBarrack :: Array Site -> Boolean
hasGiantsBarrack sites = any (\s -> s.param2 == 2) (friendlySites sites)
knightBarracks :: Array Site -> Array Site knightBarracks :: Array Site -> Array Site
knightBarracks sites = filter (\s -> s.param2 == 0) (friendlySites sites) knightBarracks sites = filter (\s -> s.param2 == 0) (friendlySites sites)
archerBarracks :: Array Site -> Array Site archerBarracks :: Array Site -> Array Site
archerBarracks sites = filter (\s -> s.param2 == 1) (friendlySites sites) archerBarracks sites = filter (\s -> s.param2 == 1) (friendlySites sites)
giantBarracks :: Array Site -> Array Site
giantBarracks sites = filter (\s -> s.param2 == 2) (friendlySites sites)
toSiteInfo :: Site -> SiteInfo toSiteInfo :: Site -> SiteInfo
toSiteInfo s = { id: s.id, x: s.x, y: s.y, radius: s.radius } toSiteInfo s = { id: s.id, x: s.x, y: s.y, radius: s.radius }
@@ -200,10 +245,6 @@ compareSiteDist u s1 s2 = compare (dist s1 u) (dist s2 u)
corner :: Boolean -> { x :: Int, y :: Int } corner :: Boolean -> { x :: Int, y :: Int }
corner leftSide = if leftSide then { x: 0, y: 0 } else { x: 1920, y: 1000 } 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
where t = if typ == 0 then "KNIGHT" else "ARCHER"
isOwn :: forall a. { owner :: Int | a } -> Boolean isOwn :: forall a. { owner :: Int | a } -> Boolean
isOwn = owner 0 isOwn = owner 0
@@ -213,6 +254,18 @@ isEnemy = owner 1
owner :: Int -> forall a. { owner :: Int | a } -> Boolean owner :: Int -> forall a. { owner :: Int | a } -> Boolean
owner oId r = r.owner == oId owner oId r = r.owner == oId
isKnight :: Minion -> Boolean
isKnight minion = minion.unitType == 0
isArcher :: Minion -> Boolean
isArcher minion = minion.unitType == 1
isGiant :: Minion -> Boolean
isGiant minion = minion.unitType == 2
isTower :: forall a. { structureType :: Int | a } -> Boolean
isTower s = s.structureType == 1
barracks :: Array Site -> Array Site barracks :: Array Site -> Array Site
barracks sites = filter (\b -> b.structureType == 2) sites barracks sites = filter (\b -> b.structureType == 2) sites

View File

@@ -41,7 +41,7 @@ type Site =
, structureType :: Int -- -1 No structure, 0 Goldmine, 1 Tower, 2 Barracks , structureType :: Int -- -1 No structure, 0 Goldmine, 1 Tower, 2 Barracks
, owner :: Int -- -1 No structure, 0 friendly, 1 enemy , owner :: Int -- -1 No structure, 0 friendly, 1 enemy
, param1 :: Int -- -1 No structure, else turns till training , param1 :: Int -- -1 No structure, else turns till training
, param2 :: Int -- -1 No structure, barracks: 0 knight 1 archer , param2 :: Int -- -1 No structure, barracks: 0 knight 1 archer 2 giant
, mineLvl :: Int -- -1 whatever, otherwise curr. mine lvl , mineLvl :: Int -- -1 whatever, otherwise curr. mine lvl
} }
@@ -49,7 +49,7 @@ type Minion =
{ x :: Int { x :: Int
, y :: Int , y :: Int
, owner :: Int -- 0 = Friendly; 1 = Enemy , owner :: Int -- 0 = Friendly; 1 = Enemy
, unitType :: Int -- -1 = QUEEN, 0 = KNIGHT, 1 = ARCHER , unitType :: Int -- -1 = QUEEN, 0 = KNIGHT, 1 = ARCHER, 2 = GIANT
, health :: Int , health :: Int
} }