Initial commit
This commit is contained in:
46
aoe/costs.py
Normal file
46
aoe/costs.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Costs():
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.costs = {
|
||||||
|
Ages.DARK: (0, 0, 0, 0),
|
||||||
|
Ages.FEUDAL: (500, 0, 0, 0),
|
||||||
|
Ages.CASTLE: (800, 0, 200, 0),
|
||||||
|
Ages.IMPERIAL: (1000, 0, 800, 0),
|
||||||
|
Buildings.HOUSE: (0, 25, 0, 0),
|
||||||
|
Buildings.LUMBERCAMP: (0, 100, 0, 0),
|
||||||
|
Buildings.MILL: (0, 100, 0, 0),
|
||||||
|
Buildings.FARM: (0, 60, 0, 0),
|
||||||
|
Buildings.MININGCAMP: (0, 100, 0, 0),
|
||||||
|
Buildings.DOCK: (0, 150, 0, 0),
|
||||||
|
Buildings.BARRACKS: (0, 175, 0, 0),
|
||||||
|
Buildings.PALISADEWALL: (0, 3, 0, 0),
|
||||||
|
Buildings.PALISADEGATE: (0, 30, 0, 0),
|
||||||
|
Buildings.TOWNCENTER: (0, 275, 0, 100),
|
||||||
|
Buildings.BLACKSMITH: (0, 200, 0, 0),
|
||||||
|
Buildings.WATCHTOWER: (0, 35, 0, 125),
|
||||||
|
Buildings.STONEWALL: (0, 0, 0, 5),
|
||||||
|
Buildings.GATE: (0, 25, 0, 0),
|
||||||
|
Buildings.DONJON: (0, 50, 0, 175),
|
||||||
|
Buildings.FISHTRAP: (0, 100, 0, 0),
|
||||||
|
Buildings.MARKET: (0, 175, 0, 0),
|
||||||
|
Buildings.ARCHERYRANGE: (0, 175, 0, 0),
|
||||||
|
Buildings.STABLE: (0, 175, 0, 0),
|
||||||
|
Units.VILLAGER: (50, 0, 0, 0),
|
||||||
|
Units.TRADECART: (0, 100, 50, 0),
|
||||||
|
Units.SPEARMAN: (35, 25, 0, 0),
|
||||||
|
Units.MILITIA: (60, 0, 20, 0),
|
||||||
|
Units.SCOUT: (80, 0, 0, 0),
|
||||||
|
Units.KNIGHT: (60, 0, 75, 0),
|
||||||
|
Units.SKIRMISHER: (25, 35, 0, 0),
|
||||||
|
Units.ARCHER: (0, 25, 45, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#def update_research(self, research):
|
||||||
|
#case research:
|
||||||
|
#match Bowsaw:
|
||||||
96
aoe/dependencies.py
Normal file
96
aoe/dependencies.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BuildingDependencies():
|
||||||
|
def __init__(self):
|
||||||
|
self.dependencies = {
|
||||||
|
Buildings.HOUSE: [Ages.DARK],
|
||||||
|
Buildings.LUMBERCAMP: [Ages.DARK],
|
||||||
|
Buildings.MILL: [Ages.DARK],
|
||||||
|
Buildings.FARM: [Ages.DARK, Buildings.MILL],
|
||||||
|
Buildings.MININGCAMP: [Ages.DARK],
|
||||||
|
Buildings.DOCK: [Ages.DARK],
|
||||||
|
Buildings.BARRACKS: [Ages.DARK],
|
||||||
|
Buildings.PALISADEWALL: [Ages.DARK],
|
||||||
|
Buildings.PALISADEGATE: [Ages.DARK],
|
||||||
|
Buildings.TOWNCENTER: [Ages.DARK],
|
||||||
|
Buildings.BLACKSMITH: [Ages.FEUDAL],
|
||||||
|
Buildings.WATCHTOWER: [Ages.FEUDAL],
|
||||||
|
Buildings.STONEWALL: [Ages.FEUDAL],
|
||||||
|
Buildings.GATE: [Ages.FEUDAL],
|
||||||
|
Buildings.DONJON: [Ages.FEUDAL],
|
||||||
|
Buildings.FISHTRAP: [Ages.FEUDAL],
|
||||||
|
Buildings.MARKET: [Ages.FEUDAL, Buildings.MILL],
|
||||||
|
Buildings.ARCHERYRANGE: [Ages.FEUDAL, Buildings.BARRACKS],
|
||||||
|
Buildings.STABLE: [Ages.FEUDAL, Buildings.BARRACKS],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AgeDependencies():
|
||||||
|
def __init__(self):
|
||||||
|
self.dependencies = {
|
||||||
|
Ages.FEUDAL: {
|
||||||
|
Buildings.BARRACKS: 1,
|
||||||
|
Buildings.MILL: 1,
|
||||||
|
Buildings.LUMBERCAMP: 1,
|
||||||
|
Buildings.MININGCAMP: 1,
|
||||||
|
Buildings.DOCK: 1,
|
||||||
|
#Buildings.MULECART: 1,
|
||||||
|
#Buildings.FOLWARK: 1,
|
||||||
|
#Buildings.SETTLEMENT: 2,
|
||||||
|
},
|
||||||
|
Ages.CASTLE: {
|
||||||
|
Buildings.ARCHERYRANGE: 1,
|
||||||
|
Buildings.STABLE: 1,
|
||||||
|
Buildings.MARKET: 1,
|
||||||
|
Buildings.BLACKSMITH: 1,
|
||||||
|
#Buildings.SIEGEWORKSHOP: 1,
|
||||||
|
},
|
||||||
|
Ages.IMPERIAL: {
|
||||||
|
#Buildings.MONASTERY: 1,
|
||||||
|
#Buildings.FORTIFIEDCHURCH: 1,
|
||||||
|
#Buildings.UNIVERSITY: 1,
|
||||||
|
#Buildings.SIEGEWORKSHOP: 1,
|
||||||
|
#Buildings.CASTLE: 2,
|
||||||
|
#Buildings.KREPOST: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.dependencies_counts = {
|
||||||
|
Ages.FEUDAL: 2,
|
||||||
|
Ages.CASTLE: 2,
|
||||||
|
Ages.IMPERIAL: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ResearchDependencies():
|
||||||
|
def __init__(self):
|
||||||
|
self.dependencies = {
|
||||||
|
Research.DOUBLEBITAXE: [Ages.FEUDAL, Buildings.LUMBERCAMP],
|
||||||
|
Research.BOWSAW: [Ages.CASTLE, Buildings.LUMBERCAMP, Research.DOUBLEBITAXE],
|
||||||
|
Research.TWOMANSAW: [Ages.IMPERIAL, Buildings.LUMBERCAMP, Research.BOWSAW],
|
||||||
|
Research.HORSECOLLAR: [Ages.FEUDAL, Buildings.MILL],
|
||||||
|
Research.HEAVYPLOW: [Ages.CASTLE, Buildings.MILL, Research.HORSECOLLAR],
|
||||||
|
Research.CROPCYCLE: [Ages.IMPERIAL, Buildings.MILL, Research.HEAVYPLOW],
|
||||||
|
Research.GOLDMINING: [Ages.FEUDAL, Buildings.MININGCAMP],
|
||||||
|
Research.GOLDSHAFTMINING: [Ages.CASTLE, Buildings.MININGCAMP, Research.GOLDMINING],
|
||||||
|
Research.STONEMINING: [Ages.FEUDAL, Buildings.MININGCAMP],
|
||||||
|
Research.STONESHAFTMINING: [Ages.CASTLE, Buildings.MININGCAMP, Research.STONEMINING],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UnitDependencies():
|
||||||
|
def __init__(self):
|
||||||
|
self.dependencies = {
|
||||||
|
Units.VILLAGER: [Buildings.TOWNCENTER],
|
||||||
|
Units.TRADECART: [Buildings.MARKET],
|
||||||
|
Units.SPEARMAN: [Ages.FEUDAL, Buildings.BARRACKS],
|
||||||
|
Units.MILITIA: [Buildings.BARRACKS],
|
||||||
|
Units.SCOUT: [Ages.FEUDAL, Buildings.STABLE],
|
||||||
|
Units.KNIGHT: [Ages.CASTLE, Buildings.STABLE],
|
||||||
|
Units.SKIRMISHER: [Ages.FEUDAL, Buildings.ARCHERYRANGE],
|
||||||
|
Units.ARCHER: [Ages.FEUDAL, Buildings.ARCHERYRANGE],
|
||||||
|
}
|
||||||
37
aoe/exceptions.py
Normal file
37
aoe/exceptions.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
class CannotChangeThePastException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConservationOfVillagersException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GatherBuildingNotAvailableException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IllegalVillagerMoveException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MissingDependencyException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NotEnoughResourcesException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ProductionBuildingNotAvailableException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class YallGotHousedException(Exception):
|
||||||
|
pass
|
||||||
85
aoe/gameplay.py
Normal file
85
aoe/gameplay.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from aoe.exceptions import *
|
||||||
|
from aoe.types import *
|
||||||
|
from aoe.world import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Gameplay():
|
||||||
|
|
||||||
|
|
||||||
|
def print_status(self):
|
||||||
|
print(f'Current time: {self.world.current_time}')
|
||||||
|
print()
|
||||||
|
print(f'Civ: {self.world.civ}')
|
||||||
|
print(f'Age: {self.world.age}')
|
||||||
|
print(f'Population: {self.world.population}')
|
||||||
|
print()
|
||||||
|
print(f'Villagers: {self.world.villagers}')
|
||||||
|
print(f'Tasks: {[(t, c) for t, c in self.world.villager_tasks.items() if c != 0]}')
|
||||||
|
print()
|
||||||
|
print(f'Resources: {[int(r) for r in self.world.resources]}')
|
||||||
|
print()
|
||||||
|
print(f'Buildings: {self.world.buildings}')
|
||||||
|
print()
|
||||||
|
print(f'Research: {self.world.research}')
|
||||||
|
|
||||||
|
|
||||||
|
def build(self, action_time, building, villagers_from, villagers_to):
|
||||||
|
self._check_time(action_time)
|
||||||
|
self.actions[action_time].append((self.world.build,
|
||||||
|
(building,
|
||||||
|
villagers_from,
|
||||||
|
villagers_to,)))
|
||||||
|
|
||||||
|
|
||||||
|
def queue_villager(self, action_time, initial_task):
|
||||||
|
self._check_time(action_time)
|
||||||
|
self.actions[action_time].append((self.world.queue_villager,
|
||||||
|
(initial_task,)))
|
||||||
|
|
||||||
|
|
||||||
|
def assign_villager(self, action_time, old_task, new_task, count=1):
|
||||||
|
self._check_time(action_time)
|
||||||
|
self.actions[action_time].append((self.world.assign_villager,
|
||||||
|
(old_task, new_task, count,)))
|
||||||
|
|
||||||
|
|
||||||
|
def _check_time(self, action_time):
|
||||||
|
# TODO will it work if we do something at current time?
|
||||||
|
if self.world.current_time > action_time:
|
||||||
|
raise CannotChangeThePastException
|
||||||
|
|
||||||
|
|
||||||
|
def _get_next_action_time(self):
|
||||||
|
try:
|
||||||
|
next_action_time = min(self.actions.keys())
|
||||||
|
except ValueError:
|
||||||
|
next_action_time = self.end_time
|
||||||
|
|
||||||
|
return next_action_time
|
||||||
|
|
||||||
|
|
||||||
|
def _play_without_actions(self, end_time):
|
||||||
|
self.world.advance_until_time(end_time)
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_actions(self):
|
||||||
|
for func, args in self.actions[self.world.current_time]:
|
||||||
|
func(*args)
|
||||||
|
del self.actions[self.world.current_time]
|
||||||
|
|
||||||
|
|
||||||
|
def play_until(self, end_time):
|
||||||
|
self.end_time = end_time
|
||||||
|
while self.world.current_time < self.end_time:
|
||||||
|
next_action_time = self._get_next_action_time()
|
||||||
|
self._play_without_actions(min(next_action_time, self.end_time))
|
||||||
|
self._handle_actions()
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.world = World()
|
||||||
|
self.actions = defaultdict(list)
|
||||||
|
self.end_time = 0
|
||||||
59
aoe/gather.py
Normal file
59
aoe/gather.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Gather():
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.gather_rates = {
|
||||||
|
Tasks.BERRIES: {Resources.FOOD: 18.6/60},
|
||||||
|
Tasks.SHEEP: {Resources.FOOD: 19.8/60},
|
||||||
|
Tasks.BOAR: {Resources.FOOD: 24.6/60},
|
||||||
|
Tasks.HUNT: {Resources.FOOD: 24.6/60},
|
||||||
|
Tasks.FARM: {Resources.FOOD: 20/60},
|
||||||
|
Tasks.FISH_VILLAGER: {Resources.FOOD: 25.8/60},
|
||||||
|
Tasks.FISH_SHIP_SHORE: {Resources.FOOD: 16.8/60},
|
||||||
|
Tasks.FISH_SHIP_DEEP: {Resources.FOOD: 29.4/60},
|
||||||
|
Tasks.FISHTRAP: {Resources.FOOD: 21/60},
|
||||||
|
Tasks.WOOD: {Resources.WOOD: 23.4/60},
|
||||||
|
Tasks.GOLD: {Resources.GOLD: 22.8/60},
|
||||||
|
Tasks.TRADECART: {Resources.GOLD: 25.9/60},
|
||||||
|
Tasks.TRADECOG: {Resources.GOLD: 25.9/60},
|
||||||
|
Tasks.RELIC: {Resources.GOLD: 30/60},
|
||||||
|
Tasks.STONE: {Resources.STONE: 21.6/60},
|
||||||
|
Tasks.BUILD: {},
|
||||||
|
Tasks.IDLE: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.required_gather_buildings = {
|
||||||
|
Tasks.BERRIES: [set([Buildings.MILL]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.SHEEP: [set([Buildings.MILL]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.BOAR: [set([Buildings.MILL]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.HUNT: [set([Buildings.MILL]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.FARM: [set([Buildings.FARM, Buildings.MILL]),
|
||||||
|
set([Buildings.FARM, Buildings.TOWNCENTER])],
|
||||||
|
Tasks.FISH_VILLAGER: [set([Buildings.DOCK]),
|
||||||
|
set([Buildings.MILL]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.FISH_SHIP_SHORE: [set([Buildings.DOCK])],
|
||||||
|
Tasks.FISH_SHIP_DEEP: [set([Buildings.DOCK])],
|
||||||
|
Tasks.FISHTRAP: [set([Buildings.DOCK, Buildings.FISHTRAP])],
|
||||||
|
Tasks.WOOD: [set([Buildings.LUMBERCAMP]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.GOLD: [set([Buildings.MININGCAMP]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.TRADECART: [set([Buildings.MARKET])],
|
||||||
|
Tasks.TRADECOG: [set([Buildings.DOCK])],
|
||||||
|
#Tasks.RELIC: [set([Buildings.MONASTERY])],
|
||||||
|
Tasks.STONE: [set([Buildings.MININGCAMP]),
|
||||||
|
set([Buildings.TOWNCENTER])],
|
||||||
|
Tasks.BUILD: [],
|
||||||
|
Tasks.IDLE: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
self.improved_gather_buildings = {}
|
||||||
14
aoe/housing.py
Normal file
14
aoe/housing.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Housing():
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.space = defaultdict(int, {
|
||||||
|
Buildings.HOUSE: 5,
|
||||||
|
Buildings.TOWNCENTER: 5,
|
||||||
|
})
|
||||||
19
aoe/production.py
Normal file
19
aoe/production.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Production():
|
||||||
|
|
||||||
|
|
||||||
|
# assumes for now each unit can be built in only one type of building
|
||||||
|
def __init__(self):
|
||||||
|
self.building = {
|
||||||
|
Units.VILLAGER: Buildings.TOWNCENTER,
|
||||||
|
Units.TRADECART: Buildings.MARKET,
|
||||||
|
Units.SPEARMAN: Buildings.BARRACKS,
|
||||||
|
Units.MILITIA: Buildings.BARRACKS,
|
||||||
|
Units.SCOUT: Buildings.STABLE,
|
||||||
|
Units.KNIGHT: Buildings.STABLE,
|
||||||
|
Units.SKIRMISHER: Buildings.ARCHERYRANGE,
|
||||||
|
Units.ARCHER: Buildings.ARCHERYRANGE,
|
||||||
|
}
|
||||||
45
aoe/times.py
Normal file
45
aoe/times.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Times():
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.build_times = {
|
||||||
|
Ages.FEUDAL: 130,
|
||||||
|
Ages.CASTLE: 160,
|
||||||
|
Ages.IMPERIAL: 190,
|
||||||
|
Buildings.HOUSE: 25,
|
||||||
|
Buildings.LUMBERCAMP: 35,
|
||||||
|
Buildings.MILL: 35,
|
||||||
|
Buildings.FARM: 15,
|
||||||
|
Buildings.MININGCAMP: 35,
|
||||||
|
Buildings.DOCK: 35,
|
||||||
|
Buildings.BARRACKS: 50,
|
||||||
|
Buildings.PALISADEWALL: 7,
|
||||||
|
Buildings.PALISADEGATE: 30,
|
||||||
|
Buildings.TOWNCENTER: 150,
|
||||||
|
Buildings.BLACKSMITH: 40,
|
||||||
|
Buildings.WATCHTOWER: 80,
|
||||||
|
Buildings.STONEWALL: 10,
|
||||||
|
Buildings.GATE: 70,
|
||||||
|
Buildings.DONJON: 83,
|
||||||
|
Buildings.FISHTRAP: 40,
|
||||||
|
Buildings.MARKET: 60,
|
||||||
|
Buildings.ARCHERYRANGE: 50,
|
||||||
|
Buildings.STABLE: 50,
|
||||||
|
Units.VILLAGER: 25,
|
||||||
|
Units.TRADECART: 50,
|
||||||
|
Units.SPEARMAN: 22,
|
||||||
|
Units.MILITIA: 21,
|
||||||
|
Units.SCOUT: 30,
|
||||||
|
Units.KNIGHT: 30,
|
||||||
|
Units.SKIRMISHER: 22,
|
||||||
|
Units.ARCHER: 35,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.exist_duration = {
|
||||||
|
Buildings.FARM: 0,
|
||||||
|
Buildings.FISHTRAP: 0,
|
||||||
|
}
|
||||||
95
aoe/types.py
Normal file
95
aoe/types.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
from enum import Enum, IntEnum, auto
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Ages(IntEnum):
|
||||||
|
DARK = 0
|
||||||
|
FEUDAL = 1
|
||||||
|
CASTLE = 2
|
||||||
|
IMPERIAL = 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Buildings(Enum):
|
||||||
|
HOUSE = auto()
|
||||||
|
LUMBERCAMP = auto()
|
||||||
|
MILL = auto()
|
||||||
|
FARM = auto()
|
||||||
|
MININGCAMP = auto()
|
||||||
|
DOCK = auto()
|
||||||
|
BARRACKS = auto()
|
||||||
|
STABLE = auto()
|
||||||
|
ARCHERYRANGE = auto()
|
||||||
|
PALISADEWALL = auto()
|
||||||
|
PALISADEGATE = auto()
|
||||||
|
TOWNCENTER = auto()
|
||||||
|
BLACKSMITH = auto()
|
||||||
|
WATCHTOWER = auto()
|
||||||
|
STONEWALL = auto()
|
||||||
|
GATE = auto()
|
||||||
|
DONJON = auto()
|
||||||
|
FISHTRAP = auto()
|
||||||
|
MARKET = auto()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Tasks(Enum):
|
||||||
|
BERRIES = auto()
|
||||||
|
SHEEP = auto()
|
||||||
|
BOAR = auto()
|
||||||
|
HUNT = auto()
|
||||||
|
FARM = auto()
|
||||||
|
FISH_VILLAGER = auto()
|
||||||
|
FISH_SHIP_SHORE = auto()
|
||||||
|
FISH_SHIP_DEEP = auto()
|
||||||
|
FISHTRAP = auto()
|
||||||
|
WOOD = auto()
|
||||||
|
GOLD = auto()
|
||||||
|
RELIC = auto()
|
||||||
|
TRADECART = auto()
|
||||||
|
TRADECOG = auto()
|
||||||
|
STONE = auto()
|
||||||
|
BUILD = auto()
|
||||||
|
IDLE = auto()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Research(Enum):
|
||||||
|
DOUBLEBITAXE = auto()
|
||||||
|
BOWSAW = auto()
|
||||||
|
TWOMANSAW = auto()
|
||||||
|
HORSECOLLAR = auto()
|
||||||
|
HEAVYPLOW = auto()
|
||||||
|
CROPCYCLE = auto()
|
||||||
|
GOLDMINING = auto()
|
||||||
|
GOLDSHAFTMINING = auto()
|
||||||
|
STONEMINING = auto()
|
||||||
|
STONESHAFTMINING = auto()
|
||||||
|
'''
|
||||||
|
BLACKSMITH = 11
|
||||||
|
WATCHTOWER = 12
|
||||||
|
STONEWALL = 13
|
||||||
|
GATE = 14
|
||||||
|
DONJON = 15
|
||||||
|
FISHTRAP = 16
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Resources(IntEnum):
|
||||||
|
FOOD = 0
|
||||||
|
WOOD = 1
|
||||||
|
GOLD = 2
|
||||||
|
STONE = 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Units(Enum):
|
||||||
|
VILLAGER = auto()
|
||||||
|
TRADECART = auto()
|
||||||
|
SPEARMAN = auto()
|
||||||
|
MILITIA = auto()
|
||||||
|
SCOUT = auto()
|
||||||
|
KNIGHT = auto()
|
||||||
|
SKIRMISHER = auto()
|
||||||
|
ARCHER = auto()
|
||||||
425
aoe/world.py
Normal file
425
aoe/world.py
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
from collections import defaultdict, Counter
|
||||||
|
|
||||||
|
from aoe.exceptions import *
|
||||||
|
from aoe.costs import *
|
||||||
|
from aoe.dependencies import *
|
||||||
|
from aoe.gather import *
|
||||||
|
from aoe.housing import *
|
||||||
|
from aoe.production import *
|
||||||
|
from aoe.times import *
|
||||||
|
from aoe.types import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class World():
|
||||||
|
|
||||||
|
|
||||||
|
# Creating a unit is split into three stages:
|
||||||
|
# queuing the unit
|
||||||
|
# building the unit
|
||||||
|
# adding the unit to the world
|
||||||
|
#
|
||||||
|
# Before queuing a thing, we:
|
||||||
|
# check its dependencies (research, age, etc.)
|
||||||
|
# pay its cost
|
||||||
|
# Once a thing is queued, it waits to be built.
|
||||||
|
#
|
||||||
|
# Before building a thing, we
|
||||||
|
# check for housing capacity
|
||||||
|
# check for available production buildings
|
||||||
|
# Once a thing is building, it waits to be added.
|
||||||
|
#
|
||||||
|
# We assume for now that anything that has built can be added.
|
||||||
|
#
|
||||||
|
# Note: queuing a unit is itself an event that gets added to the queue,
|
||||||
|
# and, if executed successfully, it adds another event to the queue that
|
||||||
|
# actually adds the unit to the world
|
||||||
|
|
||||||
|
# TODO farms, reseeding, etc.
|
||||||
|
# _check_resource_building, _use_resource_building, _free_resource_building
|
||||||
|
# TODO sheep should die like farms
|
||||||
|
# TODO need some warning, penalty, etc. if villager is assigned to task
|
||||||
|
# without appropriate building (e.g., mining with no camp)
|
||||||
|
# TODO need efficiency penalties on, e.g., lumber camp
|
||||||
|
# too many vils/camp decreases rate
|
||||||
|
# "stale" (old) camp decreases rate
|
||||||
|
# TODO research
|
||||||
|
|
||||||
|
|
||||||
|
MAX_WORLD_TIME = 3*60*60 # 3 hours
|
||||||
|
|
||||||
|
|
||||||
|
def _pay_cost(self, purchase):
|
||||||
|
for i in range(len(self.resources)):
|
||||||
|
if self.resources[i] < self.costs.costs[purchase][i]:
|
||||||
|
raise NotEnoughResourcesException
|
||||||
|
for i in range(len(self.resources)):
|
||||||
|
self.resources[i] -= self.costs.costs[purchase][i]
|
||||||
|
|
||||||
|
|
||||||
|
def _check_research_dependencies(self, research):
|
||||||
|
self._check_dependencies(self.research_deps.dependencies[research])
|
||||||
|
|
||||||
|
|
||||||
|
def _check_building_dependencies(self, building):
|
||||||
|
self._check_dependencies(self.building_deps.dependencies[building])
|
||||||
|
|
||||||
|
|
||||||
|
def _check_unit_dependencies(self, unit):
|
||||||
|
self._check_dependencies(self.unit_deps.dependencies[unit])
|
||||||
|
|
||||||
|
|
||||||
|
def _check_dependencies(self, dependencies):
|
||||||
|
for dep in dependencies:
|
||||||
|
if dep not in self.buildings and \
|
||||||
|
dep not in self.research and \
|
||||||
|
not self._is_sufficient_age(dep):
|
||||||
|
raise MissingDependencyException
|
||||||
|
|
||||||
|
|
||||||
|
def _is_sufficient_age(self, age):
|
||||||
|
if type(age) == Ages and self.age >= age:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _check_age_dependencies(self, age):
|
||||||
|
total_buildings = 0
|
||||||
|
for building in self.age_deps.dependencies[age].keys():
|
||||||
|
if self.has_building(building):
|
||||||
|
total_buildings += self.age_deps.dependencies[age][building]
|
||||||
|
if total_buildings < self.age_deps.dependencies_counts[age]:
|
||||||
|
raise MissingDependencyException
|
||||||
|
|
||||||
|
|
||||||
|
def buy_research(self, research):
|
||||||
|
self._check_research_dependencies(research)
|
||||||
|
self._pay_cost(research)
|
||||||
|
self._update_costs(research)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_costs(self, research):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _update_gather_rate(self, task):
|
||||||
|
pass # TODO
|
||||||
|
#num_farms = self.buildings.count(Buildings.FARM)
|
||||||
|
#num_workers = self.villager_tasks[task]
|
||||||
|
#villager_room =
|
||||||
|
|
||||||
|
|
||||||
|
def assign_villager(self, previous_task, new_task, count=1):
|
||||||
|
if self.villager_tasks[previous_task] < count:
|
||||||
|
raise IllegalVillagerMoveException
|
||||||
|
|
||||||
|
self._check_gather(new_task, count)
|
||||||
|
|
||||||
|
self.villager_tasks[previous_task] -= count
|
||||||
|
self.villager_tasks[new_task] += count
|
||||||
|
|
||||||
|
self._update_gather_rate(new_task)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_villager_move(self, villagers_from_count):
|
||||||
|
for task, count in villagers_from_count.items():
|
||||||
|
if self.villager_tasks[task] < count:
|
||||||
|
raise IllegalVillagerMoveException
|
||||||
|
|
||||||
|
|
||||||
|
def _move_villagers_to_build(self, villagers_from_count):
|
||||||
|
for task, count in villagers_from_count.items():
|
||||||
|
self.villager_tasks[task] -= count
|
||||||
|
self.villager_tasks[Tasks.BUILD] += count
|
||||||
|
|
||||||
|
|
||||||
|
def build(self, building, villagers_from, villagers_to):
|
||||||
|
if type(villagers_from) == Tasks:
|
||||||
|
villagers_from = [villagers_from]
|
||||||
|
if type(villagers_to) == Tasks:
|
||||||
|
villagers_to = [villagers_to]
|
||||||
|
|
||||||
|
if len(villagers_from) != len(villagers_to):
|
||||||
|
raise ConservationOfVillagersException
|
||||||
|
|
||||||
|
villagers_from_count = Counter(villagers_from)
|
||||||
|
|
||||||
|
self._check_villager_move(villagers_from_count)
|
||||||
|
self._check_building_dependencies(building)
|
||||||
|
|
||||||
|
self._pay_cost(building)
|
||||||
|
self._move_villagers_to_build(villagers_from_count)
|
||||||
|
|
||||||
|
villagers_to_count = Counter(villagers_to)
|
||||||
|
|
||||||
|
num_villagers_building = len(villagers_from)
|
||||||
|
solo_build_time = self.build_times.build_times[building]
|
||||||
|
build_time = 3*solo_build_time/(num_villagers_building + 2)
|
||||||
|
update_time = self.current_time + build_time
|
||||||
|
|
||||||
|
self.events[update_time].append((self._add_building,
|
||||||
|
(building, villagers_to_count,)))
|
||||||
|
|
||||||
|
|
||||||
|
def _add_building(self, building, villagers_to):
|
||||||
|
|
||||||
|
self.buildings.append(building)
|
||||||
|
self._free_production_building(building)
|
||||||
|
|
||||||
|
for task, count in villagers_to.items():
|
||||||
|
self.villager_tasks[Tasks.BUILD] -= count
|
||||||
|
self.villager_tasks[task] += count
|
||||||
|
|
||||||
|
if building == Buildings.HOUSE:
|
||||||
|
self._free_housing()
|
||||||
|
if building == Buildings.FARM:
|
||||||
|
# assumes a farm is worked nonstop after being built
|
||||||
|
update_time = self.current_time + times.exist_duration[Buildings.FARM]
|
||||||
|
self.events[update_time].append((self._remove_farm, ()))
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_farm(self):
|
||||||
|
self.buildings.remove(Buildings.FARM)
|
||||||
|
|
||||||
|
num_farms = self.buildings.count(Buildings.FARM)
|
||||||
|
if self.villager_tasks[Tasks.FARM] > num_farms:
|
||||||
|
self.villager_tasks[Tasks.FARM] -= 1
|
||||||
|
self.villager_tasks[Tasks.IDLE] += 1
|
||||||
|
|
||||||
|
|
||||||
|
def advance_by_time(self, duration):
|
||||||
|
self._run_clock_until(self.time + duration)
|
||||||
|
|
||||||
|
|
||||||
|
def advance_until_time(self, end_time):
|
||||||
|
self._run_clock_until_time(end_time)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_next_event_time(self):
|
||||||
|
try:
|
||||||
|
next_event_time = min(self.events.keys())
|
||||||
|
except ValueError:
|
||||||
|
next_event_time = World.MAX_WORLD_TIME
|
||||||
|
|
||||||
|
return next_event_time
|
||||||
|
|
||||||
|
|
||||||
|
def _run_clock_without_events(self, end_time):
|
||||||
|
self._gather_resources_for_duration(end_time - self.current_time)
|
||||||
|
self.current_time = end_time
|
||||||
|
|
||||||
|
|
||||||
|
def _run_clock_until_time(self, end_time):
|
||||||
|
while self.current_time < end_time:
|
||||||
|
next_event_time = self._get_next_event_time()
|
||||||
|
self._run_clock_without_events(min(next_event_time, end_time))
|
||||||
|
self._handle_events()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_population_space(self):
|
||||||
|
return sum([self.housing.space[building] for building in self.buildings])
|
||||||
|
|
||||||
|
|
||||||
|
def _get_effective_population(self):
|
||||||
|
return self.population + self.units_being_built
|
||||||
|
|
||||||
|
|
||||||
|
def _free_housing(self):
|
||||||
|
pop_space = self._get_population_space()
|
||||||
|
original_queue_len = len(self.production_queue)
|
||||||
|
|
||||||
|
# Assumes each unit costs 1 pop
|
||||||
|
new_builds = []
|
||||||
|
i = 0
|
||||||
|
while (self._get_effective_population() < pop_space and
|
||||||
|
i < original_queue_len):
|
||||||
|
|
||||||
|
unit = self.production_queue[i]
|
||||||
|
if self._check_production(unit):
|
||||||
|
self._build_unit(unit)
|
||||||
|
new_builds.append(i)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self.production_queue = [self.production_queue[i]
|
||||||
|
for i in range(original_queue_len)
|
||||||
|
if i not in new_builds]
|
||||||
|
|
||||||
|
|
||||||
|
def _free_production_building_of_unit(self, unit):
|
||||||
|
building = self.production.building[unit]
|
||||||
|
self._free_production_building(building)
|
||||||
|
|
||||||
|
|
||||||
|
def _free_production_building(self, building):
|
||||||
|
self.production_available[building] += 1
|
||||||
|
|
||||||
|
# Start the next queued unit if one exists
|
||||||
|
ready_index = None
|
||||||
|
ready_unit = None
|
||||||
|
for i in range(len(self.production_queue)):
|
||||||
|
unit = self.production_queue[i]
|
||||||
|
if self.production.building[unit] == building:
|
||||||
|
ready_index = i
|
||||||
|
ready_unit = unit
|
||||||
|
break
|
||||||
|
|
||||||
|
if ready_index:
|
||||||
|
self.production_queue.remove(i)
|
||||||
|
self._build_unit(unit)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_housing(self, unit):
|
||||||
|
# assumes for now that all units cost 1 pop
|
||||||
|
pop_space = sum([self.housing.space[building] for building in self.buildings])
|
||||||
|
if self._get_effective_population() >= pop_space:
|
||||||
|
raise YallGotHousedException
|
||||||
|
|
||||||
|
|
||||||
|
def _check_gather(self, task, count=1):
|
||||||
|
required = self.gather.required_gather_buildings[task]
|
||||||
|
have = set(self.buildings)
|
||||||
|
|
||||||
|
match = any(map(have.issuperset, required))
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
raise GatherBuildingNotAvailableException
|
||||||
|
|
||||||
|
if task == task.FARM:
|
||||||
|
num_farms = self.buildings.count(Buildings.FARM)
|
||||||
|
if self.villager_tasks[task] + count > num_farms:
|
||||||
|
raise GatherBuildingNotAvailableException
|
||||||
|
|
||||||
|
|
||||||
|
def _check_production(self, unit):
|
||||||
|
building = self.production.building[unit]
|
||||||
|
if self.production_available[building] < 1:
|
||||||
|
raise ProductionBuildingNotAvailableException
|
||||||
|
|
||||||
|
|
||||||
|
def _use_housing_and_production(self, unit):
|
||||||
|
self._check_housing(unit)
|
||||||
|
self._check_production(unit)
|
||||||
|
|
||||||
|
building = self.production.building[unit]
|
||||||
|
|
||||||
|
self.production_available[building] -= 1
|
||||||
|
self.units_being_built += 1
|
||||||
|
|
||||||
|
|
||||||
|
def _kill_unit(self, unit, task=None):
|
||||||
|
if unit == Units.VILLAGER and not task:
|
||||||
|
raise IllegalVillagerMoveException
|
||||||
|
|
||||||
|
# assumes population of 1 for every unit
|
||||||
|
self.population -= 1
|
||||||
|
|
||||||
|
if unit == Units.VILLAGER:
|
||||||
|
self.villagers -= 1
|
||||||
|
self.villager_tasks[task] -= 1
|
||||||
|
self._update_gather_rate(task)
|
||||||
|
|
||||||
|
self._free_housing()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_unit(self, unit, initial_task):
|
||||||
|
# assumes population of 1 for every unit
|
||||||
|
self.units_being_built -= 1
|
||||||
|
self.population += 1
|
||||||
|
self._free_production_building_of_unit(unit)
|
||||||
|
|
||||||
|
if unit == Units.VILLAGER:
|
||||||
|
self.villagers += 1
|
||||||
|
self.villager_tasks[Tasks.IDLE] += 1
|
||||||
|
self.assign_villager(Tasks.IDLE, initial_task)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_unit(self, unit, initial_task):
|
||||||
|
try:
|
||||||
|
self._use_housing_and_production(unit)
|
||||||
|
except ProductionBuildingNotAvailableException:
|
||||||
|
self.production_queue.append(unit)
|
||||||
|
except YallGotHousedException:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
update_time = self.current_time + self.build_times.build_times[unit]
|
||||||
|
self.events[update_time].append((self._add_unit, (unit, initial_task,)))
|
||||||
|
|
||||||
|
|
||||||
|
def queue_unit(self, unit, initial_task=None):
|
||||||
|
self._check_unit_dependencies(unit)
|
||||||
|
self._pay_cost(unit)
|
||||||
|
|
||||||
|
self._build_unit(unit, initial_task)
|
||||||
|
|
||||||
|
|
||||||
|
def queue_villager(self, initial_task):
|
||||||
|
self.queue_unit(Units.VILLAGER, initial_task)
|
||||||
|
|
||||||
|
|
||||||
|
def _gather_resources_for_duration(self, duration):
|
||||||
|
for task, count in self.villager_tasks.items():
|
||||||
|
gather_rates = self.gather.gather_rates[task]
|
||||||
|
for resource, rate in gather_rates.items():
|
||||||
|
self.resources[resource] += count*rate*duration
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_events(self):
|
||||||
|
for func, args in self.events[self.current_time]:
|
||||||
|
func(*args)
|
||||||
|
del self.events[self.current_time]
|
||||||
|
|
||||||
|
|
||||||
|
def _update_age(self, new_age):
|
||||||
|
self.age = new_age
|
||||||
|
|
||||||
|
|
||||||
|
def increase_age(self):
|
||||||
|
if self.age == Ages.IMPERIAL:
|
||||||
|
raise Exception("no.")
|
||||||
|
|
||||||
|
new_age = self.age + 1
|
||||||
|
|
||||||
|
self._check_age_dependencies(new_age)
|
||||||
|
self._pay_cost(new_age)
|
||||||
|
|
||||||
|
update_time = self.current_time + self.build_times.build_times[new_age]
|
||||||
|
self.events[update_time].append((self._update_age, (new_age,)))
|
||||||
|
|
||||||
|
|
||||||
|
def _deal_with_civ(self):
|
||||||
|
pass # deal with any civ-specific changes
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, civ=None):
|
||||||
|
self.civ = civ
|
||||||
|
|
||||||
|
self.age = Ages.DARK
|
||||||
|
self.villagers = 3
|
||||||
|
self.population = 4
|
||||||
|
self.resources = [200, 200, 100, 200]
|
||||||
|
self.research = []
|
||||||
|
self.buildings = [Buildings.TOWNCENTER]
|
||||||
|
|
||||||
|
self.villager_tasks = {t: 0 for t in Tasks}
|
||||||
|
self.villager_tasks[Tasks.IDLE] = self.villagers
|
||||||
|
|
||||||
|
self.units_being_built = 0
|
||||||
|
self.production_available = {b: 0 for b in Buildings}
|
||||||
|
self.production_available[Buildings.TOWNCENTER] = 1
|
||||||
|
self.production_queue = []
|
||||||
|
|
||||||
|
self.current_time = 0
|
||||||
|
self.events = defaultdict(list)
|
||||||
|
|
||||||
|
self.build_times = Times()
|
||||||
|
self.costs = Costs()
|
||||||
|
self.building_deps = BuildingDependencies()
|
||||||
|
self.age_deps = AgeDependencies()
|
||||||
|
self.research_deps = ResearchDependencies()
|
||||||
|
self.unit_deps = UnitDependencies()
|
||||||
|
self.housing = Housing()
|
||||||
|
self.production = Production()
|
||||||
|
self.gather = Gather()
|
||||||
|
|
||||||
|
self._deal_with_civ()
|
||||||
77
description.md
Normal file
77
description.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
* Get aoe info
|
||||||
|
* API for it somewhere?
|
||||||
|
* Write simple code
|
||||||
|
* Write tests
|
||||||
|
* Write GUI
|
||||||
|
|
||||||
|
Deal with getting housed
|
||||||
|
|
||||||
|
Put villagers on resource sources
|
||||||
|
* what to do about farms?
|
||||||
|
* one villager per
|
||||||
|
* run out occaisionally and must be rebuilt
|
||||||
|
|
||||||
|
# Outline
|
||||||
|
|
||||||
|
Timeline of villager production
|
||||||
|
* should be able to duplicate them easily and make modifications to compare
|
||||||
|
* throws an error if a task is impossible and a warning if you get idle time
|
||||||
|
|
||||||
|
# Design
|
||||||
|
|
||||||
|
Was originally doing object-centric:
|
||||||
|
each thing contained all information about itself.
|
||||||
|
For example,
|
||||||
|
a Blacksmith was a thing (class) that had fields representing
|
||||||
|
a cost, a time to build, an Age requirement, etc.
|
||||||
|
However, this led to circular dependencies.
|
||||||
|
For example, a Blacksmith required Feudal Age,
|
||||||
|
and Castle Age optionally required a Blacksmith.
|
||||||
|
This made it difficult in Python to have Ages and Buildings in separate files.
|
||||||
|
They had to import each other,
|
||||||
|
which is not possible in any simple way in Python.
|
||||||
|
|
||||||
|
Instead, data is stored in an attribute-centric way.
|
||||||
|
Costs is a class,
|
||||||
|
and it holds costs for Blacksmith, Feudal Age, Militia, and everything else.
|
||||||
|
Dependencies is another class,
|
||||||
|
and Times (i.e., build or unit production times) is yet another,
|
||||||
|
and others are added as needed.
|
||||||
|
|
||||||
|
If you think of the information needed as residing in a table
|
||||||
|
with the rows representing entities (House, Villager, etc.)
|
||||||
|
and the columns representing attributes (Cost, Build Time, etc.),
|
||||||
|
we are storing the data by column rather than by row.
|
||||||
|
As an aside,
|
||||||
|
this is similar to what I've seen described as the fundamental difference
|
||||||
|
between object-oriented (row-based) and functional (column-based) programming,
|
||||||
|
though I'm still using classes and objects.
|
||||||
|
|
||||||
|
|
||||||
|
# Scratch
|
||||||
|
|
||||||
|
```
|
||||||
|
def create(self):
|
||||||
|
self.
|
||||||
|
|
||||||
|
# Assume entries sorted by time
|
||||||
|
# Not necessarily from user, but from queue_villager and move_villager
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Class Task():
|
||||||
|
requires = {"wood": "lumber camp"}
|
||||||
|
# what if more villagers than farms?
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Assumptions
|
||||||
|
|
||||||
|
* Fixed gather rate for each resource
|
||||||
|
* what about food -- hunt vs farm?
|
||||||
|
* At least one TC always exists
|
||||||
|
* Using no civ bonuses for now
|
||||||
60
runner.py
Normal file
60
runner.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from aoe.exceptions import *
|
||||||
|
from aoe.types import *
|
||||||
|
from aoe.gameplay import *
|
||||||
|
#import ipdb; breakpoint()
|
||||||
|
|
||||||
|
|
||||||
|
# for testing
|
||||||
|
# TODO need to go back through old tasks when new housing is added
|
||||||
|
# TODO wherever we test against Age, it's probably wrong
|
||||||
|
# TODO add building to production_available when finished
|
||||||
|
# TODO need some warning, penalty, etc. if villager is assigned to task
|
||||||
|
# without appropriate building (e.g., mining with no camp)
|
||||||
|
# TODO building time assumes just 1 vil is working for now
|
||||||
|
|
||||||
|
|
||||||
|
# Set up world
|
||||||
|
game = Gameplay()
|
||||||
|
|
||||||
|
# should work even before we do anything
|
||||||
|
#game.print_status()
|
||||||
|
|
||||||
|
game.queue_villager(5, Tasks.WOOD) # first number is time in seconds
|
||||||
|
|
||||||
|
# should generate exception
|
||||||
|
#game.assign_villager(40, Tasks.SHEEP, Tasks.WOOD)
|
||||||
|
|
||||||
|
game.assign_villager(0, Tasks.IDLE, Tasks.SHEEP)
|
||||||
|
game.assign_villager(2, Tasks.IDLE, Tasks.SHEEP, 2)
|
||||||
|
|
||||||
|
# should generate exception
|
||||||
|
#game.assign_villager(0, Tasks.IDLE, Tasks.SHEEP)
|
||||||
|
|
||||||
|
# should generate exception
|
||||||
|
#game.build(45, Buildings.STABLE, Tasks.WOOD, Tasks.IDLE)
|
||||||
|
|
||||||
|
#game.build(45, Buildings.LUMBERCAMP, Tasks.WOOD, Tasks.IDLE)
|
||||||
|
game.build(45, Buildings.LUMBERCAMP, Tasks.WOOD, Tasks.WOOD)
|
||||||
|
|
||||||
|
game.queue_villager(45, Tasks.WOOD)
|
||||||
|
game.queue_villager(45, Tasks.WOOD)
|
||||||
|
|
||||||
|
game.build(100, Buildings.HOUSE, [Tasks.SHEEP, Tasks.WOOD], [Tasks.IDLE]*2)
|
||||||
|
#game.assign_villager(Build.Stable(), "food", wait_until_finished=True)
|
||||||
|
|
||||||
|
'''
|
||||||
|
game.queue_unit(s1, Scout)
|
||||||
|
'''
|
||||||
|
|
||||||
|
# should generate exception
|
||||||
|
#game.build(200, Buildings.FARM, [Tasks.SHEEP], [Tasks.WOOD])
|
||||||
|
|
||||||
|
game.build(200, Buildings.MILL, [Tasks.SHEEP], [Tasks.WOOD])
|
||||||
|
game.build(250, Buildings.FARM, [Tasks.SHEEP, Tasks.WOOD], [Tasks.IDLE])
|
||||||
|
|
||||||
|
# Evaluate game
|
||||||
|
game.play_until(800)
|
||||||
|
game.print_status()
|
||||||
|
|
||||||
|
# should generate exception
|
||||||
|
#game.assign_villager(0, Tasks.SHEEP, Tasks.WOOD)
|
||||||
Reference in New Issue
Block a user