From ec8d3777deb53a5679d9ae4a3301a9d197b5e894 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Bergstr=C3=B6m?= <davbe125@student.liu.se>
Date: Tue, 26 Jun 2018 16:18:45 +0200
Subject: [PATCH] Add enough Python API to make it possible to mine minerals

---
 python-api-src/lib_sc2_typeenums.cpp |  2 +-
 python-api-src/lib_unit.cpp          | 41 ++++++++++++++++++++++++++
 python-api-src/lib_unittype.cpp      | 43 ++++++++++++++++++++++++++++
 python-api-src/lib_util.cpp          | 12 ++++++++
 python-api-src/library.cpp           |  8 +++++-
 python-api-src/library.h             |  6 +++-
 src/Common.h                         |  4 ++-
 7 files changed, 112 insertions(+), 4 deletions(-)
 create mode 100644 python-api-src/lib_unit.cpp
 create mode 100644 python-api-src/lib_unittype.cpp
 create mode 100644 python-api-src/lib_util.cpp

diff --git a/python-api-src/lib_sc2_typeenums.cpp b/python-api-src/lib_sc2_typeenums.cpp
index 21b0c09..3715131 100644
--- a/python-api-src/lib_sc2_typeenums.cpp
+++ b/python-api-src/lib_sc2_typeenums.cpp
@@ -4,7 +4,7 @@ namespace py = pybind11;
 
 // This file is brought to you by VIM, phew.
 
