&
Screeps
PureScript
- składnia podobna do Haskella
- podzbiór rozszerzeń GHC, np. RankNTypes,
FunctionalDependencies - kompiluje się do JavaScriptu
- strict evaluation
Screeps w JavaScript
function(creep, myRooms) {
var homeRoom = Game.rooms[creep.memory.homeRoom];
if (creep.memory.fullcheck === undefined) creep.memory.fullcheck = false;
if (creep.memory.fullcheck && creep.carry.energy == 0) {
creep.memory.fullcheck = false;
}
if (!creep.memory.fullcheck && creep.carry.energy == creep.carryCapacity) {
creep.memory.fullcheck = true;
}
if (creep.memory.fullcheck) {
if (creep.upgradeController(homeRoom.controller) == ERR_NOT_IN_RANGE) {
creep.moveTo(homeRoom.controller);
}
if (creep.pos.roomName != 'W3N58' && creep.pos.getRangeTo(homeRoom.controller) > 2) {
creep.moveTo(homeRoom.controller);
}
} else {
var myRoom = _.filterWithCache('Game.myRooms.' + creep.memory.homeRoom, Game.myRooms, {
filter: {
name: creep.memory.homeRoom
}
})[0];
var energy = creep.pos.findInRange(FIND_DROPPED_RESOURCES, 3);
if (energy.length != 0 && energy[0].resourceType == RESOURCE_ENERGY && energy[0].amount > 20) {
if (creep.pickup(energy[0]) != OK) {
creep.moveTo(energy[0]);
}
} else if (myRoom != undefined && myRoom.controllerLink != undefined && myRoom.controllerLink.energy > 0) {
if (creep.withdraw(myRoom.controllerLink, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(myRoom.controllerLink);
}
} else {
var storage = homeRoom.storage;
if (storage != undefined && storage.store[RESOURCE_ENERGY] > 50000) {
if (creep.withdraw(storage, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(storage);
} else {}
} else {
if (creep.memory.assignedNode != undefined) {
var myMiner = util.findMyMiner(creep);
util.collectEnergyFromMiner(creep, myMiner);
} else {
energy = creep.pos.findClosestByRange(FIND_DROPPED_RESOURCES);
if (creep.pickup(energy) != OK) {
creep.moveTo(energy);
}
}
}
}
}
}
Screeps w PureScript
repeat do
harvestEnergy
transferEnergyToBase
Wolne monady
data Free f a
= Pure a
| Free (f (Free f a))
instance monadFree :: Functor f => Monad (Free f) where
pure = Pure
Pure a >>= f = f a
Free m >>= f = Free ((>>= f) <$> m)
wrap :: forall f a. f (Free f a) -> Free f a
wrap = Free
resume :: forall f a. Functor f => Free f a -> Either (f (Free f a)) a
resume (Pure x) = Right x
resume (Free f) = Left f
liftF :: forall f a. Functor f => f a -> Free f a
liftF f = wrap $ pure <$> f
Plan
data PlanF a
= HarvestEnergy a
| TransferEnergyToBase a
| Repeat (Plan Unit) a
instance functorPlanF :: Functor PlanF where
map k f = case f of
HarvestEnergy f' -> HarvestEnergy $ k f'
TransferEnergyToBase f' -> TransferEnergyToBase $ k f'
Repeat x f' -> Repeat x $ k f'
type Plan a = Free PlanF a
harvestEnergy :: Plan Unit
harvestEnergy = liftF $ HarvestEnergy unit
transferEnergyToBase :: Plan Unit
transferEnergyToBase = liftF $ TransferEnergyToBase unit
repeat :: Plan Unit -> Plan Unit
repeat block = liftF $ Repeat block unit
Flow
plan
memory
world
encodeJson
decodeJson
executePlan
Wykonywanie
executePlan :: Creep -> Plan Unit -> Exec (Plan Unit)
executePlan creep = resume >>> case _ of
Left action -> peel action
Right _ -> pure $ pure unit
where
peel action = case action of
HarvestEnergy next ->
if amtCarrying creep resource_energy < carryCapacity creep
then do
maybeSource <- findClosest find_sources
case maybeSource of
Just source -> do
harvestSource creep source `orMoveTo` source
stay
Nothing -> throwError $ ErrorMessage "source not found"
else transition next
-- ...
where
stay = pure $ wrap action
transition = executePlan creep
-- ...
Kombinatory
- repeat
...
Repeat
executePlan creep = -- ...
where
peel action = case action of
Repeat block -> do
block' <- executePlan creep block
pure do
block'
plan
-- ...
where
plan = wrap action
-- ...
Demo
repeat do
harvestEnergy
transferEnergyToBase
Kombinatory
- repeat
...
- interrupt
+
Interrupt
executePlan creep = -- ...
where
peel action = case action of
Interrupt interruptee interrupter next -> do
interrupter' <- executePlan creep interrupter
if isPure interrupter'
then do
interruptee' <- executePlan creep interruptee
if isPure interruptee'
then transition next
else pure do
liftF $ Interrupt interruptee' interrupter unit
next
else pure do
interrupter'
plan
-- ...
where
plan = wrap action
isPure = isRight <<< resume
-- ...
Demo
repeat do
harvestEnergy
transferEnergyToBase `interrupt` build
Kombinatory
- repeat
...
- interrupt
- interleave
+
+
Interleave
plan1 `interleave` plan2
do
threadId <- fork plan2
plan1
kill threadId
do
0 <- fork plan2
plan1
kill 0
Interleave
executePlan creep = -- ...
where
peel action = case action of
Fork thread threadId next -> do
isRunning <- hasThread threadId
unless isRunning $
addThread threadId thread
transition next
Join threadId next -> do
isRunning <- hasThread threadId
if isRunning
then stay
else transition next
Kill threadId next -> do
isRunning <- hasThread threadId
when isRunning $
removeThread threadId
transition next
-- ...
where
addThread threadId thread = -- ...
hasThread threadId = -- ...
removeThread threadId = -- ...
-- ...
Demo
(repeat do
harvestEnergy
transferEnergyToBase `interrupt` build
)
`interleave` repeat fight
Co dalej?
- więcej akcji
- optymalizacja
- plany dla spawnów
- metaplany dla zespołów creepów
PureScript & Screeps
By Piotr Kozakowski
PureScript & Screeps
- 1,020