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 Control.Monad.State (State, gets, runState)
import Control.MonadZero (guard)
import Data.Array (any, filter, foldl, head, length, sortBy, (!!))
import Data.JSDate (now, getTime)
import Data.Array (any, filter, foldl, head, length, sortBy)
-- import Data.JSDate (now, getTime)
import Data.Maybe (Maybe(..), fromJust)
import Data.Tuple (fst, snd)
import Effect (Effect)
import Effect.Console (error, log)
import Effect.Random (randomInt)
import GameInput (Minion, Site, SiteInfo, ProtoSite, parseInitInput, parseInput)
import Lib (dist)
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 = do
initInput <- parseInitInput
@@ -32,7 +42,21 @@ loop numSites siteInfo leftSide = do
then Nothing
else Just input.touchedSite
loop' numSites input.gold touchedSite (combinedSites input.sites) input.units leftSide'
let gameState =
{ gold: input.gold
, numSites
, touchedSite
, sites: combinedSites input.sites
, units: input.units
, leftSide: 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
-- queenPos :: Array Minion -> { x :: Int, y :: Int }
-- queenPos minions = queen minions
@@ -53,24 +77,19 @@ loop numSites siteInfo leftSide = do
, x: infoS.x
, y: infoS.y
, radius: infoS.radius
, mineLvl: -1
}
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
loop' :: State GameState String
loop' = do
ba <- buildAll
ta <- trainAll
pure $ ba <> "\n" <> ta
log buildAll
t <- trainAll gold sites
log $ t
n' <- now
let t1 = getTime n
-- error $ show t0 <> "Execution time: " <> (show $ t1 - t0)
loop numSites (toSiteInfo <$> sites) (Just leftSide)
where
buildAll :: String
buildAll =
buildAll :: State GameState String
buildAll = do
sites <- gets _.sites
units <- gets _.units
if (length $ friendlySites sites) == 0
then buildTowers
else if (length $ friendlyMines sites) < 3
@@ -78,57 +97,66 @@ loop' numSites gold touchedSite sites units leftSide = do
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
then case head $ nearFreeSites (queen units) sites of
Just site -> do
let typ = if hasKnightsBarrack sites then 1 else 0
build site typ
pure $ build site typ
Nothing -> avoid
else avoid
buildTowers :: String
buildTowers = case head $ nearSites (corner leftSide) sites of
Just site -> "BUILD " <> show site.id <> " TOWER"
buildTowers :: State GameState String
buildTowers = do
leftSide <- gets _.leftSide
sites <- gets _.sites
case head $ nearFreeSites (corner leftSide) sites of
Just site -> pure $ "BUILD " <> show site.id <> " TOWER"
Nothing -> avoid
buildMines :: String
buildMines = case head $ nearNonEmptyMines (queen units) sites of
Just site -> "BUILD " <> show site.id <> " MINE"
buildMines :: State GameState String
buildMines = do
units <- gets _.units
sites <- gets _.sites
case head $ nearNonEmptyMines (queen units) sites of
Just site ->
pure $ "BUILD " <> show site.id <> " MINE"
Nothing -> avoid
avoid :: String
avoid = case nearestEnemy of
Just enemy -> "MOVE " <> show (site enemy).x <> " " <> show (site enemy).y
Nothing -> "MOVE 0 0"
where site enemy = unsafePartial $ fromJust $ head $
avoid :: State GameState String
avoid = do
sites <- gets _.sites
nEnemy <- nearestEnemy
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 :: Maybe Minion
nearestEnemy = head $ filter (\u -> u.unitType /= -1 && isEnemy u) units
nearestEnemy :: State GameState (Maybe Minion)
nearestEnemy = do
units <- gets _.units
pure $ head $ filter (\u -> u.unitType /= -1 && isEnemy u) units
-- TODO: make pure
trainAll :: Int -> Array Site -> Effect String
trainAll gold sites = do
randBarrack <- randomBarrack
choose <- randomInt 1 100
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
then if choose < 42 then knightBarrack else archerBarrack
then if choose < 42 then knightBarrack sites else archerBarrack sites
else []
pure $ foldl siteToIds "TRAIN" barrack
where
siteToIds acc site = acc <> " " <> show site.id
knightBarrack = case head $ knightBarracks sites of
knightBarrack sites = case head $ knightBarracks sites of
Just barrack -> [barrack]
Nothing -> []
archerBarrack = case head $ archerBarracks sites of
archerBarrack sites = case head $ archerBarracks sites of
Just barrack -> [barrack]
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 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 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)
nearSites :: forall a. { x :: Int, y :: Int | a } -> Array Site -> Array Site
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 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 sites = any (\s -> s.param2 == 0) (friendlySites sites)

View File

@@ -23,12 +23,12 @@ type SiteInfo =
type ProtoSite =
{ id :: Int
, 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
, gold :: Int
, maxMineSize :: Int
, structureType :: Int
, owner :: Int
, param1 :: Int
, param2 :: Int
}
type Site =
@@ -36,12 +36,13 @@ type Site =
, x :: Int
, y :: Int
, radius :: Int
, gold :: Int
, maxMineSize :: Int
, structureType :: Int
, owner :: Int
, param1 :: Int
, param2 :: Int
, 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
, mineLvl :: Int -- -1 whatever, otherwise curr. mine lvl
}
type Minion =