Browse Source

Further changes for programming challenge.
* setting obstacles and creature params in duel config file
* handling arguments for Odpalarka
* handling runner + runner + runner + client scenario (memory measura AND visualziation)

Michał W. Urbańczyk 14 years ago
parent
commit
92e508fe1f

+ 10 - 0
AI/StupidAI/StupidAI.cpp

@@ -204,6 +204,16 @@ void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2
 {
 	print("battleStart called");
 	side = Side;
+	TStacks myStacks = cb->battleGetStacks(CBattleCallback::ONLY_MINE);
+	std::cout << "My side: " << side << std::endl
+		<< "I have " << myStacks.size() << " stacks. They are:\n";
+
+	for(int i = 0; i < myStacks.size(); i++)
+	{
+		const CStack *s = myStacks.at(i);
+		tlog5 << format("%2d) Stack of %4d %s.\n\tAttack:\t%4d, \n\tDefense:\t%4d, \n\tHP:\t%4d\n\tDamage:\t%4d-%d\n")
+			% i % s->count % s->getCreature()->namePl % s->Attack() % s->Defense() % s->MaxHealth() % s->getMinDamage() % s->getMaxDamage();
+	}
 }
 
 void CStupidAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) 

+ 6 - 1
AI/StupidAI/stdafx.h

@@ -1,3 +1,8 @@
 #pragma  once
 #include <boost/lexical_cast.hpp>
-#include "../../AI_Base.h"
+#include <boost/format.hpp>
+#include "../../AI_Base.h"
+
+using boost::format;
+using boost::str;
+using boost::lexical_cast;

+ 59 - 7
Odpalarka/main.cpp

