use State Monad for GameState
This commit is contained in:
@@ -2,17 +2,27 @@ module Main where
|
|||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Monad.State (State, gets, 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.JSDate (now, getTime)
|
||||||
import Data.Maybe (Maybe(..), fromJust)
|
import Data.Maybe (Maybe(..), fromJust)
|
||||||
|
import Data.Tuple (fst, snd)
|
||||||
import Effect (Effect)
|
import Effect (Effect)
|
||||||
import Effect.Console (error, log)
|
import Effect.Console (error, log)
|
||||||
import Effect.Random (randomInt)
|
|
||||||
import GameInput (Minion, Site, SiteInfo, ProtoSite, parseInitInput, parseInput)
|
import GameInput (Minion, Site, SiteInfo, ProtoSite, parseInitInput, parseInput)
|
||||||
import Lib (dist)
|
import Lib (dist)
|
||||||
import Partial.Unsafe (unsafePartial)
|
import Partial.Unsafe (unsafePartial)
|
||||||
|
|
||||||
|
type GameState =
|
||||||
|
{ gold :: Int
|
||||||
|
, numSites :: Int
|
||||||
|
, touchedSite :: Maybe Int
|
||||||
|
, sites :: Array Site
|
||||||
|
, units :: Array Minion
|
||||||
|
, leftSide :: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
main :: Effect Unit
|
main :: Effect Unit
|
||||||
main = do
|
main = do
|
||||||
initInput <- parseInitInput
|
initInput <- parseInitInput
|
||||||
@@ -31,8 +41,22 @@ loop numSites siteInfo leftSide = do
|
|||||||
let touchedSite = if input.touchedSite == -1
|
let touchedSite = if input.touchedSite == -1
|
||||||
then Nothing
|
then Nothing
|
||||||
else Just input.touchedSite
|
else Just input.touchedSite
|
||||||
|
|
||||||
|
let gameState =
|
||||||
|
{ gold: input.gold
|
||||||
|
, numSites
|
||||||
|
, touchedSite
|
||||||
|
, sites: combinedSites input.sites
|
||||||
|
, units: input.units
|
||||||
|
, leftSide: leftSide'
|
||||||
|
}
|
||||||
|
|
||||||
loop' numSites input.gold touchedSite (combinedSites input.sites) input.units leftSide'
|
let res = runState loop' gameState
|
||||||
|
let state = snd res
|
||||||
|
let val = fst res
|
||||||
|
log $ val
|
||||||
|
loop state.numSites (toSiteInfo <$> state.sites) (Just state.leftSide)
|
||||||
|
|
||||||
where
|
where
|
||||||
-- queenPos :: Array Minion -> { x :: Int, y :: Int }
|
-- queenPos :: Array Minion -> { x :: Int, y :: Int }
|
||||||
-- queenPos minions = queen minions
|
-- queenPos minions = queen minions
|
||||||
@@ -53,82 +77,86 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
loop' :: Int -> Int -> Maybe Int -> Array Site -> Array Minion -> Boolean -> Effect Unit
|
loop' :: State GameState String
|
||||||
loop' numSites gold touchedSite sites units leftSide = do
|
loop' = do
|
||||||
n <- now
|
ba <- buildAll
|
||||||
let t0 = getTime n
|
ta <- trainAll
|
||||||
|
pure $ ba <> "\n" <> ta
|
||||||
|
|
||||||
log buildAll
|
buildAll :: State GameState String
|
||||||
t <- trainAll gold sites
|
buildAll = do
|
||||||
log $ t
|
sites <- gets _.sites
|
||||||
|
units <- gets _.units
|
||||||
n' <- now
|
if (length $ friendlySites sites) == 0
|
||||||
let t1 = getTime n
|
then buildTowers
|
||||||
-- error $ show t0 <> "Execution time: " <> (show $ t1 - t0)
|
else if (length $ friendlyMines sites) < 3
|
||||||
loop numSites (toSiteInfo <$> sites) (Just leftSide)
|
then buildMines
|
||||||
where
|
else if (length $ friendlySites sites) > 5 && (length $ friendlySites sites) < 11
|
||||||
buildAll :: String
|
then buildTowers
|
||||||
buildAll =
|
else if (length $ friendlySites sites) <= 5
|
||||||
if (length $ friendlySites sites) == 0
|
then case head $ nearFreeSites (queen units) sites of
|
||||||
then buildTowers
|
Just site -> do
|
||||||
else if (length $ friendlyMines sites) < 3
|
let typ = if hasKnightsBarrack sites then 1 else 0
|
||||||
then buildMines
|
pure $ build site typ
|
||||||
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
|
Nothing -> avoid
|
||||||
|
else avoid
|
||||||
|
|
||||||
avoid :: String
|
buildTowers :: State GameState String
|
||||||
avoid = case nearestEnemy of
|
buildTowers = do
|
||||||
Just enemy -> "MOVE " <> show (site enemy).x <> " " <> show (site enemy).y
|
leftSide <- gets _.leftSide
|
||||||
Nothing -> "MOVE 0 0"
|
sites <- gets _.sites
|
||||||
where site enemy = unsafePartial $ fromJust $ head $
|
case head $ nearFreeSites (corner leftSide) sites of
|
||||||
sortBy (\s1 s2 -> compare (dist enemy s2) (dist enemy s1)) (friendlySites sites)
|
Just site -> pure $ "BUILD " <> show site.id <> " TOWER"
|
||||||
|
Nothing -> avoid
|
||||||
|
|
||||||
-- nearest non-queen enemy
|
buildMines :: State GameState String
|
||||||
nearestEnemy :: Maybe Minion
|
buildMines = do
|
||||||
nearestEnemy = head $ filter (\u -> u.unitType /= -1 && isEnemy u) units
|
units <- gets _.units
|
||||||
|
sites <- gets _.sites
|
||||||
|
case head $ nearNonEmptyMines (queen units) sites of
|
||||||
|
Just site ->
|
||||||
|
pure $ "BUILD " <> show site.id <> " MINE"
|
||||||
|
Nothing -> avoid
|
||||||
|
|
||||||
-- TODO: make pure
|
avoid :: State GameState String
|
||||||
trainAll :: Int -> Array Site -> Effect String
|
avoid = do
|
||||||
trainAll gold sites = do
|
sites <- gets _.sites
|
||||||
randBarrack <- randomBarrack
|
nEnemy <- nearestEnemy
|
||||||
choose <- randomInt 1 100
|
case nEnemy of
|
||||||
|
Just enemy -> do
|
||||||
|
let site = unsafePartial $ fromJust $ head $
|
||||||
|
sortBy (\s1 s2 -> compare (dist enemy s2) (dist enemy s1)) (friendlySites sites)
|
||||||
|
pure $ "MOVE " <> show site.x <> " " <> show site.y
|
||||||
|
Nothing -> pure $ "MOVE 0 0"
|
||||||
|
|
||||||
|
-- nearest non-queen enemy
|
||||||
|
nearestEnemy :: State GameState (Maybe Minion)
|
||||||
|
nearestEnemy = do
|
||||||
|
units <- gets _.units
|
||||||
|
pure $ head $ filter (\u -> u.unitType /= -1 && isEnemy u) units
|
||||||
|
|
||||||
|
trainAll :: State GameState String
|
||||||
|
trainAll = do
|
||||||
|
gold <- gets _.gold
|
||||||
|
sites <- gets _.sites
|
||||||
|
|
||||||
|
-- choose <- randomInt 1 100
|
||||||
|
let choose = -1 -- TODO!!!
|
||||||
let barrack = if gold > 140
|
let barrack = if gold > 140
|
||||||
then if choose < 42 then knightBarrack else archerBarrack
|
then if choose < 42 then knightBarrack sites else archerBarrack sites
|
||||||
else []
|
else []
|
||||||
pure $ foldl siteToIds "TRAIN" barrack
|
pure $ foldl siteToIds "TRAIN" barrack
|
||||||
where
|
where
|
||||||
siteToIds acc site = acc <> " " <> show site.id
|
siteToIds acc site = acc <> " " <> show site.id
|
||||||
knightBarrack = case head $ knightBarracks sites of
|
knightBarrack sites = case head $ knightBarracks sites of
|
||||||
Just barrack -> [barrack]
|
Just barrack -> [barrack]
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
archerBarrack = case head $ archerBarracks sites of
|
archerBarrack sites = case head $ archerBarracks sites of
|
||||||
Just barrack -> [barrack]
|
Just barrack -> [barrack]
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
randomBarrack = do
|
|
||||||
let ownBarracks = filter isOwn $ barracks sites
|
|
||||||
rand <- randomInt 0 $ length ownBarracks
|
|
||||||
case ownBarracks !! rand of
|
|
||||||
Just barrack -> pure [barrack]
|
|
||||||
Nothing -> pure []
|
|
||||||
|
|
||||||
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
|
||||||
@@ -145,11 +173,14 @@ 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
|
||||||
|
|
||||||
nearSites :: forall x. { x :: Int, y :: Int | x } -> Array Site -> Array Site
|
nearSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site
|
||||||
nearSites unit sites = sortBy (compareSiteDist unit) (freeSites sites)
|
nearSites unit sites = sortBy (compareSiteDist unit) sites
|
||||||
|
|
||||||
|
nearFreeSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site
|
||||||
|
nearFreeSites unit sites = sortBy (compareSiteDist unit) (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) $ nearSites unit sites
|
nearNonEmptyMines unit sites = filter (\s -> s.gold > 0 && s.mineLvl < 5 && s.owner /= 1) $ nearSites unit 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)
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ type SiteInfo =
|
|||||||
|
|
||||||
type ProtoSite =
|
type ProtoSite =
|
||||||
{ id :: Int
|
{ id :: Int
|
||||||
, gold :: Int -- The total number of gold remaining to be mined from this site (-1 if unknown)
|
, gold :: Int
|
||||||
, maxMineSize :: Int -- The maximum rate that a mine can extract gold from this site (-1 if unknown)
|
, maxMineSize :: Int
|
||||||
, structureType :: Int -- -1 No structure, 0 Goldmine, 1 Tower, 2 Barracks
|
, structureType :: Int
|
||||||
, owner :: Int -- -1 No structure, 0 friendly, 1 enemy
|
, owner :: Int
|
||||||
, param1 :: Int -- -1 No structure, else turns till training
|
, param1 :: Int
|
||||||
, param2 :: Int -- -1 No structure, barracks: 0 knight 1 archer
|
, param2 :: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Site =
|
type Site =
|
||||||
@@ -36,12 +36,13 @@ type Site =
|
|||||||
, x :: Int
|
, x :: Int
|
||||||
, y :: Int
|
, y :: Int
|
||||||
, radius :: Int
|
, radius :: Int
|
||||||
, gold :: Int
|
, gold :: Int -- The total number of gold remaining to be mined from this site (-1 if unknown)
|
||||||
, maxMineSize :: Int
|
, maxMineSize :: Int -- The maximum rate that a mine can extract gold from this site (-1 if unknown)
|
||||||
, structureType :: Int
|
, structureType :: Int -- -1 No structure, 0 Goldmine, 1 Tower, 2 Barracks
|
||||||
, owner :: Int
|
, owner :: Int -- -1 No structure, 0 friendly, 1 enemy
|
||||||
, param1 :: Int
|
, param1 :: Int -- -1 No structure, else turns till training
|
||||||
, param2 :: Int
|
, param2 :: Int -- -1 No structure, barracks: 0 knight 1 archer
|
||||||
|
, mineLvl :: Int -- -1 whatever, otherwise curr. mine lvl
|
||||||
}
|
}
|
||||||
|
|
||||||
type Minion =
|
type Minion =
|
||||||
|
|||||||
Reference in New Issue
Block a user