main.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. //#include "../global.h"
  2. #include "StdInc.h"
  3. #include "../lib/VCMI_Lib.h"
  4. namespace po = boost::program_options;
  5. //FANN
  6. #include <doublefann.h>
  7. #include <fann_cpp.h>
  8. std::string leftAI, rightAI, battle, results, logsDir;
  9. bool withVisualization = false;
  10. std::string servername;
  11. std::string runnername;
  12. extern DLL_EXPORT LibClasses * VLC;
  13. std::string addQuotesIfNeeded(const std::string &s)
  14. {
  15. if(s.find_first_of(' ') != std::string::npos)
  16. return "\"" + s + "\"";
  17. return s;
  18. }
  19. void prog_help()
  20. {
  21. std::cout << "If run without args, then StupidAI will be run on b1.json.\n";
  22. }
  23. void runCommand(const std::string &command, const std::string &name, const std::string &logsDir = "")
  24. {
  25. static std::string commands[100];
  26. static int i = 0;
  27. std::string &cmd = commands[i++];
  28. if(logsDir.size() && name.size())
  29. {
  30. std::string directionLogs = logsDir + "/" + name + ".txt";
  31. cmd = command + " > " + addQuotesIfNeeded(directionLogs);
  32. }
  33. else
  34. cmd = command;
  35. boost::thread tt(boost::bind(std::system, cmd.c_str()));
  36. }
  37. double playBattle(const DuelParameters &dp)
  38. {
  39. {
  40. CSaveFile out("pliczek.ssnb");
  41. out << dp;
  42. }
  43. std::string serverCommand = servername + " " + addQuotesIfNeeded(battle) + " " + addQuotesIfNeeded(leftAI) + " " + addQuotesIfNeeded(rightAI) + " " + addQuotesIfNeeded(results) + " " + addQuotesIfNeeded(logsDir) + " " + (withVisualization ? " v" : "");
  44. std::string runnerCommand = runnername + " " + addQuotesIfNeeded(logsDir);
  45. std::cout <<"Server command: " << serverCommand << std::endl << "Runner command: " << runnerCommand << std::endl;
  46. int code = 0;
  47. boost::thread t([&]
  48. {
  49. code = std::system(serverCommand.c_str());
  50. });
  51. runCommand(runnerCommand, "first_runner", logsDir);
  52. runCommand(runnerCommand, "second_runner", logsDir);
  53. runCommand(runnerCommand, "third_runner", logsDir);
  54. if(withVisualization)
  55. {
  56. //boost::this_thread::sleep(boost::posix_time::millisec(500)); //FIXME
  57. boost::thread tttt(boost::bind(std::system, "VCMI_Client.exe -battle"));
  58. }
  59. //boost::this_thread::sleep(boost::posix_time::seconds(5));
  60. t.join();
  61. return code / 1000000.0;
  62. }
  63. typedef std::map<int, CArtifactInstance*> TArtSet;
  64. double cmpArtSets(DuelParameters dp, TArtSet setL, TArtSet setR)
  65. {
  66. //lewa strona z art 0.9
  67. //bez artefaktow -0.41
  68. //prawa strona z art. -0.926
  69. dp.sides[0].artifacts = setL;
  70. dp.sides[1].artifacts = setR;
  71. auto battleOutcome = playBattle(dp);
  72. return battleOutcome;
  73. }
  74. std::vector<CArtifactInstance*> genArts(const std::vector<Bonus> & bonusesToGive)
  75. {
  76. std::vector<CArtifactInstance*> ret;
  77. CArtifact *nowy = new CArtifact();
  78. nowy->description = "Cudowny miecz Towa gwarantuje zwyciestwo";
  79. nowy->name = "Cudowny miecz";
  80. nowy->constituentOf = nowy->constituents = NULL;
  81. nowy->possibleSlots.push_back(Arts::LEFT_HAND);
  82. BOOST_FOREACH(auto b, bonusesToGive)
  83. {
  84. CArtifactInstance *artinst = new CArtifactInstance(nowy);
  85. auto &arts = VLC->arth->artifacts;
  86. artinst->addNewBonus(new Bonus(b));
  87. ret.push_back(artinst);
  88. }
  89. // auto bonuses = artinst->getBonuses([](const Bonus *){ return true; });
  90. // BOOST_FOREACH(Bonus *b, *bonuses)
  91. // {
  92. // std::cout << format("%s (%d) value:%d, description: %s\n") % bonusTypeToString(b->type) % b->subtype % b->val % b->Description();
  93. // }
  94. return ret;
  95. }
  96. //returns how good the artifact is for the neural network
  97. double runSSN(FANN::neural_net & net, const DuelParameters dp, CArtifactInstance * inst)
  98. {
  99. TArtSet setL, setR;
  100. setL[inst->artType->possibleSlots[0]] = inst;
  101. double resultLR = cmpArtSets(dp, setL, setR),
  102. resultRL = cmpArtSets(dp, setR, setL),
  103. resultsBase = cmpArtSets(dp, TArtSet(), TArtSet());
  104. double LRgain = resultLR - resultsBase,
  105. RLgain = resultRL - resultsBase;
  106. return LRgain+RLgain;
  107. }
  108. const unsigned int num_input = 16;
  109. double * genSSNinput(const DuelParameters & dp, CArtifactInstance * art)
  110. {
  111. double * ret = new double[num_input];
  112. double * cur = ret;
  113. //general description
  114. *(cur++) = dp.bfieldType;
  115. *(cur++) = dp.terType;
  116. //creature & hero description
  117. for(int i=0; i<2; ++i)
  118. {
  119. auto & side = dp.sides[0];
  120. *(cur++) = side.heroId;
  121. for(int k=0; k<4; ++k)
  122. *(cur++) = side.heroPrimSkills[k];
  123. //weighted average of statistics
  124. auto avg = [&](std::function<int(CCreature *)> getter) -> double
  125. {
  126. double ret;
  127. int div = 0;
  128. for(int i=0; i<7; ++i)
  129. {
  130. auto & cstack = side.stacks[i];
  131. if(cstack.count > 0)
  132. {
  133. ret += getter(VLC->creh->creatures[cstack.type]) * cstack.count;
  134. div+=cstack.count;
  135. }
  136. }
  137. return ret/div;
  138. };
  139. *(cur++) = avg([](CCreature * c){return c->attack;});
  140. *(cur++) = avg([](CCreature * c){return c->defence;});
  141. *(cur++) = avg([](CCreature * c){return c->speed;});
  142. *(cur++) = avg([](CCreature * c){return c->hitPoints;});
  143. }
  144. //bonus description
  145. auto & blist = art->getBonusList();
  146. *(cur++) = art->Attack();
  147. *(cur++) = art->Defense();
  148. *(cur++) = blist.valOfBonuses(Selector::type(Bonus::STACKS_SPEED));
  149. *(cur++) = blist.valOfBonuses(Selector::type(Bonus::STACK_HEALTH));
  150. return ret;
  151. }
  152. void learnSSN(FANN::neural_net & net, const std::vector<std::pair<DuelParameters, CArtifactInstance *> > & input)
  153. {
  154. FANN::training_data td;
  155. double ** inputs = new double *[input.size()];
  156. double ** outputs = new double *[input.size()];
  157. for(int i=0; i<input.size(); ++i)
  158. {
  159. inputs[i] = genSSNinput(input[i].first, input[i].second);
  160. outputs[i] = new double;
  161. *(outputs[i]) = runSSN(net, input[i].first, input[i].second);
  162. }
  163. td.set_train_data(input.size(), num_input, inputs, 1, outputs);
  164. net.train_epoch(td);
  165. }
  166. void initNet(FANN::neural_net & ret)
  167. {
  168. const float learning_rate = 0.7f;
  169. const unsigned int num_layers = 3;
  170. const unsigned int num_hidden = 3;
  171. const unsigned int num_output = 1;
  172. const float desired_error = 0.001f;
  173. const unsigned int max_iterations = 300000;
  174. const unsigned int iterations_between_reports = 1000;
  175. ret.create_standard(num_layers, num_input, num_hidden, num_output);
  176. ret.set_learning_rate(learning_rate);
  177. ret.set_activation_steepness_hidden(1.0);
  178. ret.set_activation_steepness_output(1.0);
  179. ret.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
  180. ret.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
  181. ret.randomize_weights(0.0, 1.0);
  182. }
  183. void SSNRun()
  184. {
  185. std::vector<std::pair<CArtifactInstance *, double> > artNotes;
  186. TArtSet setL, setR;
  187. FANN::neural_net network;
  188. initNet(network);
  189. // for(int i=0; i<availableArts.size(); ++i)
  190. // {
  191. // artNotes.push_back(std::make_pair(availableArts[i], runSSN(network, availableArts[i])));
  192. // }
  193. // boost::range::sort(artNotes,
  194. // [](const std::pair<CArtifactInstance *, double> & a1, const std::pair<CArtifactInstance *, double> & a2)
  195. // {return a1.second > a2.second;});
  196. //
  197. // //pick best arts into setL
  198. // BOOST_FOREACH(auto & ap, artNotes)
  199. // {
  200. // auto art = ap.first;
  201. // BOOST_FOREACH(auto slot, art->artType->possibleSlots)
  202. // {
  203. // if(setL.find(slot) != setL.end())
  204. // {
  205. // setL[slot] = art;
  206. // break;
  207. // }
  208. // }
  209. // }
  210. //duels to test on
  211. std::vector<DuelParameters> dps;
  212. for(int k = 0; k<10; ++k)
  213. {
  214. DuelParameters dp;
  215. dp.bfieldType = 1;
  216. dp.terType = 1;
  217. auto &side = dp.sides[0];
  218. side.heroId = 0;
  219. side.heroPrimSkills.resize(4,0);
  220. side.stacks[0] = DuelParameters::SideSettings::StackSettings(10+k*3, rand()%30);
  221. dp.sides[1] = side;
  222. dp.sides[1].heroId = 1;
  223. }
  224. std::vector<Bonus> btt; //bonuses to test on
  225. for(int i=0; i<5; ++i)
  226. {
  227. Bonus b;
  228. b.type = Bonus::PRIMARY_SKILL;
  229. b.subtype = PrimarySkill::ATTACK;
  230. b.val = 5 * i + 1;
  231. btt.push_back(b);
  232. b.subtype = PrimarySkill::DEFENSE;
  233. btt.push_back(b);
  234. b.type = Bonus::STACKS_SPEED;
  235. b.subtype = 0;
  236. btt.push_back(b);
  237. b.type = Bonus::STACK_HEALTH;
  238. btt.push_back(b);
  239. }
  240. auto arts = genArts(btt);
  241. //evaluate
  242. std::vector<std::pair<DuelParameters, CArtifactInstance *> > setups;
  243. for(int i=0; i<dps.size(); ++i)
  244. {
  245. for(int j=0; j<arts.size(); ++j)
  246. {
  247. setups.push_back(std::make_pair(dps[i], arts[j]));
  248. }
  249. }
  250. learnSSN(network, setups);
  251. }
  252. int main(int argc, char **argv)
  253. {
  254. std::cout << "VCMI Odpalarka\nMy path: " << argv[0] << std::endl;
  255. po::options_description opts("Allowed options");
  256. opts.add_options()
  257. ("help,h", "Display help and exit")
  258. ("aiLeft,l", po::value<std::string>()->default_value("StupidAI"), "Left AI path")
  259. ("aiRight,r", po::value<std::string>()->default_value("StupidAI"), "Right AI path")
  260. ("battle,b", po::value<std::string>()->default_value("pliczek.ssnb"), "Duel file path")
  261. ("resultsOut,o", po::value<std::string>()->default_value("./results.txt"), "Output file when results will be appended")
  262. ("logsDir,d", po::value<std::string>()->default_value("."), "Directory where log files will be created")
  263. ("visualization,v", "Runs a client to display a visualization of battle");
  264. try
  265. {
  266. po::variables_map vm;
  267. po::store(po::parse_command_line(argc, argv, opts), vm);
  268. po::notify(vm);
  269. if(vm.count("help"))
  270. {
  271. opts.print(std::cout);
  272. prog_help();
  273. return 0;
  274. }
  275. leftAI = vm["aiLeft"].as<std::string>();
  276. rightAI = vm["aiRight"].as<std::string>();
  277. battle = vm["battle"].as<std::string>();
  278. results = vm["resultsOut"].as<std::string>();
  279. logsDir = vm["logsDir"].as<std::string>();
  280. withVisualization = vm.count("visualization");
  281. }
  282. catch(std::exception &e)
  283. {
  284. std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
  285. exit(1);
  286. }
  287. std::cout << "Config:\n" << leftAI << " vs " << rightAI << " on " << battle << std::endl;
  288. if(leftAI.empty() || rightAI.empty() || battle.empty())
  289. {
  290. std::cerr << "I wasn't able to retreive names of AI or battles. Ending.\n";
  291. return 1;
  292. }
  293. runnername =
  294. #ifdef _WIN32
  295. "VCMI_BattleAiHost.exe"
  296. #else
  297. "./vcmirunner"
  298. #endif
  299. ;
  300. servername =
  301. #ifdef _WIN32
  302. "VCMI_server.exe"
  303. #else
  304. "./vcmiserver"
  305. #endif
  306. ;
  307. VLC = new LibClasses();
  308. VLC->init();
  309. SSNRun();
  310. return EXIT_SUCCESS;
  311. }