@@ -1,9 +1,60 @@
 #include "../global.h"
 #include <boost/thread.hpp>
 #include <boost/bind.hpp>
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+void prog_help() 
+{
+	throw std::exception("The method or operation is not implemented.");
+}
 
 int main(int argc, const char **argv)
 {
+	std::cout << "VCMI Odpalarka\nMy path: " << argv[0] << std::endl;
+
+	po::options_description opts("Allowed options");
+	opts.add_options()
+		("help,h", "display help and exit")
+		("aiLeft,l", po::value<std::string>(), "Left AI path")
+		("aiRight,r", po::value<std::string>(), "Right AI path")
+		("battle,b", po::value<std::string>(), "Duel file path")
+		("visualization,v", "Runs a client to display a visualization of battle");
+
+	std::string leftAI = "StupidAI", 
+				rightAI = "StupidAI",
+				battle = "b1.json";
+
+	po::variables_map vm;
+	if(argc > 1)
+	{
+		try
+		{
+			po::store(po::parse_command_line(argc, argv, opts), vm);
+			po::notify(vm);
+			leftAI = vm["aiLeft"].as<std::string>();
+			rightAI = vm["aiRight"].as<std::string>();
+			battle = vm["battle"].as<std::string>();
+		}
+		catch(std::exception &e) 
+		{
+			std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
+			exit(1);
+		}
+	}
+	else
+	{
+		std::cout << "Default AIs will be used." << std::endl;
+	}
+
+	if(vm.count("help"))
+	{
+		prog_help();
+		return 0;
+	}
+
+
+
 	std::string runnername = 
 #ifdef _WIN32
 		"VCMI_BattleAiHost.exe"
@@ -19,17 +70,18 @@ int main(int argc, const char **argv)
 #endif
 	;
 
-	std::string serverCommand = servername + " b1.json StupidAI MadAI"; // StupidAI MadAI
+	bool withVisualization = vm.count("visualization");
+	std::string serverCommand = servername + " " + battle + " " + leftAI + " " + rightAI + (withVisualization ? " v" : "");
+	std::string runnerCommand = runnername;
 	boost::thread t(boost::bind(std::system, serverCommand.c_str()));
-	boost::thread tt(boost::bind(std::system, runnername.c_str()));
-	boost::thread ttt(boost::bind(std::system, runnername.c_str()));
-	if(argc == 2)
+	boost::thread tt(boost::bind(std::system, runnerCommand.c_str()));
+	boost::thread ttt(boost::bind(std::system, runnerCommand.c_str()));
+	boost::thread tttt(boost::bind(std::system, runnername.c_str()));
+	if(withVisualization)
 	{
-		boost::this_thread::sleep(boost::posix_time::millisec(500)); //FIXME
+		//boost::this_thread::sleep(boost::posix_time::millisec(500)); //FIXME
 		boost::thread tttt(boost::bind(std::system, "VCMI_Client.exe -battle"));
 	}
-	else if(argc == 1)
-		boost::thread tttt(boost::bind(std::system, runnername.c_str()));
 
 	//boost::this_thread::sleep(boost::posix_time::seconds(5));
 

+ 1 - 0
VCMI_BattleAiHost/CheckTime.h

@@ -61,6 +61,7 @@ const int PROCESS_INFO_TIME = 5;
 const int MAKE_DECIDION_TIME = 75; 
 const int MEASURE_MARGIN = 1;
 const int HANGUP_TIME = 50;
+const int STARTUP_TIME = 100;
 
 void postInfoCall(int timeUsed);
 void postDecisionCall(int timeUsed);

+ 12 - 8
VCMI_BattleAiHost/NetPacksRunner.cpp

@@ -12,12 +12,12 @@
 #include "CheckTime.h"
 
 
-void postInfoCall(int timeUsed)
+void postInfoCall(int timeUsed, int limit)
 {
 	tlog0 << "AI was processing info for " << timeUsed << " ms.\n";
-	if(timeUsed > PROCESS_INFO_TIME + MEASURE_MARGIN)
+	if(timeUsed > limit + MEASURE_MARGIN)
 	{
-		tlog1 << "That's too long! AI is disqualified!\n";
+		tlog1 << "That's too long (limit=" << limit << "+" << MEASURE_MARGIN << ")! AI is disqualified!\n";
 		exit(1);
 	}
 }
@@ -34,20 +34,25 @@ void postDecisionCall(int timeUsed)
 
 //macros to avoid code duplication - calls given method with given arguments if interface for specific player is present
 //awaiting variadic templates...
-#define BATTLE_INTERFACE_CALL_IF_PRESENT(function,...) 		\
+// 
+
+#define BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(TIME_LIMIT, function, ...) 		\
 	do														\
 	{														\
 		int timeUsed = 0;									\
 		if(cl->ai)											\
 		{													\
-			Bomb *b = new Bomb(PROCESS_INFO_TIME + HANGUP_TIME);\
+			Bomb *b = new Bomb(TIME_LIMIT + HANGUP_TIME);	\
 			CheckTime pr;									\
 			cl->ai->function(__VA_ARGS__);					\
-			postInfoCall(pr.timeSinceStart());				\
+			postInfoCall(pr.timeSinceStart(), TIME_LIMIT);	\
 			b->disarm();									\
 		}													\
 	} while(0)
 
+#define BATTLE_INTERFACE_CALL_IF_PRESENT(function,...) 		\
+	BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(PROCESS_INFO_TIME, function, __VA_ARGS__)
+
 #define UNEXPECTED_PACK assert(0)
 
 void SetResources::applyCl( CClient *cl )
@@ -277,8 +282,7 @@ void GarrisonDialog::applyCl(CClient *cl)
 
 void BattleStart::applyCl( CClient *cl )
 {
-	//TODO!!!!
-	BATTLE_INTERFACE_CALL_IF_PRESENT(battleStart, info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], cl->color);
+	BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(STARTUP_TIME, battleStart, info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], cl->color);
 }
 
 void BattleNextRound::applyFirstCl(CClient *cl)

+ 8 - 1
VCMI_BattleAiHost/main.cpp

@@ -17,6 +17,8 @@
 #include <boost/format.hpp>
 #include "Client.h"
 #include "../lib/VCMI_Lib.h"
+#include "../lib/BattleState.h"
+#include "../lib/NetPacks.h"
 
 using namespace std;
 using namespace boost;
@@ -51,7 +53,7 @@ int main(int argc, char** argv)
 			try
 			{
 				tlog0 << "Establishing connection...\n";
-				serv = new CConnection(host, port, "DLL host");
+				serv = new CConnection(host, port, NAME);
 			}
 			catch(...)
 			{
@@ -87,6 +89,11 @@ int main(int argc, char** argv)
 			cl.color = color;
 			tlog0 << "AI created\n";
 			cl.ai->init(cbc);
+
+			BattleStart bs;
+			bs.info = gs->curB;
+			bs.applyFirstCl(&cl);
+			bs.applyCl(&cl);
 		}
 		else
 			tlog0 << "Not loading AI, only simulation will be run\n";

+ 10 - 1
b1.json

@@ -1,6 +1,7 @@
 {
 	"terType" : 1,
 	"bfieldType" : 0,
+	"obstacles" : [77, 94, 111, [28, 7]], //vector of positions or vectored pairs [type, pos]
 	
 	"sides" : 
 	[
@@ -9,10 +10,18 @@
 			"army" : [[10, 40]],
 			"heroid" : 0,
 			"spells" : [1,2,3,4]
-		}
+		},
 		{
 			"side" : 1,
 			"army" : [[11, 41]]
 		}
