TorchCraftAI
A bot for machine learning research on StarCraft: Brood War
gamemechanics.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 <algorithm>
11 #include <cfloat>
12 #include <vector>
13 
14 #include <torchcraft/client.h>
15 
16 #include "cherrypi.h"
17 #include "debugging.h"
18 #include "filter.h"
19 #include "state.h"
20 #include "unitsinfo.h"
21 #include "utils.h"
22 #include <math.h>
23 
24 namespace cherrypi {
25 namespace utils {
26 
27 /// Approximation of Euclidian distance
28 /// This is the same approximation that StarCraft's engine uses
29 /// and thus should be more accurate than true Euclidian distance
30 inline unsigned int disthelper(unsigned int dx, unsigned int dy) {
31  // Helper takes and returns pixels
32  if (dx < dy) {
33  std::swap(dx, dy);
34  }
35  if (dx / 4u < dy) {
36  dx = dx - dx / 16u + dy * 3u / 8u - dx / 64u + dy * 3u / 256u;
37  }
38  return dx;
39 }
40 
41 /// Pixel distance
42 inline unsigned int pxdistance(int px1, int py1, int px2, int py2) {
43  unsigned int dx = std::abs(px1 - px2);
44  unsigned int dy = std::abs(py1 - py2);
45  return disthelper(dx, dy);
46 }
47 
48 /// Walktile distance
49 inline float distance(int x1, int y1, int x2, int y2) {
50  unsigned int dx = std::abs(x1 - x2) * unsigned(tc::BW::XYPixelsPerWalktile);
51  unsigned int dy = std::abs(y1 - y2) * unsigned(tc::BW::XYPixelsPerWalktile);
52  return float(disthelper(dx, dy)) / tc::BW::XYPixelsPerWalktile;
53 }
54 
55 /// Walktile distance
56 inline float distance(Unit const* a, Unit const* b) {
57  return distance(a->x, a->y, b->x, b->y);
58 }
59 
60 /// Walktile distance
61 inline float distance(Position const& a, Position const& b) {
62  return distance(a.x, a.y, b.x, b.y);
63 }
64 
65 /// Walktile distance
66 inline float distance(Unit const* a, Position const& b) {
67  return distance(a->x, a->y, b.x, b.y);
68 }
69 
70 /// Walktile distance
71 inline float distance(Position const& a, Unit const* b) {
72  return distance(a.x, a.y, b->x, b->y);
73 }
74 
75 /// Distance between two bounding boxes, in pixels.
76 /// Brood War uses bounding boxes for both collisions and range checks
77 inline int pxDistanceBB(
78  int xminA,
79  int yminA,
80  int xmaxA,
81  int ymaxA,
82  int xminB,
83  int yminB,
84  int xmaxB,
85  int ymaxB) {
86  if (xmaxB < xminA) { // To the left
87  if (ymaxB < yminA) { // Fully above
88  return pxdistance(xmaxB, ymaxB, xminA, yminA);
89  } else if (yminB > ymaxA) { // Fully below
90  return pxdistance(xmaxB, yminB, xminA, ymaxA);
91  } else { // Adjecent
92  return xminA - xmaxB;
93  }
94  } else if (xminB > xmaxA) { // To the right
95  if (ymaxB < yminA) { // Fully above
96  return pxdistance(xminB, ymaxB, xmaxA, yminA);
97  } else if (yminB > ymaxA) { // Fully below
98  return pxdistance(xminB, yminB, xmaxA, ymaxA);
99  } else { // Adjecent
100  return xminB - xmaxA;
101  }
102  } else if (ymaxB < yminA) { // Above
103  return yminA - ymaxB;
104  } else if (yminB > ymaxA) { // Below
105  return yminB - ymaxA;
106  }
107 
108  return 0;
109 }
110 
111 inline int pxDistanceBB(Unit const* a, Unit const* b) {
112  return pxDistanceBB(
113  a->unit.pixel_x - a->type->dimensionLeft,
114  a->unit.pixel_y - a->type->dimensionUp,
115  a->unit.pixel_x + a->type->dimensionRight,
116  a->unit.pixel_y + a->type->dimensionDown,
117  b->unit.pixel_x - b->type->dimensionLeft,
118  b->unit.pixel_y - b->type->dimensionUp,
119  b->unit.pixel_x + b->type->dimensionRight,
120  b->unit.pixel_y + b->type->dimensionDown);
121 }
122 
123 inline float distanceBB(Unit const* a, Unit const* b) {
124  return float(pxDistanceBB(a, b)) / tc::BW::XYPixelsPerWalktile;
125 }
126 
127 // Bounding box distance given that unit a is in position a and unit b is in
128 // position b.
129 template <typename T>
130 inline float distanceBB(
131  Unit const* a,
132  Vec2T<T> const& pa,
133  Unit const* b,
134  Vec2T<T> const& pb) {
135  return float(pxDistanceBB(
136  int(pa.x * tc::BW::XYPixelsPerWalktile - a->type->dimensionLeft),
137  int(pa.y * tc::BW::XYPixelsPerWalktile - a->type->dimensionUp),
138  int(pa.x * tc::BW::XYPixelsPerWalktile + a->type->dimensionRight),
139  int(pa.y * tc::BW::XYPixelsPerWalktile + a->type->dimensionDown),
140  int(pb.x * tc::BW::XYPixelsPerWalktile - b->type->dimensionLeft),
141  int(pb.y * tc::BW::XYPixelsPerWalktile - b->type->dimensionUp),
142  int(pb.x * tc::BW::XYPixelsPerWalktile + b->type->dimensionRight),
143  int(pb.y * tc::BW::XYPixelsPerWalktile +
144  b->type->dimensionDown))) /
145  tc::BW::XYPixelsPerWalktile;
146 }
147 
148 /// Predict the position of a unit some frames into the future
149 inline Position predictPosition(Unit const* unit, double frames) {
150  return Position(
151  static_cast<int>(unit->x + frames * unit->unit.velocityX),
152  static_cast<int>(unit->y + frames * unit->unit.velocityY));
153 }
154 
155 // Get movement towards position p, rotated by angle in degrees.
156 // If not exact, we click past it so we maintain flyer acceleration
157 // Positive angle rotates from the top right to the bottom left corner,
158 // since the y axis points down.
160  int ux,
161  int uy,
162  int px,
163  int py,
164  int mx,
165  int my,
166  double angle,
167  bool exact) {
168  auto fdirX = px - ux;
169  auto fdirY = py - uy;
170  if (fdirX == 0 && fdirY == 0) {
171  return Position(px, py);
172  }
173  auto rad = angle * kDegPerRad;
174  auto c = std::cos(rad);
175  auto s = std::sin(rad);
176  auto dirX = fdirX * c - fdirY * s;
177  auto dirY = fdirX * s + fdirY * c;
178  if (!exact && dirX * dirX + dirY * dirY < 10) {
179  // Approximate, I don't want to compute the magnitude
180  // Clicks at least 10 walktiles ahead
181  auto div = std::abs(dirX == 0 ? dirY : dirX);
182  dirX = dirX / div * 10;
183  dirY = dirY / div * 10;
184  }
185  return Position(
186  utils::clamp(ux + (int)dirX, 0, mx - 1),
187  utils::clamp(uy + (int)dirY, 0, my - 1));
188 }
190  const State* const state,
191  const Unit* const u,
192  Position p,
193  double angle = 0,
194  bool exact = true) {
195  return getMovePosHelper(
196  u->x,
197  u->y,
198  p.x,
199  p.y,
200  state->mapWidth(),
201  state->mapHeight(),
202  angle,
203  exact);
204 }
206  const State* const state,
207  const Unit* const u,
208  const Unit* const p,
209  double angle = 0,
210  bool exact = true) {
211  return getMovePosHelper(
212  u->x,
213  u->y,
214  p->x,
215  p->y,
216  state->mapWidth(),
217  state->mapHeight(),
218  angle,
219  exact);
220 }
221 
223  const State* const state,
224  int const x,
225  int const y,
226  bool strict = false) {
227  auto cx = utils::clamp(x, 1, state->mapWidth() - 1);
228  auto cy = utils::clamp(y, 1, state->mapHeight() - 1);
229  if (strict && (cx != x || cy != y)) {
230  return Position(-1, -1);
231  }
232  return Position(cx, cy);
233 }
234 
236  const State* const state,
237  Position const& pos,
238  bool strict = false) {
239  return clampPositionToMap(state, pos.x, pos.y, strict);
240 }
241 
242 inline bool isWorker(tc::Unit const& unit) {
243  auto ut = tc::BW::UnitType::_from_integral_nothrow(unit.type);
244  if (ut) {
245  return tc::BW::isWorker(*ut);
246  }
247  return false;
248 }
249 
250 inline bool isBuilding(tc::Unit const& unit) {
251  auto ut = tc::BW::UnitType::_from_integral_nothrow(unit.type);
252  if (ut) {
253  return tc::BW::isBuilding(*ut);
254  }
255  return false;
256 }
257 
258 inline std::vector<tc::Unit> getWorkers(tc::State* state) {
260  state->units[state->player_id],
261  static_cast<bool (*)(tc::BW::UnitType)>(&tc::BW::isWorker));
262 }
263 
264 inline std::vector<tc::Unit> getMineralFields(tc::State* state) {
266  state->units[state->neutral_id], tc::BW::isMineralField);
267 }
268 
269 // x,y in walktiles
270 inline bool isBuildable(tc::State* state, int x, int y) {
271  if (x < 0 || y < 0 || x >= state->map_size[0] || y >= state->map_size[1]) {
272  return false;
273  }
274  return state->buildable_data[y * state->map_size[0] + x];
275 }
276 
277 inline bool prerequisitesReady(State* state, const BuildType* buildType) {
278  auto& unitsInfo = state->unitsInfo();
279  for (auto* prereq : buildType->prerequisites) {
280  if (prereq->isUnit()) {
281  bool hasPrereq = !unitsInfo.myCompletedUnitsOfType(prereq).empty();
282  if (!hasPrereq) {
283  if (prereq == buildtypes::Zerg_Spire) {
284  hasPrereq =
285  !unitsInfo.myUnitsOfType(buildtypes::Zerg_Greater_Spire).empty();
286  } else if (prereq == buildtypes::Zerg_Hatchery) {
287  hasPrereq =
288  !unitsInfo.myCompletedUnitsOfType(buildtypes::Zerg_Lair).empty();
289  if (!hasPrereq) {
290  hasPrereq = !unitsInfo.myUnitsOfType(buildtypes::Zerg_Hive).empty();
291  }
292  } else if (prereq == buildtypes::Zerg_Lair) {
293  hasPrereq = !unitsInfo.myUnitsOfType(buildtypes::Zerg_Hive).empty();
294  }
295  }
296  if (!hasPrereq) {
297  return false;
298  }
299  } else if (prereq->isUpgrade()) {
300  if (state->getUpgradeLevel(prereq) < prereq->level) {
301  return false;
302  }
303  } else if (prereq->isTech()) {
304  if (!state->hasResearched(prereq)) {
305  return false;
306  }
307  } else {
308  VLOG(2) << "Unknown prerequisite " << buildTypeString(prereq) << " for "
309  << buildTypeString(buildType);
310  return false;
311  }
312  }
313  return true;
314 }
315 
316 } // namespace utils
317 } // namespace cherrypi
Game state.
Definition: state.h:42
bool isWorker(tc::Unit const &unit)
Definition: gamemechanics.h:242
Position predictPosition(Unit const *unit, double frames)
Predict the position of a unit some frames into the future.
Definition: gamemechanics.h:149
std::vector< tc::Unit > getMineralFields(tc::State *state)
Definition: gamemechanics.h:264
int32_t pixel_x
Definition: frame.h:88
int mapHeight() const
Definition: state.h:84
Represents and holds information about buildable types (units, upgrades, techs).
Definition: buildtype.h:36
bool isBuildable(tc::State *state, int x, int y)
Definition: gamemechanics.h:270
int map_size[2]
Definition: state.h:61
bool isBuilding(tc::Unit const &unit)
Definition: gamemechanics.h:250
T y
Definition: basetypes.h:44
double constexpr kDegPerRad
Definition: basetypes.h:28
const BuildType * Zerg_Hive
Definition: buildtype.cpp:346
int32_t pixel_y
Definition: frame.h:88
UnitsInfo & unitsInfo()
Definition: state.h:116
int pxDistanceBB(int xminA, int yminA, int xmaxA, int ymaxA, int xminB, int yminB, int xmaxB, int ymaxB)
Distance between two bounding boxes, in pixels.
Definition: gamemechanics.h:77
std::vector< tc::Unit > getWorkers(tc::State *state)
Definition: gamemechanics.h:258
const BuildType * Zerg_Greater_Spire
Definition: buildtype.cpp:350
int32_t type
Definition: frame.h:87
Definition: frame.h:81
Position getMovePosHelper(int ux, int uy, int px, int py, int mx, int my, double angle, bool exact)
Definition: gamemechanics.h:159
int dimensionLeft
Definition: buildtype.h:83
Represents a unit in the game.
Definition: unitsinfo.h:35
float distance(int x1, int y1, int x2, int y2)
Walktile distance.
Definition: gamemechanics.h:49
int player_id
Definition: state.h:69
const Units & myCompletedUnitsOfType(const BuildType *type)
Our completed units of a particular type (does not include dead units).
Definition: unitsinfo.cpp:238
int mapWidth() const
Definition: state.h:81
const BuildType * Zerg_Spire
Definition: buildtype.cpp:354
Definition: state.h:43
std::string buildTypeString(BuildType const *buildType)
Definition: algorithms.h:176
tc::Unit unit
A copy of the torchcraft unit data.
Definition: unitsinfo.h:81
const BuildType * Zerg_Lair
Definition: buildtype.cpp:345
int dimensionRight
Definition: buildtype.h:85
unsigned int pxdistance(int px1, int py1, int px2, int py2)
Pixel distance.
Definition: gamemechanics.h:42
int dimensionDown
Definition: buildtype.h:86
std::vector< tc::Unit > filterUnitsByType(std::vector< tc::Unit > const &units, tc::BW::UnitType type)
Definition: filter.h:26
Position getMovePos(const State *const state, const Unit *const u, Position p, double angle=0, bool exact=true)
Definition: gamemechanics.h:189
std::vector< const BuildType * > prerequisites
Definition: buildtype.h:43
int dimensionUp
Definition: buildtype.h:84
T x
Definition: basetypes.h:43
Main namespace for bot-related code.
Definition: areainfo.cpp:17
UpgradeLevel getUpgradeLevel(const BuildType *upgrade) const
Get the current level of a given upgrade.
Definition: state.cpp:99
std::vector< uint8_t > buildable_data
Definition: state.h:64
const BuildType * type
Definition: unitsinfo.h:56
bool prerequisitesReady(State *state, const BuildType *buildType)
Definition: gamemechanics.h:277
int x
Definition: unitsinfo.h:37
std::unordered_map< int32_t, std::vector< Unit > > units
Definition: state.h:116
bool hasResearched(const BuildType *tech) const
Check whether a given technology has been researched.
Definition: state.cpp:86
double velocityX
Definition: frame.h:97
int y
Definition: unitsinfo.h:38
int neutral_id
Definition: state.h:70
double velocityY
Definition: frame.h:97
Vec2T< int > Position
Definition: basetypes.h:178
unsigned int disthelper(unsigned int dx, unsigned int dy)
Approximation of Euclidian distance This is the same approximation that StarCraft&#39;s engine uses and t...
Definition: gamemechanics.h:30
Position clampPositionToMap(const State *const state, int const x, int const y, bool strict=false)
Definition: gamemechanics.h:222
float distanceBB(Unit const *a, Unit const *b)
Definition: gamemechanics.h:123
const BuildType * Zerg_Hatchery
Definition: buildtype.cpp:344