TorchCraftAI
A bot for machine learning research on StarCraft: Brood War
controller.h
1 /*
2  * Copyright (c) 2017-present, Facebook, Inc.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #pragma once
9 
10 #include "cherrypi.h"
11 #include "module.h"
12 #include "state.h"
13 #include "task.h"
14 #include "utils.h"
15 
16 namespace cherrypi {
17 
18 /**
19  * Base class for controllers.
20  *
21  * A Controller is a mix between a Task and a Module: it has a sense of unit
22  * ownership and contains state similar to Task, and has a step() function which
23  * is used to post UPCs to Blackboard similar to Module. It is tailored to
24  * control of individual units: units can be added or removed, and per-unit UPC
25  * posting is made easy.
26  *
27  * For using a controller, Module and Task objects still required. Module create
28  * controller instances and call step(). Tasks take care of player-wide unit
29  * allocation via Blackboard and provide source UPCs for each unit being
30  * controller.
31  *
32  * See Controller and SharedController for usage exampels of two common
33  * controller patterns.
34  */
36  public:
37  ControllerBase(Module* module);
38  virtual ~ControllerBase() = default;
39 
40  /// Add a unit to this controller.
41  /// This is usually called whenever a new Task for a controller is being
42  /// cretaed.
43  /// Re-implement this function if you need to update internal data structures
44  /// when gaining control of units but make sure to also call the base class
45  /// method.
46  virtual void addUnit(State* state, Unit* unit, UpcId id);
47 
48  /// Remove a unit from this controller.
49  /// This is usually called from Task::update() to remove units that were
50  /// assigned to other Tasks, or for which keepUnit() returns false.
51  /// Re-implement this function if you need to update internal data structures
52  /// when gaining control of units but make sure to also call the base class
53  /// method.
54  virtual void removeUnit(State* state, Unit* unit, UpcId id);
55 
56  /// Decide whether to keep a unit.
57  /// By default, this returns false for dead and non-allied units.
58  virtual bool keepUnit(State* state, Unit* unit) const;
59 
60  /// Advance controller state and produce UPCs.
61  /// This is intended to be called from Module::step() of the instantiating
62  /// module.
63  /// The default implementation does nothing.
64  virtual void step(State* state);
65 
66  /// Checks if the controller is controlling the given unit via the given UPC
67  /// ID.
68  /// Tasks are required to call this function before calling removeUnit() when
69  /// removing units from controllers.
70  bool isControllingUnitWith(Unit* unit, UpcId id) const;
71 
72  /// A name for this Controller, for debugging purposes
73  virtual const char* getName() const {
74  return "Controller";
75  };
76 
77  protected:
78  /// Posts scheduled UPCs to the Blackboard.
79  /// UPCs can be scheduled by addUpc().
80  void postUpcs(State* state);
81 
82  /// Schedules an action (as a UPC) for the given unit which will be posted
83  /// after doStep().
84  template <typename... Args>
85  void addUpc(Unit* unit, Args&&... args) {
86  if (upcs_.find(unit) != upcs_.end()) {
87  LOG(WARNING) << "Duplicate UPC for unit " << utils::unitString(unit);
88  }
89  if (units_.find(unit) == units_.end()) {
90  LOG(WARNING) << "Not controlling unit " << utils::unitString(unit);
91  return;
92  }
93 
94  // TODO
95  auto sourceId = units_[unit];
96  if (sourceId == kInvalidUpcId) {
97  return;
98  }
99 
100  auto upc = utils::makeSharpUPC(unit, std::forward<Args>(args)...);
101  upcs_.emplace(
102  std::piecewise_construct,
103  std::forward_as_tuple(unit),
104  std::forward_as_tuple(sourceId, std::move(upc)));
105  }
106 
107  protected:
109  std::unordered_map<Unit*, UpcId> units_;
110  std::unordered_map<Unit*, std::pair<UpcId, std::shared_ptr<UPCTuple>>> upcs_;
111 };
112 
113 /**
114  * Base class for single-task controllers.
115  *
116  * This class models a 1:1 relationship with an accompanying Task. Units are
117  * added to the controller when the respective task object (ControllerTask) is
118  * created.
119  *
120  * Controller provides two additional virtual function that can be
121  * re-implemented by sub-classes: didSucceed() and didFail(). These will be used
122  * by the accompanying Task object to update its status. If your controller
123  * returns true for one of these functions, the accompanying Task will end and
124  * the controller is free to be disposed of and should not be stepped through
125  * any more.
126  *
127  * A typical Module::step() function with Controller objects might look
128  * similar to this:
129  *
130 ```
131 void MyModule::step(State* state) {
132  // For the current relevant UPCs on the Blackboard
133  for (auto& it : relevantUpcs()) {
134  auto upcId = it.first;
135  auto& upc = it.second;
136  board->consumeUPC(upcId, this);
137 
138  // Select units from upc.unit
139  auto units = sampleUnits(upc);
140 
141  // Create a new task with a new controller instance
142  auto controller = std::make_shared<MyController>(this);
143  auto task = std::make_shared<ControllerTask>(upcId, units, controller);
144  board->postTask(task, this, true);
145  }
146 
147  // Update active controllers
148  for (auto& task : state->board()->tasksOfModule(this)) {
149  auto ctask = std::static_pointer_cast<ControllerTask>(task);
150  auto controller = ctask->controller();
151  controller->step(state);
152  }
153 }
154 ```
155  */
156 class Controller : public ControllerBase {
157  public:
158  Controller(Module* module);
159  virtual ~Controller() = default;
160 
161  /// Implement this to return whether your custom Controller did succeed in its
162  /// mission (if applicable) and can be disposed.
163  /// By default, this returns false.
164  virtual bool didSucceed() const {
165  return false;
166  }
167 
168  /// Implement this to return whether your custom Controller did fail in its
169  /// mission (if applicable) and can be disposed.
170  /// By default, a Controller fails if it does not control any units.
171  virtual bool didFail() const;
172 
173  /// Set the UPC ID of the corresponding task.
174  void setUpcId(UpcId id);
175 
176  protected:
178 };
179 
180 /**
181  * Base class for Controllers shared between multiple tasks.
182  *
183  * A common pattern is the control of multiple units in a centralized fashion.
184  * Since unit allocation is globally managed via Task objects which have a 1:1
185  * relation to their respective UPCs, this requires handling multiple Task
186  * objects.
187  *
188  * With SharedController and SharedControllerTask, this pattern can be
189  * implemented quite easily by inheriting from SharedController. Typically, the
190  * resulting code in Module::step() will look similar to this:
191  *
192 ```
193 void MyModule::step(State* state) {
194  auto controller = SharedController::globalInstance<MyController>(state, this);
195 
196  // For the current relevant UPCs on the Blackboard
197  for (auto& it : relevantUpcs()) {
198  auto upcId = it.first;
199  auto& upc = it.second;
200  board->consumeUPC(upcId, this);
201 
202  // Select units from upc.unit
203  auto units = sampleUnits(upc);
204 
205  // Create a new task and register it in the controller instance
206  auto task = std::make_shared<SharedControllerTask>(upcId, units,
207  controller);
208  board->postTask(task, this, true);
209  }
210 
211  controller->step(state);
212 }
213 ```
214  *
215  */
217  public:
219  virtual ~SharedController() = default;
220 
221  /// Retrieves the global instance of a shared controller.
222  /// Shared controllers can be stored in the Blackboard. This function will
223  /// create the requested controller object if necessary (the Blackboard key
224  /// is "controller_<module name>/<controller name>"
225  template <typename T>
226  static std::shared_ptr<T> globalInstance(
227  State* state,
228  Module* module,
229  std::string name = std::string()) {
230  auto board = state->board();
231  auto key = std::string("controller_") + module->name() + "/" + name;
232  auto controller =
233  board->get<std::shared_ptr<SharedController>>(key, nullptr);
234  if (controller == nullptr) {
235  controller = std::make_shared<T>(module);
236  board->post(key, controller);
237  }
238  return std::dynamic_pointer_cast<T>(controller);
239  }
240 };
241 
242 /**
243  * Generic Task for Controller.
244  *
245  * Please see Controller for further details and a usage example.
246  */
247 class ControllerTask : public Task {
248  public:
250  UpcId upcId,
251  std::unordered_set<Unit*> units,
252  State* state,
253  std::shared_ptr<Controller> controller);
254  virtual ~ControllerTask() = default;
255 
256  virtual void update(State* state) override;
257  virtual void cancel(State* state) override;
258 
259  std::shared_ptr<Controller> controller() const;
260  virtual const char* getName() const override {
261  return controller()->getName();
262  };
263 
264  protected:
265  std::shared_ptr<Controller> controller_;
266 };
267 
268 /**
269  * Generic Task for SharedController.
270  *
271  * Please see Controller for further details and a usage example.
272  *
273  * This task will enter failure state if there are no more units allocated to
274  * it. In contrast to Controller, SharedController does not report any success
275  * or failure status and the sole responsibility of this task is to keep track
276  * of unit allocations. If there are no more units, this task's job is done.
277  */
278 class SharedControllerTask : public Task {
279  public:
281  UpcId upcId,
282  std::unordered_set<Unit*> units,
283  State* state,
284  std::shared_ptr<SharedController> controller);
285  virtual ~SharedControllerTask() = default;
286 
287  virtual void update(State* state) override;
288  virtual void cancel(State* state) override;
289 
290  std::shared_ptr<SharedController> controller() const;
291 
292  protected:
293  std::shared_ptr<SharedController> controller_;
294 };
295 
296 } // namespace cherrypi
Game state.
Definition: state.h:42
virtual void addUnit(State *state, Unit *unit, UpcId id)
Add a unit to this controller.
Definition: controller.cpp:18
virtual bool didSucceed() const
Implement this to return whether your custom Controller did succeed in its mission (if applicable) an...
Definition: controller.h:164
void postUpcs(State *state)
Posts scheduled UPCs to the Blackboard.
Definition: controller.cpp:56
The primary way for modules to publish their activity.
Definition: task.h:50
virtual void step(State *state)
Advance controller state and produce UPCs.
Definition: controller.cpp:54
ControllerBase(Module *module)
Definition: controller.cpp:16
std::string name()
Definition: module.cpp:41
virtual bool keepUnit(State *state, Unit *unit) const
Decide whether to keep a unit.
Definition: controller.cpp:42
virtual const char * getName() const
A name for this Controller, for debugging purposes.
Definition: controller.h:73
Represents a unit in the game.
Definition: unitsinfo.h:35
std::unordered_map< Unit *, std::pair< UpcId, std::shared_ptr< UPCTuple > > > upcs_
Definition: controller.h:110
static std::shared_ptr< T > globalInstance(State *state, Module *module, std::string name=std::string())
Retrieves the global instance of a shared controller.
Definition: controller.h:226
bool isControllingUnitWith(Unit *unit, UpcId id) const
Checks if the controller is controlling the given unit via the given UPC ID.
Definition: controller.cpp:65
virtual ~ControllerBase()=default
Generic Task for SharedController.
Definition: controller.h:278
std::unordered_map< Unit *, UpcId > units_
Definition: controller.h:109
Generic Task for Controller.
Definition: controller.h:247
std::shared_ptr< SharedController > controller_
Definition: controller.h:293
Blackboard * board() const
Definition: state.h:99
Base class for single-task controllers.
Definition: controller.h:156
std::string unitString(Unit const *unit)
Definition: debugging.h:65
auto makeSharpUPC(Unit *u, Command c)
Definition: upcs.h:17
virtual const char * getName() const override
A name for this task, for debugging purposes.
Definition: controller.h:260
UpcId upcId_
Definition: controller.h:177
Main namespace for bot-related code.
Definition: areainfo.cpp:17
Base class for controllers.
Definition: controller.h:35
virtual void removeUnit(State *state, Unit *unit, UpcId id)
Remove a unit from this controller.
Definition: controller.cpp:22
int UpcId
Definition: basetypes.h:23
std::shared_ptr< Controller > controller_
Definition: controller.h:262
Interface for bot modules.
Definition: module.h:30
void addUpc(Unit *unit, Args &&...args)
Schedules an action (as a UPC) for the given unit which will be posted after doStep().
Definition: controller.h:85
Base class for Controllers shared between multiple tasks.
Definition: controller.h:216
UpcId constexpr kInvalidUpcId
Definition: basetypes.h:25
Module * module_
Definition: controller.h:108