+	],
+	
+	"creatures" : 
+	[
+		{
+			"id" : 10,
+			"attack" : 50
+		}
 	]
 }

+ 2 - 1
client/CBattleInterface.cpp

@@ -1577,6 +1577,7 @@ void CBattleInterface::activate()
 
 void CBattleInterface::deactivate()
 {
+	if(!active) return;
 	deactivateKeys();
 	deactivateMouseMove();
 	deactivateRClick();
@@ -4502,7 +4503,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 
 		CCS->musich->playMusic(musicBase::winBattle);
 		CCS->videoh->open(VIDEO_WIN);
-		std::string str = CGI->generaltexth->allTexts[text];
+		std::string str = text >= 0 ? CGI->generaltexth->allTexts[text] : std::string("AI has been disqualified!");
 		
 		const CGHeroInstance * ourHero = weAreAttacker? owner->attackingHeroInstance : owner->defendingHeroInstance;
 		if (ourHero)

+ 1 - 1
client/Client.cpp

@@ -433,7 +433,7 @@ void CClient::newDuel(CConnection *con, StartInfo *si)
 				try
 				{
 					tlog0 << "Establishing connection...\n";
-					serv = new CConnection(host, port, "DLL host");
+					serv = new CConnection(host, port, NAME);
 				}
 				catch(...)
 				{

+ 1 - 1
config/settings.txt

@@ -13,5 +13,5 @@ clientSettings
 	defaultPlayerAI=GeniusAI; 
 	neutralBattleAI=StupidAI;
 	showFPS=0;
-	classicCreatureWindow=0;
+	classicCreatureWindow=1;
 }

+ 60 - 0
lib/CGameState.cpp

@@ -989,9 +989,30 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 					if(count || obj->hasStackAtSlot(j))
 						obj->setCreature(j, cre, count);
 				}
+
+				BOOST_FOREACH(const DuelParameters::CusomCreature &cc, dp.creatures)
+				{
+					CCreature *c = VLC->creh->creatures[cc.id];
+					if(cc.attack >= 0)
+						c->getBonus(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK))->val = cc.attack;
+					if(cc.defense >= 0)
+						c->getBonus(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE))->val = cc.defense;
+					if(cc.speed >= 0)
+						c->getBonus(Selector::type(Bonus::STACKS_SPEED))->val = cc.speed;
+					if(cc.HP >= 0)
+						c->getBonus(Selector::type(Bonus::STACK_HEALTH))->val = cc.HP;
+					if(cc.dmg >= 0)
+					{
+						c->getBonus(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1))->val = cc.dmg;
+						c->getBonus(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2))->val = cc.dmg;
+					}
+					if(cc.shoots >= 0)
+						c->getBonus(Selector::type(Bonus::SHOTS))->val = cc.shoots;
+				}
 			}
 
 			curB = BattleInfo::setupBattle(int3(), dp.bfieldType, dp.terType, armies, heroes, false, town);
+			curB->obstacles = dp.obstacles;
 			curB->localInit();
 			return;
 		}
@@ -2798,6 +2819,45 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
 		
 	}
 
+	BOOST_FOREACH(const JsonNode &n, duelData["obstacles"].Vector())
+	{
+		CObstacleInstance oi;
+		if(n.getType() == JsonNode::DATA_VECTOR)
+		{
+			oi.ID = n.Vector()[0].Float();
+			oi.pos = n.Vector()[1].Float();
+		}
+		else
+		{
+			assert(n.getType() == JsonNode::DATA_FLOAT);
+			oi.ID = 21;
+			oi.pos = n.Float();
+		}
+		oi.uniqueID = ret.obstacles.size();
+		ret.obstacles.push_back(oi);
+	}
+
+	BOOST_FOREACH(const JsonNode &n, duelData["creatures"].Vector())
+	{
+		CusomCreature cc;
+		cc.id = n["id"].Float();
+
+#define retreive(name)								\
+	if(n[ #name ].getType() == JsonNode::DATA_FLOAT)\
+			cc.name = n[ #name ].Float();			\
+	else											\
+		cc.name = -1;
+
+		retreive(attack);
+		retreive(defense);
+		retreive(HP);
+		retreive(dmg);
+		retreive(shoots);
+		retreive(speed);
+		ret.creatures.push_back(cc);
+	}
+
+
 	return ret;
 }
 

+ 20 - 1
lib/CGameState.h

@@ -306,12 +306,31 @@ struct DLL_EXPORT DuelParameters
 		}
 	} sides[2];
 
