diff --git a/ExampleAIClient.vcxproj b/ExampleAIClient.vcxproj index d9010e6..164a6a3 100644 --- a/ExampleAIClient.vcxproj +++ b/ExampleAIClient.vcxproj @@ -88,19 +88,27 @@ + + + + + + + + diff --git a/ExampleAIClient.vcxproj.filters b/ExampleAIClient.vcxproj.filters index 199b5a2..5f3e183 100644 --- a/ExampleAIClient.vcxproj.filters +++ b/ExampleAIClient.vcxproj.filters @@ -33,6 +33,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -50,5 +62,17 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/Source/CameraControl.cpp b/Source/CameraControl.cpp new file mode 100644 index 0000000..31f0cb9 --- /dev/null +++ b/Source/CameraControl.cpp @@ -0,0 +1,25 @@ +#include "CameraControl.h" + +#include + +using BWAPI::Broodwar; +using BWAPI::Position; +using BWAPI::Unit; + +void centerCameraOnPoint(Position p) { + if(p == BWAPI::Positions::Unknown) { + return; + } + Broodwar->setScreenPosition(p - Position(300, 200)); //experimental testing +} + +void centerCameraOnUnit(Unit unit) { + if(!unit) { + return; + } + Position p = unit->getPosition(); + if(p == BWAPI::Positions::Unknown) { + return; + } + centerCameraOnPoint(p); +} \ No newline at end of file diff --git a/Source/CameraControl.h b/Source/CameraControl.h new file mode 100644 index 0000000..ac5367b --- /dev/null +++ b/Source/CameraControl.h @@ -0,0 +1,9 @@ +#ifndef CAMERA_CONTROL_H +#define CAMERA_CONTROL_H + +#include + +void centerCameraOnPoint(BWAPI::Position p); +void centerCameraOnUnit(BWAPI::Unit unit); + +#endif \ No newline at end of file diff --git a/Source/ChatControl.cpp b/Source/ChatControl.cpp new file mode 100644 index 0000000..ec54b28 --- /dev/null +++ b/Source/ChatControl.cpp @@ -0,0 +1,105 @@ +#define _WINSOCKAPI_ // stops windows.h including winsock.h +#define NOMINMAX + +#include "ChatControl.h" + +#include + +#include "IRCClient.h" +#include "CameraControl.h" +#include "WorkerControl.h" +#include "Mutex.h" + +using namespace BWAPI; + +ChatControl::ChatControl(Mutex& gameMutex) : gameMutex(gameMutex) { + +} + +ChatControl::ChatControl(ChatControl& other) : gameMutex(other.gameMutex) { + +} + +ChatControl::ChatControl(ChatControl&& control) : gameMutex(control.gameMutex) { + +} + +ChatControl::~ChatControl() { + +} + +void ChatControl::operator()(const IRCMessage& message, IRCClient& client) { + std::string text = message.parameters.at(message.parameters.size() - 1); + LockGuard guard(gameMutex); + if(text == "build worker") { + Unitset units = Broodwar->self()->getUnits(); + units.remove_if([](Unit i) { return !i->getType().isResourceDepot(); }); + if(units.size() > 0) { + Unit u = units.rand(); + u->train(Broodwar->self()->getRace().getWorker()); + centerCameraOnUnit(u); + } + } else if(text == "send idle workers to mine") { + Unitset units = Broodwar->self()->getUnits(); + for(auto it = units.begin(); it != units.end(); it++) { + if(it->getType().isWorker() && it->isIdle()) { + it->gather(it->getClosestUnit(BWAPI::Filter::IsMineralField)); + centerCameraOnUnit(*it); + } + } + } else if(text == "build supply depot") { + Unitset units = Broodwar->self()->getUnits(); + // Get all our workers + units.remove_if([](Unit i) { return !i->getType().isWorker(); }); + units.remove_if([](Unit i) { return !i->canBuild(); }); + units.remove_if([](Unit i) { return !i->canBuild(UnitTypes::Terran_Supply_Depot); }); + if(units.size() > 0) { + // Choose a random one + Unit worker = units.rand(); + //Find the closest place to build one + buildAtClosestLocation(worker, UnitTypes::Terran_Supply_Depot); + centerCameraOnUnit(worker); + } + } else if(text == "build barracks") { + Unitset units = Broodwar->self()->getUnits(); + // Get all our workers + units.remove_if([](Unit i) { return !i->getType().isWorker(); }); + units.remove_if([](Unit i) { return !i->canBuild(); }); + units.remove_if([](Unit i) { return !i->canBuild(UnitTypes::Terran_Barracks); }); + if(units.size() > 0) { + // Choose a random one + Unit worker = units.rand(); + //Find the closest place to build one + buildAtClosestLocation(worker, UnitTypes::Terran_Barracks); + centerCameraOnUnit(worker); + } + } else if(text == "build marine") { + Unitset units = Broodwar->self()->getUnits(); + units.remove_if([](Unit i) { return i->getType().getID() != UnitTypes::Terran_Barracks; }); + if(units.size() > 0) { + Unit barracks = units.rand(); + if(barracks->train(UnitTypes::Terran_Marine)) { + centerCameraOnUnit(barracks); + } + } + } else if(text == "yolo marines") { + Unitset units = Broodwar->self()->getUnits(); + units.remove_if([](Unit i) { return i->getType().getID() != UnitTypes::Terran_Marine; }); + + Unitset enemyUnits = Broodwar->enemies()[0]->getUnits(); + enemyUnits.remove_if([](Unit i) { return !i->getType().isResourceDepot(); }); + if(enemyUnits.size() > 0) { + + for(auto it = units.begin(); it != units.end(); it++) { + if(!it->attack(PositionOrUnit(enemyUnits.getPosition()))) { + std::cout << "unit failed to attack" << std::endl; + } else { + std::cout << "successful attack!" << std::endl; + } + centerCameraOnUnit(*it); + } + } else { + std::cout << "nothing to attack" << std::endl; + } + } +} \ No newline at end of file diff --git a/Source/ChatControl.h b/Source/ChatControl.h new file mode 100644 index 0000000..a171a22 --- /dev/null +++ b/Source/ChatControl.h @@ -0,0 +1,21 @@ +#ifndef CHAT_CONTROL +#define CHAT_CONTROL + +#include "Mutex.h" +#include "IRCClient.h" + +class ChatControl { +private: + Mutex& gameMutex; + + ChatControl& operator=(const ChatControl&) {} //deleted +public: + ChatControl(Mutex& gameMutex); + ChatControl(ChatControl& o); + ChatControl(ChatControl&&); + ~ChatControl(); + + void operator()(const IRCMessage& message, IRCClient& client); +}; + +#endif \ No newline at end of file diff --git a/Source/ExampleAIClient.cpp b/Source/ExampleAIClient.cpp index 093b3a5..990c856 100644 --- a/Source/ExampleAIClient.cpp +++ b/Source/ExampleAIClient.cpp @@ -11,6 +11,8 @@ #include "IRCClient.h" #include "Thread.h" #include "Mutex.h" +#include "ChatControl.h" +#include "WorkerControl.h" #include @@ -18,11 +20,6 @@ using namespace BWAPI; -void initialMining(); -void handleMessage(const IRCMessage& message, IRCClient& client); - -Mutex mutex; - void reconnect() { while(!BWAPIClient.connect()) @@ -47,8 +44,10 @@ int main(int argc, const char* argv[]) { std::string nickname = "twitchplaysbw"; std::string username = "twitchplaysbw"; + Mutex mutex; + IRCClient client; - client.HookIRCCommand("PRIVMSG", handleMessage); + client.HookIRCCommand("PRIVMSG", ChatControl(mutex)); if(!client.InitSocket()) { std::cerr << "Unable to init socket" << std::endl; @@ -119,198 +118,3 @@ int main(int argc, const char* argv[]) { return 0; } -void centerCameraOnPoint(Position p) { - if(p == Positions::Unknown) { - return; - } - Broodwar->setScreenPosition(p - Position(300, 200)); //experimental testing -} - -void centerCameraOnUnit(UnitInterface* unit) { - if(!unit) { - return; - } - Position p = unit->getPosition(); - if(p == Positions::Unknown) { - return; - } - centerCameraOnPoint(p); -} - -void initialMining() { - //send each worker to the mineral field that is closest to it - Unitset units = Broodwar->self()->getUnits(); - Unitset minerals = Broodwar->getMinerals(); - for ( Unitset::iterator i = units.begin(); i != units.end(); ++i ) - { - if ( i->getType().isWorker() ) - { - Unit closestMineral = NULL; - - for( Unitset::iterator m = minerals.begin(); m != minerals.end(); ++m ) - { - if ( !closestMineral || i->getDistance(*m) < i->getDistance(closestMineral)) - closestMineral = *m; - } - if ( closestMineral ) - i->rightClick(closestMineral); - } - else if ( i->getType().isResourceDepot() ) - { - //if this is a center, tell it to build the appropiate type of worker - i->train(Broodwar->self()->getRace().getWorker()); - } - } -} - -bool strBeginsWith(const std::string& str, const std::string& begin) { - return str.substr(0, begin.size()) == begin; -} - -std::vector strSplit(const std::string& str, char splitOn) { - std::vector ret; - std::string current; - for(std::size_t i = 0; i < str.size(); i++) { - if(str[i] == splitOn) { - ret.push_back(current); - current = ""; - } else { - current.push_back(str[i]); - } - } - ret.push_back(current); - return std::move(ret); -} - -bool buildAtLocation(Unit worker, UnitType type, TilePosition tile) { - if(worker->canBuild(type, tile)) { - Broodwar->drawCircle(CoordinateType::Map, tile.x * 32, tile.y * 32, 1.2 * std::max(type.width(), type.height()), Color(255, 0 , 0)); - Unitset closestOtherUnits = Broodwar->getUnitsInRadius(tile.x * 32, tile.y * 32, 1.2 * std::max(type.width(), type.height())); - closestOtherUnits.remove_if([worker](Unit u) { return u == worker; }); - if(closestOtherUnits.size() == 0) { - return worker->build(type, tile); - } - } - return false; -} - -void buildAtClosestLocation(Unit worker, UnitType type) { - TilePosition tile = worker->getTilePosition(); - if(buildAtLocation(worker, type, tile)) { - std::cout << "built successfully!" << std::endl; - return; - } - for(int radius = 1; radius < 200; radius++) { - //Spiral out - tile.x++; - for(int j = 0; j < radius; j++) { - tile.x--; - tile.y--; - if(buildAtLocation(worker, type, tile)) { - std::cout << "built successfully!" << std::endl; - return; - } - } - for(int j = 0; j < radius; j++) { - tile.x--; - tile.y++; - if(buildAtLocation(worker, type, tile)) { - std::cout << "built successfully!" << std::endl; - return; - } - } - for(int j = 0; j < radius; j++) { - tile.x++; - tile.y++; - if(buildAtLocation(worker, type, tile)) { - std::cout << "built successfully!" << std::endl; - return; - } - } - for(int j = 0; j < radius; j++) { - tile.x++; - tile.y--; - if(buildAtLocation(worker, type, tile)) { - std::cout << "built successfully!" << std::endl; - return; - } - } - } -} - -void handleMessage(const IRCMessage& message, IRCClient& client) { - std::string text = message.parameters.at(message.parameters.size() - 1); - LockGuard guard(mutex); - if(text == "build worker") { - Unitset units = Broodwar->self()->getUnits(); - units.remove_if([](Unit i) { return !i->getType().isResourceDepot(); }); - if(units.size() > 0) { - Unit u = units.rand(); - u->train(Broodwar->self()->getRace().getWorker()); - centerCameraOnUnit(u); - } - } else if(text == "send idle workers to mine") { - Unitset units = Broodwar->self()->getUnits(); - for(auto it = units.begin(); it != units.end(); it++) { - if(it->getType().isWorker() && it->isIdle()) { - it->gather(it->getClosestUnit(Filter::IsMineralField)); - centerCameraOnUnit(*it); - } - } - } else if(text == "build supply depot") { - Unitset units = Broodwar->self()->getUnits(); - // Get all our workers - units.remove_if([](Unit i) { return !i->getType().isWorker(); }); - units.remove_if([](Unit i) { return !i->canBuild(); }); - units.remove_if([](Unit i) { return !i->canBuild(UnitTypes::Terran_Supply_Depot); }); - if(units.size() > 0) { - // Choose a random one - Unit worker = units.rand(); - //Find the closest place to build one - buildAtClosestLocation(worker, UnitTypes::Terran_Supply_Depot); - centerCameraOnUnit(worker); - } - } else if(text == "build barracks") { - Unitset units = Broodwar->self()->getUnits(); - // Get all our workers - units.remove_if([](Unit i) { return !i->getType().isWorker(); }); - units.remove_if([](Unit i) { return !i->canBuild(); }); - units.remove_if([](Unit i) { return !i->canBuild(UnitTypes::Terran_Barracks); }); - if(units.size() > 0) { - // Choose a random one - Unit worker = units.rand(); - //Find the closest place to build one - buildAtClosestLocation(worker, UnitTypes::Terran_Barracks); - centerCameraOnUnit(worker); - } - } else if(text == "build marine") { - Unitset units = Broodwar->self()->getUnits(); - units.remove_if([](Unit i) { return i->getType().getID() != UnitTypes::Terran_Barracks; }); - if(units.size() > 0) { - Unit barracks = units.rand(); - if(barracks->train(UnitTypes::Terran_Marine)) { - centerCameraOnUnit(barracks); - } - } - } else if(text == "yolo marines") { - Unitset units = Broodwar->self()->getUnits(); - units.remove_if([](Unit i) { return i->getType().getID() != UnitTypes::Terran_Marine; }); - - Unitset enemyUnits = Broodwar->enemies()[0]->getUnits(); - enemyUnits.remove_if([](Unit i) { return !i->getType().isResourceDepot(); }); - if(enemyUnits.size() > 0) { - - for(auto it = units.begin(); it != units.end(); it++) { - if(!it->attack(PositionOrUnit(enemyUnits.getPosition()))) { - std::cout << "unit failed to attack" << std::endl; - } else { - std::cout << "successful attack!" << std::endl; - } - centerCameraOnUnit(*it); - } - } else { - std::cout << "nothing to attack" << std::endl; - } - } -} - diff --git a/Source/IRCClient.cpp b/Source/IRCClient.cpp index 5f4aa2b..7654325 100644 --- a/Source/IRCClient.cpp +++ b/Source/IRCClient.cpp @@ -20,23 +20,11 @@ #include "IRCSocket.h" #include "IRCClient.h" #include "IRCHandler.h" +#include "StringOperations.h" using std::string; using std::vector; -vector split(const string& text, char sep) -{ - vector tokens; - size_t start = 0, end = 0; - while ((end = text.find(sep, start)) != string::npos) - { - tokens.push_back(text.substr(start, end - start)); - start = end + 1; - } - tokens.push_back(text.substr(start)); - return tokens; -} - bool IRCClient::InitSocket() { return _socket.Init(); @@ -157,12 +145,12 @@ void IRCClient::Parse(const string& _data) CallHook(command, ircMessage); } -void IRCClient::HookIRCCommand(const string& command, void (*function)(const IRCMessage& /*message*/, IRCClient& /*client*/)) +void IRCClient::HookIRCCommand(const string& command, std::function&& function) { IRCCommandHook hook; hook.command = command; - hook.function = function; + hook.function = std::move(function); _hooks.push_back(hook); } @@ -176,7 +164,7 @@ void IRCClient::CallHook(const string& command, const IRCMessage& message) { if (itr->command == command) { - (*(itr->function))(message, *this); + itr->function(message, *this); break; } } diff --git a/Source/IRCClient.h b/Source/IRCClient.h index a90910a..f236d37 100644 --- a/Source/IRCClient.h +++ b/Source/IRCClient.h @@ -21,12 +21,12 @@ #include #include #include +#include #include "IRCSocket.h" +#include "StringOperations.h" class IRCClient; -extern std::vector split(const std::string&, char); - struct IRCCommandPrefix { void Parse(const std::string& data) @@ -73,7 +73,7 @@ struct IRCCommandHook IRCCommandHook() : function(NULL) {}; std::string command; - void (*function)(const IRCMessage& /*message*/, IRCClient& /*client*/); + std::function function; }; class IRCClient @@ -92,7 +92,7 @@ class IRCClient void ReceiveData(); - void HookIRCCommand(const std::string& /*command*/, void (*function)(const IRCMessage& /*message*/, IRCClient& /*client*/)); + void HookIRCCommand(const std::string& /*command*/, std::function&& function); void Parse(const std::string& /*data*/); diff --git a/Source/StringOperations.cpp b/Source/StringOperations.cpp new file mode 100644 index 0000000..6cdc545 --- /dev/null +++ b/Source/StringOperations.cpp @@ -0,0 +1,19 @@ +#include "StringOperations.h" + +#include +#include + +using std::string; +using std::vector; + +vector split(const string& text, char sep) { + vector tokens; + size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != string::npos) + { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); + return tokens; +} \ No newline at end of file diff --git a/Source/StringOperations.h b/Source/StringOperations.h new file mode 100644 index 0000000..c33dcc9 --- /dev/null +++ b/Source/StringOperations.h @@ -0,0 +1,9 @@ +#ifndef STRING_OPERATIONS_H +#define STRING_OPERATIONS_H + +#include +#include + +std::vector split(const std::string&, char); + +#endif \ No newline at end of file diff --git a/Source/WorkerControl.cpp b/Source/WorkerControl.cpp new file mode 100644 index 0000000..557eef9 --- /dev/null +++ b/Source/WorkerControl.cpp @@ -0,0 +1,94 @@ +#include "WorkerControl.h" + +#include + +#include + +using BWAPI::Unitset; +using BWAPI::Unit; +using BWAPI::Broodwar; +using BWAPI::UnitType; +using BWAPI::TilePosition; +using BWAPI::Color; + +void initialMining() { + //send each worker to the mineral field that is closest to it + Unitset units = Broodwar->self()->getUnits(); + Unitset minerals = Broodwar->getMinerals(); + for ( Unitset::iterator i = units.begin(); i != units.end(); ++i ) + { + if ( i->getType().isWorker() ) + { + Unit closestMineral = NULL; + + for( Unitset::iterator m = minerals.begin(); m != minerals.end(); ++m ) + { + if ( !closestMineral || i->getDistance(*m) < i->getDistance(closestMineral)) + closestMineral = *m; + } + if ( closestMineral ) + i->rightClick(closestMineral); + } + else if ( i->getType().isResourceDepot() ) + { + //if this is a center, tell it to build the appropiate type of worker + i->train(Broodwar->self()->getRace().getWorker()); + } + } +} + +bool buildAtLocation(Unit worker, UnitType type, TilePosition tile) { + if(worker->canBuild(type, tile)) { + Broodwar->drawCircle(BWAPI::CoordinateType::Map, tile.x * 32, tile.y * 32, 1.2 * std::max(type.width(), type.height()), Color(255, 0 , 0)); + Unitset closestOtherUnits = Broodwar->getUnitsInRadius(tile.x * 32, tile.y * 32, 1.2 * std::max(type.width(), type.height())); + closestOtherUnits.remove_if([worker](Unit u) { return u == worker; }); + if(closestOtherUnits.size() == 0) { + return worker->build(type, tile); + } + } + return false; +} + +void buildAtClosestLocation(Unit worker, UnitType type) { + TilePosition tile = worker->getTilePosition(); + if(buildAtLocation(worker, type, tile)) { + std::cout << "built successfully!" << std::endl; + return; + } + for(int radius = 1; radius < 200; radius++) { + //Spiral out + tile.x++; + for(int j = 0; j < radius; j++) { + tile.x--; + tile.y--; + if(buildAtLocation(worker, type, tile)) { + std::cout << "built successfully!" << std::endl; + return; + } + } + for(int j = 0; j < radius; j++) { + tile.x--; + tile.y++; + if(buildAtLocation(worker, type, tile)) { + std::cout << "built successfully!" << std::endl; + return; + } + } + for(int j = 0; j < radius; j++) { + tile.x++; + tile.y++; + if(buildAtLocation(worker, type, tile)) { + std::cout << "built successfully!" << std::endl; + return; + } + } + for(int j = 0; j < radius; j++) { + tile.x++; + tile.y--; + if(buildAtLocation(worker, type, tile)) { + std::cout << "built successfully!" << std::endl; + return; + } + } + } +} \ No newline at end of file diff --git a/Source/WorkerControl.h b/Source/WorkerControl.h new file mode 100644 index 0000000..30f8a69 --- /dev/null +++ b/Source/WorkerControl.h @@ -0,0 +1,10 @@ +#ifndef WORKER_CONTROL_H +#define WORKER_CONTROL_H + +#include + +void initialMining(); +bool buildAtLocation(BWAPI::Unit worker, BWAPI::UnitType type, BWAPI::TilePosition tile); +void buildAtClosestLocation(BWAPI::Unit worker, BWAPI::UnitType type); + +#endif \ No newline at end of file