main.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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()
  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. CArtifactInstance *artinst = new CArtifactInstance(nowy);
  83. auto &arts = VLC->arth->artifacts;
  84. CArtifactInstance *inny = new CArtifactInstance(VLC->arth->artifacts[15]);
  85. artinst->addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::ARTIFACT_INSTANCE, +25, nowy->id, PrimarySkill::ATTACK));
  86. artinst->addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::ARTIFACT_INSTANCE, +25, nowy->id, PrimarySkill::DEFENSE));
  87. auto bonuses = artinst->getBonuses([](const Bonus *){ return true; });
  88. BOOST_FOREACH(Bonus *b, *bonuses)
  89. {
  90. std::cout << format("%s (%d) value:%d, description: %s\n") % bonusTypeToString(b->type) % b->subtype % b->val % b->Description();
  91. }
  92. return ret;
  93. }
  94. //returns how good the artifact is for the neural network
  95. double runSSN(FANN::neural_net & net, CArtifactInstance * inst)
  96. {
  97. return 0.0;
  98. }
  99. const unsigned int num_input = 2;
  100. double * genSSNinput(const DuelParameters & dp, CArtifactInstance * art)
  101. {
  102. double * ret = new double[num_input];
  103. double * cur = ret;
  104. //general description
  105. *(cur++) = dp.bfieldType;
  106. *(cur++) = dp.terType;
  107. //creature & hero description
  108. for(int i=0; i<2; ++i)
  109. {
  110. auto & side = dp.sides[0];
  111. *(cur++) = side.heroId;
  112. for(int k=0; k<4; ++k)
  113. *(cur++) = side.heroPrimSkills[k];
  114. //weighted average of statistics
  115. auto avg = [&](std::function<int(CCreature *)> getter) -> double
  116. {
  117. double ret;
  118. int div = 0;
  119. for(int i=0; i<7; ++i)
  120. {
  121. auto & cstack = side.stacks[i];
  122. if(cstack.count > 0)
  123. {
  124. ret += getter(VLC->creh->creatures[cstack.type]) * cstack.count;
  125. div+=cstack.count;
  126. }
  127. }
  128. return ret/div;
  129. };
  130. *(cur++) = avg([](CCreature * c){return c->attack;});
  131. *(cur++) = avg([](CCreature * c){return c->defence;});
  132. *(cur++) = avg([](CCreature * c){return c->speed;});
  133. *(cur++) = avg([](CCreature * c){return c->hitPoints;});
  134. }
  135. //bonus description
  136. return ret;
  137. }
  138. void learnSSN(FANN::neural_net & net, const DuelParameters & dp, CArtifactInstance * art, double desiredVal)
  139. {
  140. double * input = genSSNinput(dp, art);
  141. net.train(input, &desiredVal);
  142. delete input;
  143. }
  144. void initNet(FANN::neural_net & ret)
  145. {
  146. const float learning_rate = 0.7f;
  147. const unsigned int num_layers = 3;
  148. const unsigned int num_hidden = 3;
  149. const unsigned int num_output = 1;
  150. const float desired_error = 0.001f;
  151. const unsigned int max_iterations = 300000;
  152. const unsigned int iterations_between_reports = 1000;
  153. ret.create_standard(num_layers, num_input, num_hidden, num_output);
  154. ret.set_learning_rate(learning_rate);
  155. ret.set_activation_steepness_hidden(1.0);
  156. ret.set_activation_steepness_output(1.0);
  157. ret.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
  158. ret.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
  159. ret.randomize_weights(0.0, 1.0);
  160. }
  161. void SSNRun()
  162. {
  163. auto availableArts = genArts();
  164. std::vector<std::pair<CArtifactInstance *, double> > artNotes;
  165. TArtSet setL, setR;
  166. FANN::neural_net network;
  167. initNet(network);
  168. for(int i=0; i<availableArts.size(); ++i)
  169. {
  170. artNotes.push_back(std::make_pair(availableArts[i], runSSN(network, availableArts[i])));
  171. }
  172. boost::range::sort(artNotes,
  173. [](const std::pair<CArtifactInstance *, double> & a1, const std::pair<CArtifactInstance *, double> & a2)
  174. {return a1.second > a2.second;});
  175. //pick best arts into setL
  176. BOOST_FOREACH(auto & ap, artNotes)
  177. {
  178. auto art = ap.first;
  179. BOOST_FOREACH(auto slot, art->artType->possibleSlots)
  180. {
  181. if(setL.find(slot) != setL.end())
  182. {
  183. setL[slot] = art;
  184. break;
  185. }
  186. }
  187. }
  188. //duels to test on
  189. std::vector<DuelParameters> dps;
  190. for(int k = 0; k<10; ++k)
  191. {
  192. DuelParameters dp;
  193. dp.bfieldType = 1;
  194. dp.terType = 1;
  195. auto &side = dp.sides[0];
  196. side.heroId = 0;
  197. side.heroPrimSkills.resize(4,0);
  198. side.stacks[0] = DuelParameters::SideSettings::StackSettings(10+k*3, rand()%30);
  199. dp.sides[1] = side;
  200. dp.sides[1].heroId = 1;
  201. }
  202. std::vector<Bonus> btt; //bonuses to test on
  203. for(int i=0; i<5; ++i)
  204. {
  205. Bonus b;
  206. b.type = Bonus::PRIMARY_SKILL;
  207. b.subtype = PrimarySkill::ATTACK;
  208. b.val = 5 * i + 1;
  209. btt.push_back(b);
  210. b.subtype = PrimarySkill::DEFENSE;
  211. btt.push_back(b);
  212. b.type = Bonus::STACKS_SPEED;
  213. b.subtype = 0;
  214. btt.push_back(b);
  215. b.type = Bonus::STACK_HEALTH;
  216. btt.push_back(b);
  217. }
  218. //evaluate
  219. for(int i=0; i<dps.size(); ++i)
  220. {
  221. auto & dp = dps[i];
  222. double resultLR = cmpArtSets(dp, setL, setR),
  223. resultRL = cmpArtSets(dp, setR, setL),
  224. resultsBase = cmpArtSets(dp, TArtSet(), TArtSet());
  225. double LRgain = resultLR - resultsBase,
  226. RLgain = resultRL - resultsBase;
  227. }
  228. }
  229. int main(int argc, char **argv)
  230. {
  231. std::cout << "VCMI Odpalarka\nMy path: " << argv[0] << std::endl;
  232. po::options_description opts("Allowed options");
  233. opts.add_options()
  234. ("help,h", "Display help and exit")
  235. ("aiLeft,l", po::value<std::string>()->default_value("StupidAI"), "Left AI path")
  236. ("aiRight,r", po::value<std::string>()->default_value("StupidAI"), "Right AI path")
  237. ("battle,b", po::value<std::string>()->default_value("pliczek.ssnb"), "Duel file path")
  238. ("resultsOut,o", po::value<std::string>()->default_value("./results.txt"), "Output file when results will be appended")
  239. ("logsDir,d", po::value<std::string>()->default_value("."), "Directory where log files will be created")
  240. ("visualization,v", "Runs a client to display a visualization of battle");
  241. try
  242. {
  243. po::variables_map vm;
  244. po::store(po::parse_command_line(argc, argv, opts), vm);
  245. po::notify(vm);
  246. if(vm.count("help"))
  247. {
  248. opts.print(std::cout);
  249. prog_help();
  250. return 0;
  251. }
  252. leftAI = vm["aiLeft"].as<std::string>();
  253. rightAI = vm["aiRight"].as<std::string>();
  254. battle = vm["battle"].as<std::string>();
  255. results = vm["resultsOut"].as<std::string>();
  256. logsDir = vm["logsDir"].as<std::string>();
  257. withVisualization = vm.count("visualization");
  258. }
  259. catch(std::exception &e)
  260. {
  261. std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
  262. exit(1);
  263. }
  264. std::cout << "Config:\n" << leftAI << " vs " << rightAI << " on " << battle << std::endl;
  265. if(leftAI.empty() || rightAI.empty() || battle.empty())
  266. {
  267. std::cerr << "I wasn't able to retreive names of AI or battles. Ending.\n";
  268. return 1;
  269. }
  270. runnername =
  271. #ifdef _WIN32
  272. "VCMI_BattleAiHost.exe"
  273. #else
  274. "./vcmirunner"
  275. #endif
  276. ;
  277. servername =
  278. #ifdef _WIN32
  279. "VCMI_server.exe"
  280. #else
  281. "./vcmiserver"
  282. #endif
  283. ;
  284. VLC = new LibClasses();
  285. VLC->init();
  286. SSNRun();
  287. return EXIT_SUCCESS;
  288. }