2021-04-18 11:37:14 +00:00
|
|
|
#include "Runner.hpp"
|
|
|
|
#include <CreatureLib/Battling/TurnChoices/AttackTurnChoice.hpp>
|
|
|
|
#include <PkmnLib/Battling/Battle/Battle.hpp>
|
|
|
|
#include <PkmnLib/Battling/Pokemon/CreatePokemon.hpp>
|
|
|
|
#include <PkmnLib/Battling/Pokemon/PokemonParty.hpp>
|
|
|
|
#include <iomanip>
|
|
|
|
|
|
|
|
void LearnMove(PkmnLib::Battling::CreatePokemon& c, ArbUt::Random& random,
|
|
|
|
ArbUt::BorrowedPtr<const CreatureLib::Library::SpeciesVariant> forme) {
|
|
|
|
auto a = forme->GetLearnableAttacks();
|
|
|
|
auto move = a->GetRandomAttack(random);
|
|
|
|
if (move.has_value()) {
|
|
|
|
c.LearnMove(move.value()->GetName(), CreatureLib::Battling::AttackLearnMethod::Level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PkmnLib::Battling::Pokemon* CreateMon(const PkmnLib::Battling::BattleLibrary* library, ArbUt::Random& random) {
|
|
|
|
auto species = library->GetSpeciesLibrary()->GetRandomValue(random);
|
|
|
|
auto c = PkmnLib::Battling::CreatePokemon(library, species->GetName(), 100);
|
|
|
|
LearnMove(c, random, species->GetVariant("default"_cnc));
|
|
|
|
LearnMove(c, random, species->GetVariant("default"_cnc));
|
|
|
|
LearnMove(c, random, species->GetVariant("default"_cnc));
|
|
|
|
LearnMove(c, random, species->GetVariant("default"_cnc));
|
|
|
|
return c.Build(random);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArbUt::ScopedPtr<PkmnLib::Battling::PokemonParty> CreateParty(const PkmnLib::Battling::BattleLibrary* library,
|
|
|
|
ArbUt::Random& random) {
|
|
|
|
return new PkmnLib::Battling::PokemonParty({
|
|
|
|
CreateMon(library, random),
|
|
|
|
CreateMon(library, random),
|
|
|
|
CreateMon(library, random),
|
|
|
|
CreateMon(library, random),
|
|
|
|
CreateMon(library, random),
|
|
|
|
CreateMon(library, random),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 RunBattle(ArbUt::Random& rand, const PkmnLib::Battling::BattleLibrary* library, PkmnLibAI::PokemonAI* aiOne,
|
|
|
|
PkmnLibAI::PokemonAI* aiTwo) {
|
|
|
|
auto party1 = CreateParty(library, rand);
|
|
|
|
auto party2 = CreateParty(library, rand);
|
|
|
|
auto battle = ArbUt::ScopedPtr<PkmnLib::Battling::Battle>(new PkmnLib::Battling::Battle(
|
|
|
|
library, {
|
|
|
|
new CreatureLib::Battling::BattleParty(party1, {CreatureLib::Battling::CreatureIndex(0, 0)}),
|
|
|
|
new CreatureLib::Battling::BattleParty(party2, {CreatureLib::Battling::CreatureIndex(1, 0)}),
|
|
|
|
}));
|
|
|
|
|
|
|
|
while (!battle->HasEnded()) {
|
|
|
|
if (!battle->GetCreature(0, 0).HasValue() || battle->GetCreature(0, 0).GetValue()->IsFainted()) {
|
|
|
|
for (auto* mon : party1->GetParty()) {
|
|
|
|
if (!mon->IsFainted()) {
|
|
|
|
battle->SwitchCreature(0, 0, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!battle->GetCreature(1, 0).HasValue() || battle->GetCreature(1, 0).GetValue()->IsFainted()) {
|
|
|
|
for (auto* mon : party2->GetParty()) {
|
|
|
|
if (!mon->IsFainted()) {
|
|
|
|
battle->SwitchCreature(1, 0, mon);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto c1 =
|
|
|
|
aiOne->GetChoice(battle, static_cast<PkmnLib::Battling::Pokemon*>(battle->GetCreature(0, 0).GetValue()));
|
|
|
|
auto c2 =
|
|
|
|
aiTwo->GetChoice(battle, static_cast<PkmnLib::Battling::Pokemon*>(battle->GetCreature(1, 0).GetValue()));
|
|
|
|
EnsureNotNull(c1);
|
|
|
|
EnsureNotNull(c2);
|
2021-08-22 10:06:11 +00:00
|
|
|
if (!battle->TrySetChoice(c1)) {
|
|
|
|
if (c1->GetKind() == CreatureLib::Battling::TurnChoiceKind::Attack) {
|
|
|
|
auto a = (CreatureLib::Battling::AttackTurnChoice*)c1;
|
2021-11-21 11:46:30 +00:00
|
|
|
THROW("Failed to set move choice of move: '", a->GetAttack()->GetAttack()->GetName(),
|
|
|
|
"' targeting side ", (i32)a->GetTarget().GetSideIndex(), " and index ",
|
|
|
|
(i32)a->GetTarget().GetCreatureIndex());
|
2021-08-22 10:06:11 +00:00
|
|
|
} else {
|
2021-11-21 11:46:30 +00:00
|
|
|
THROW("Failed to set choice of kind: ",
|
|
|
|
CreatureLib::Battling::TurnChoiceKindHelper::ToString(c1->GetKind()));
|
2021-08-22 10:06:11 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-18 11:37:14 +00:00
|
|
|
Ensure(battle->TrySetChoice(c2));
|
|
|
|
if (battle->GetCurrentTurn() >= 2000) {
|
|
|
|
std::cout << ((CreatureLib::Battling::AttackTurnChoice*)c1)->GetAttack()->GetAttack()->GetName()
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << ((CreatureLib::Battling::AttackTurnChoice*)c2)->GetAttack()->GetAttack()->GetName()
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << battle->GetCreature(0, 0).GetValue()->GetSpecies()->GetName() << std::endl;
|
|
|
|
std::cout << battle->GetCreature(1, 0).GetValue()->GetSpecies()->GetName() << std::endl;
|
|
|
|
}
|
|
|
|
Ensure(battle->GetCurrentTurn() < 2000);
|
|
|
|
}
|
|
|
|
return battle->GetResult().GetWinningSide();
|
|
|
|
}
|
|
|
|
|
|
|
|
#define align std::setw(15) << std::left <<
|
|
|
|
|
|
|
|
void PrintResult(std::string name, size_t wins, size_t total) {
|
|
|
|
std::cout << align name << align wins << wins / (float)total * 100.0 << "%" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Runner::Run(const PkmnLib::Battling::BattleLibrary* library, PkmnLibAI::PokemonAI* aiOne,
|
|
|
|
PkmnLibAI::PokemonAI* aiTwo, size_t runs) {
|
|
|
|
ArbUt::Random rand;
|
|
|
|
std::vector<size_t> wins = {0, 0};
|
|
|
|
auto startTime = std::chrono::steady_clock::now();
|
|
|
|
std::cout << "Running 0/" << runs << " battles (0/0)";
|
|
|
|
for (size_t i = 0; i < runs; ++i) {
|
|
|
|
std::cout << "\rRunning " << i + 1 << "/" << runs << " battles (" << wins[0] / (float)i << "/"
|
|
|
|
<< wins[1] / (float)i << ")" << std::flush;
|
|
|
|
auto result = RunBattle(rand, library, aiOne, aiTwo);
|
|
|
|
wins[result]++;
|
|
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
auto endTime = std::chrono::steady_clock::now();
|
|
|
|
std::cout << "Ran " << runs << " battles in "
|
|
|
|
<< duration_cast<std::chrono::milliseconds>((endTime - startTime)).count()
|
|
|
|
<< " ms. Results:" << std::endl;
|
|
|
|
|
|
|
|
std::cout << align "AI Name" << align "Wins" << align "Percentage" << std::endl;
|
|
|
|
PrintResult(aiOne->GetName(), wins[0], runs);
|
|
|
|
PrintResult(aiTwo->GetName(), wins[1], runs);
|
|
|
|
}
|