1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +02:00

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)
This commit is contained in:
Michał W. Urbańczyk 2011-10-20 23:29:32 +00:00
parent 51ace689c4
commit 92e508fe1f
16 changed files with 227 additions and 33 deletions

@ -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)

@ -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;

@ -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));

@ -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,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)

@ -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";

11
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
}
]
}

@ -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)

@ -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(...)
{

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

@ -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;
}

@ -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;
}
};

@ -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;

@ -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;

@ -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)
{

@ -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();