use State Monad for GameState

This commit is contained in:
weiss
2020-04-10 21:18:06 +02:00
parent 149e4173d0
commit 11bafb6f60
2 changed files with 111 additions and 79 deletions

View File

@@ -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)

View File

@@ -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 =