-void define_pyteenums(py::module & m)
+void define_typeenums(py::module & m)
 {
     py::enum_<sc2::UNIT_TYPEID>(m, "UNIT_TYPEID")
         .value("INVALID", sc2::UNIT_TYPEID::INVALID)
diff --git a/python-api-src/lib_unit.cpp b/python-api-src/lib_unit.cpp
new file mode 100644
index 0000000..c3b71e1
--- /dev/null
+++ b/python-api-src/lib_unit.cpp
@@ -0,0 +1,41 @@
+#include "library.h"
+
+namespace py = pybind11;
+
+void define_unit(py::module & m)
+{
+    py::class_<Unit>(m, "Unit")
+        .def_property_readonly("unit_type", &Unit::getType)
+        .def_property_readonly("position", &Unit::getPosition)
+        .def_property_readonly("tile_position", &Unit::getTilePosition)
+        .def_property_readonly("hit_points", &Unit::getHitPoints)
+        .def_property_readonly("shields", &Unit::getShields)
+        .def_property_readonly("energy", &Unit::getEnergy)
+        .def_property_readonly("player", &Unit::getPlayer)
+        .def_property_readonly("id", &Unit::getID)
+        .def_property_readonly("build_percentage", &Unit::getBuildPercentage)
+        .def_property_readonly("weapon_cooldown", &Unit::getWeaponCooldown)
+        .def_property_readonly("completed", &Unit::isCompleted)
+        .def_property_readonly("being_constructed", &Unit::isBeingConstructed)
+        .def_property_readonly("cloaked", &Unit::isCloaked)
+        .def_property_readonly("flying", &Unit::isFlying)
+        .def_property_readonly("alive", &Unit::isAlive)
+        .def_property_readonly("powered", &Unit::isPowered)
+        .def_property_readonly("idle", &Unit::isIdle)
+        .def_property_readonly("burrowed", &Unit::isBurrowed)
+        .def_property_readonly("valid", &Unit::isValid)
+        .def_property_readonly("training", &Unit::isTraining)
+        .def_property_readonly("constructing", &Unit::isConstructing)
+        .def("stop", &Unit::stop)
+        .def("attackUnit", &Unit::attackUnit)
+        .def("attackMove", &Unit::attackMove)
+        .def("move", py::overload_cast<const CCPosition &>(&Unit::move, py::const_))
+        .def("move", py::overload_cast<const CCTilePosition &>(&Unit::move, py::const_))
+        .def("rightClick", &Unit::rightClick)
+        .def("repair", &Unit::repair)
+        .def("build", &Unit::build)
+        .def("buildTarget", &Unit::buildTarget)
+        .def("train", &Unit::train)
+        .def("morph", &Unit::morph)
+        .def("__repr__", [](const Unit & unit) { return "<Unit of type: '" + unit.getType().getName() + "'>"; });
+}
diff --git a/python-api-src/lib_unittype.cpp b/python-api-src/lib_unittype.cpp
new file mode 100644
index 0000000..0586d1b
--- /dev/null
+++ b/python-api-src/lib_unittype.cpp
@@ -0,0 +1,43 @@
+#include "library.h"
+
+namespace py = pybind11;
+
+void define_unittype(py::module & m)
+{
+    py::class_<UnitType>(m, "UnitType")
+        .def("is", &UnitType::is, "Check if UnitType is UnitTypeID")
+        .def(py::self == py::self)
+        .def_property_readonly("name", &UnitType::getName)
+        .def_property_readonly("race", &UnitType::getRace)
+        .def_property_readonly("is_valid", &UnitType::isValid)
+        .def_property_readonly("is_building", &UnitType::isBuilding)
+        .def_property_readonly("is_combat_unit", &UnitType::isCombatUnit)
+        .def_property_readonly("is_supply_provider", &UnitType::isSupplyProvider)
+        .def_property_readonly("is_resource_depot", &UnitType::isResourceDepot)
+        .def_property_readonly("is_refinery", &UnitType::isRefinery)
+        .def_property_readonly("is_detector", &UnitType::isDetector)
+        .def_property_readonly("is_geyser", &UnitType::isGeyser)
+        .def_property_readonly("is_mineral", &UnitType::isMineral)
+        .def_property_readonly("is_worker", &UnitType::isWorker)
+        .def_property_readonly("is_morphed_building", &UnitType::isMorphedBuilding)
+        // Not implemented in CommandCenter
+        //.def_property_readonly("can_attack", &UnitType::canAttack)
+        //.def_property_readonly("can_Move", &UnitType::canMove)
+        .def_property_readonly("is_addon", &UnitType::isAddon)
+        .def_property_readonly("attack_range", &UnitType::getAttackRange)
+        .def_property_readonly("tile_width", &UnitType::tileWidth)
+        .def_property_readonly("tile_height", &UnitType::tileHeight)
+        .def_property_readonly("supply_provided", &UnitType::supplyProvided)
+        .def_property_readonly("supply_required", &UnitType::supplyRequired)
+        .def_property_readonly("mineral_price", &UnitType::mineralPrice)
+        .def_property_readonly("gas_price", &UnitType::gasPrice)
+        .def_property_readonly("is_overlord", &UnitType::isOverlord)
+        .def_property_readonly("is_larva", &UnitType::isLarva)
+        .def_property_readonly("is_egg", &UnitType::isEgg)
+        .def_property_readonly("is_queen", &UnitType::isQueen)
+        .def_property_readonly("is_tank", &UnitType::isTank)
+        .def("__repr__", [](const UnitType & unit_type) { return "<UnitType: '" + unit_type.getName() + "'>"; });
+
+        // Not implemented in CommandCenter
+        //.def("whatBuilds", &UnitType::whatBuilds);
+}
\ No newline at end of file
diff --git a/python-api-src/lib_util.cpp b/python-api-src/lib_util.cpp
new file mode 100644
index 0000000..116975c
--- /dev/null
+++ b/python-api-src/lib_util.cpp
@@ -0,0 +1,12 @@
+#include "library.h"
+
+namespace py = pybind11;
+
+void define_util(py::module & mod)
+{
+    py::module m = mod.def_submodule("util");
+
+    m.def("dist", py::overload_cast<const Unit &, const Unit &>(&Util::Dist));
+    m.def("dist", py::overload_cast<const Unit &, const CCPosition &>(&Util::Dist));
+    m.def("dist", py::overload_cast<const CCPosition &, const CCPosition &>(&Util::Dist));
+}
\ No newline at end of file
diff --git a/python-api-src/library.cpp b/python-api-src/library.cpp
index 1185755..08add75 100644
--- a/python-api-src/library.cpp
+++ b/python-api-src/library.cpp
@@ -4,7 +4,12 @@ namespace py = pybind11;
 
 PYBIND11_MODULE(library, m)
 {
-    m.doc() = "pybind11 example plugin";
+    m.doc() = "Python API for playing Starcraft II";
+
+    define_typeenums(m);
+    define_unit(m);
+    define_unittype(m);
+    define_util(m);
 
     py::class_<Coordinator>(m, "Coordinator")
         .def(py::init())
@@ -28,6 +33,7 @@ PYBIND11_MODULE(library, m)
         .def("OnGameStart", &IDABot::OnGameStart)
         .def("OnStep", &IDABot::OnStep)
         .def("OnStep_UpdateIDABot", &IDABot::OnStep_UpdateIDABot)
+        .def("GetAllUnits", &IDABot::GetAllUnits)
         .def("GetMyUnits", &IDABot::GetMyUnits);
 
     py::class_<sc2::PlayerSetup>(m, "PlayerSetup");
diff --git a/python-api-src/library.h b/python-api-src/library.h
index c8bdb79..14223b3 100644
--- a/python-api-src/library.h
+++ b/python-api-src/library.h
@@ -5,6 +5,7 @@
 #include "../src/IDABot.h"
 #include <iostream>
 #include <pybind11/stl.h> /* Automatic conversion from std::vector to Python lists */
+#include <pybind11/operators.h> /* Convenient operator support */
 
 // Wrapper class since the initialization uses pure argc/argv and these cannot be wrapped into Python correctly
 class Coordinator : public sc2::Coordinator
@@ -49,4 +50,7 @@ public:
     }
 };
 
-void define_typeenums(pybind11::module & m);
\ No newline at end of file
+void define_typeenums(pybind11::module & m);
+void define_unit(pybind11::module & m);
+void define_unittype(pybind11::module &m);
+void define_util(pybind11::module &m);
\ No newline at end of file
diff --git a/src/Common.h b/src/Common.h
index dec31ff..fa45feb 100644
--- a/src/Common.h
+++ b/src/Common.h
@@ -10,7 +10,8 @@
 #include <string>
 #include <array>
 
-// Somewhere in the dependency graph someone defines a macro max, which ruins everything
+// Somewhere in the dependency graph someone defines a macro called max,
+// which breaks all calls to std::limits::max
 #define NOMINMAX
 
 #include <sc2api/sc2_api.h>
@@ -25,6 +26,7 @@ typedef float               CCHealth;
 typedef float               CCPositionType;
     
 typedef size_t CCPlayer;
+
 namespace Players
 {
     enum {Self = 0u, Enemy = 1u, Neutral = 2u, Ally = 3u, Size = 4u, None = 5u};
-- 
GitLab