+	std::vector<CObstacleInstance> obstacles;
+
 	static DuelParameters fromJSON(const std::string &fname);
 
+	struct CusomCreature
+	{
+		int id;
+		int attack, defense, dmg, HP, speed, shoots;
+
+		CusomCreature() 
+		{
+			id = attack = defense = dmg = HP = speed = shoots = -1;
+		}
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & id & attack & defense & dmg & HP & speed & shoots;
+		}
+	};
+
+	std::vector<CusomCreature> creatures;
+
 	DuelParameters();
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & terType & bfieldType & sides;
+		h & terType & bfieldType & sides & obstacles & creatures;
 	}
 };
 

+ 1 - 0
lib/Connection.cpp

@@ -75,6 +75,7 @@ void CConnection::init()
 	wmx = new boost::mutex;
 	rmx = new boost::mutex;
 
+	contactName = pom;
 	handler = NULL;
 	receivedStop = sendStop = false;
 	static int cid = 1;

+ 1 - 0
lib/Connection.h

@@ -902,6 +902,7 @@ public:
 	bool myEndianess, contactEndianess; //true if little endian, if endianess is different we'll have to revert received multi-byte vars
     boost::asio::io_service *io_service;
 	std::string name; //who uses this connection
+	std::string contactName;
 
 	int connectionID;
 	CConnection *c;

+ 34 - 10
server/CVCMIServer.cpp

@@ -498,13 +498,14 @@ void CVCMIServer::loadGame()
 	gh.run(true);
 }
 
-void CVCMIServer::startDuel(const std::string &battle, const std::string &leftAI, const std::string &rightAI)
+void CVCMIServer::startDuel(const std::string &battle, const std::string &leftAI, const std::string &rightAI, int howManyClients)
 {
 
 	//we need three connections
-	boost::thread* threads[3] = {0};
-	CConnection *conns[3] = {0};
-	for (int i = 0; i < 3 ; i++)
+	std::vector<boost::thread*> threads(howManyClients, NULL);
+	std::vector<CConnection*> conns(howManyClients, NULL);
+
+	for (int i = 0; i < howManyClients ; i++)
 	{
 		boost::system::error_code error;
 
@@ -554,9 +555,32 @@ void CVCMIServer::startDuel(const std::string &battle, const std::string &leftAI
 	}
 
 	tlog0 << boost::format("Sending start info to connections!\n");
-	*gh->connections[0] << leftAI << ui8(0);
-	*gh->connections[1] << rightAI << ui8(1);
-	*gh->connections[2] << std::string() << ui8(254);
+	int aisSoFar = 0;
+	for (int i = 0; i < howManyClients ; i++)
+	{
+		tlog0 << "Connection nr " << i;
+		CConnection *c = conns[i];
+		
+		if(c->contactName.find("client") != std::string::npos)
+		{
+			tlog0 << " is a visualization client!\n";
+			*c << std::string() << ui8(254);
+		}
+		else
+		{
+			if(aisSoFar < 2)
+			{
+				tlog0 << " will run " << (aisSoFar ? "right" : "left") << " AI " << std::endl;
+				*c << gh->ais[aisSoFar] << ui8(aisSoFar);
+				aisSoFar++;
+			}
+			else
+			{
+				tlog0 << " will serve as a memory reference.\n";
+				*c << std::string() << ui8(254);
+			}
+		}
+	}
 
 	std::string logFName = "duel_log.vdat";
 	tlog0 << "Logging battle activities (for replay possibility) in " << logFName << std::endl;
@@ -598,10 +622,10 @@ int main(int argc, char** argv)
 	{
 		io_service io_service;
 		CVCMIServer server;
-		if(argc == 4)
-			server.startDuel(argv[1], argv[2], argv[3]);
+		if(argc == 4 || argc == 5)
+			server.startDuel(argv[1], argv[2], argv[3], argc-1);
 		else
-			server.startDuel("b1.json", "StupidAI", "StupidAI");
+			server.startDuel("b1.json", "StupidAI", "StupidAI", 2);
 
 		while(!end2)
 		{

+ 1 - 1
server/CVCMIServer.h

@@ -54,7 +54,7 @@ public:
 	~CVCMIServer(); //d-tor
 
 	void start();
-	void startDuel(const std::string &battle, const std::string &leftAI, const std::string &rightAI);
+	void startDuel(const std::string &battle, const std::string &leftAI, const std::string &rightAI, int howManyClients);
 	CGameHandler *initGhFromHostingConnection(CConnection &c);
 
 	void newGame();