From 47a27960a223e9aa66d4026c03fc400e561f8a77 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 22 Aug 2021 12:06:11 +0200 Subject: [PATCH] Deal with setting targets --- CMakeLists.txt | 5 +- src/DepthSearchAI.hpp | 112 +++++++++++++++++++------ test_runner/BuildData/BuildLibrary.hpp | 61 ++++++++++++++ test_runner/Runner.cpp | 12 ++- test_runner/main.cpp | 54 ++---------- 5 files changed, 165 insertions(+), 79 deletions(-) create mode 100644 test_runner/BuildData/BuildLibrary.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 41d6f4b..b0b70d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,9 +32,6 @@ endif (WINDOWS) SET(_LINKS Arbutils CreatureLib pkmnLib -lpthread) -if (NOT WINDOWS) - SET(_LINKS ${LINKS} -lbfd -ldl) -endif() target_link_libraries(pkmnlib_ai PUBLIC ${_LINKS}) target_compile_options(pkmnlib_ai PRIVATE -Wall -Wextra -Werror) @@ -43,5 +40,5 @@ if (AIRUNNER) file(GLOB_RECURSE RUNNER_SRC_FILES test_runner/*.cpp test_runner/*.hpp) add_executable(pkmnlib_ai_runner ${RUNNER_SRC_FILES}) target_compile_options(pkmnlib_ai_runner PRIVATE -Wall -Wextra -Werror) - target_link_libraries(pkmnlib_ai_runner PUBLIC pkmnlib_ai) + target_link_libraries(pkmnlib_ai_runner PUBLIC pkmnlib_ai -lpthread) endif() \ No newline at end of file diff --git a/src/DepthSearchAI.hpp b/src/DepthSearchAI.hpp index b1e5d60..235014d 100644 --- a/src/DepthSearchAI.hpp +++ b/src/DepthSearchAI.hpp @@ -17,6 +17,27 @@ namespace PkmnLibAI { NaiveAI _naive; private: + class SimulatedResult { + public: + std::optional Score; + CreatureLib::Battling::CreatureIndex Target; + + SimulatedResult(std::optional score, CreatureLib::Battling::CreatureIndex target) + : Score(score), Target(target) {} + SimulatedResult() {} + }; + + class ScoredOption { + public: + u8 OptionIndex; + std::optional Score; + CreatureLib::Battling::CreatureIndex Target; + + ScoredOption(u8 optionIndex, SimulatedResult result) + : OptionIndex(optionIndex), Score(result.Score), Target(result.Target) {} + ScoredOption() {} + }; + float ScoreBattle(PkmnLib::Battling::Battle* battle, PkmnLib::Battling::Pokemon* user) { auto side = user->GetBattleSide().GetValue(); if (battle->HasEnded()) { @@ -62,9 +83,9 @@ namespace PkmnLibAI { } }; - std::vector> ScoreChoicesThreaded(PkmnLib::Battling::Battle* battle, - PkmnLib::Battling::Pokemon* user, uint8_t depth) { - std::vector>> threadPool; + std::vector ScoreChoicesThreaded(PkmnLib::Battling::Battle* battle, + PkmnLib::Battling::Pokemon* user, uint8_t depth) { + std::vector> threadPool; auto side = user->GetBattleSide().GetValue(); for (u8 moveIndex = 0; moveIndex < (u8)user->GetMoves().Count(); ++moveIndex) { auto move = user->GetMoves()[moveIndex]; @@ -75,7 +96,7 @@ namespace PkmnLibAI { continue; } threadPool.push_back(std::async([this, battle, side, moveIndex, depth] { - auto v = std::tuple(moveIndex, SimulateTurn(battle, side->GetSideIndex(), 0, moveIndex, depth)); + auto v = ScoredOption(moveIndex, SimulateTurn(battle, side->GetSideIndex(), 0, moveIndex, depth)); asThreadCleanup(); return v; })); @@ -90,21 +111,21 @@ namespace PkmnLibAI { continue; } threadPool.push_back(std::async([this, battle, side, i, depth] { - auto v = std::tuple((u8)(i + 4), SimulateTurn(battle, side->GetSideIndex(), 0, i + 4, depth)); + auto v = ScoredOption((u8)(i + 4), SimulateTurn(battle, side->GetSideIndex(), 0, i + 4, depth)); asThreadCleanup(); return v; })); } - std::vector> results(threadPool.size()); + std::vector results(threadPool.size()); for (size_t i = 0; i < threadPool.size(); ++i) { results[i] = threadPool[i].get(); } return results; } - std::vector> ScoreChoices(PkmnLib::Battling::Battle* battle, - PkmnLib::Battling::Pokemon* user, uint8_t depth) { - std::vector> scoredMoves; + std::vector ScoreChoices(PkmnLib::Battling::Battle* battle, PkmnLib::Battling::Pokemon* user, + uint8_t depth) { + std::vector scoredMoves; auto side = user->GetBattleSide().GetValue(); for (u8 moveIndex = 0; moveIndex < (u8)user->GetMoves().Count(); ++moveIndex) { auto move = user->GetMoves()[moveIndex]; @@ -133,32 +154,60 @@ namespace PkmnLibAI { return scoredMoves; } - float SimulateTurn(PkmnLib::Battling::Battle* originalBattle, u8 sideIndex, u8 pokemonIndex, u8 index, - u8 depth) { + std::optional + GetTarget(PkmnLib::Battling::Pokemon* user, ArbUt::BorrowedPtr move) { + switch (move->GetTarget()) { + + case CreatureLib::Library::AttackTarget::Adjacent: return {}; + case CreatureLib::Library::AttackTarget::AdjacentAlly: return {}; + case CreatureLib::Library::AttackTarget::AdjacentAllySelf: return user->GetBattleIndex(); + case CreatureLib::Library::AttackTarget::AdjacentOpponent: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::All: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::AllAdjacent: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::AllAdjacentOpponent: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::AllAlly: return user->GetBattleIndex(); + case CreatureLib::Library::AttackTarget::AllOpponent: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::Any: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::RandomOpponent: return GetOppositeIndex(user); + case CreatureLib::Library::AttackTarget::Self: return user->GetBattleIndex(); + } + } + + SimulatedResult SimulateTurn(PkmnLib::Battling::Battle* originalBattle, u8 sideIndex, u8 pokemonIndex, u8 index, + u8 depth) { auto battle = BattlePointerWrapper(originalBattle->Clone()); auto user = dynamic_cast(battle->GetCreature(sideIndex, pokemonIndex).GetValue()); - auto target = GetOppositeIndex(user); + CreatureLib::Battling::CreatureIndex target; if (index < 4) { auto move = user->GetMoves()[index]; if (!move.HasValue()) { - return std::numeric_limits::min(); + return {}; } + if (move.GetValue()->GetRemainingUses() <= 0) { + return {}; + } + auto targetOption = GetTarget(user, move.GetValue()->GetAttack()); + if (!targetOption.has_value()) { + return {}; + } + target = targetOption.value(); auto choice = new CreatureLib::Battling::AttackTurnChoice(user, move.GetValue(), target); if (!battle->TrySetChoice(choice)) { delete choice; - return std::numeric_limits::min(); + return {}; } } else { auto mon = battle->GetParties()[sideIndex]->GetParty()->GetParty().At(index - 4); auto choice = new CreatureLib::Battling::SwitchTurnChoice(user, mon); if (!battle->TrySetChoice(choice)) { delete choice; - return std::numeric_limits::min(); + return {}; } } battle->TrySetChoice(_naive.GetChoice( - battle.Battle, dynamic_cast(battle->GetCreature(target).GetValue()))); + battle.Battle, + dynamic_cast(battle->GetCreature(GetOppositeIndex(user)).GetValue()))); float score; if (depth <= 1) { @@ -168,16 +217,20 @@ namespace PkmnLibAI { float summedScore = 0; size_t amount = 0; for (auto& option : scoredChoices) { - summedScore += std::get<1>(option); + auto v = option.Score; + if (!v.has_value()) { + continue; + } + summedScore += v.value(); amount++; } if (amount == 0) { - return std::numeric_limits::min(); + return {}; } score = summedScore / amount; } - return score; + return SimulatedResult(score, target); } public: @@ -189,25 +242,32 @@ namespace PkmnLibAI { auto side = user->GetBattleSide().GetValue(); auto& party = battle->GetParties()[side->GetSideIndex()]->GetParty()->GetParty(); - auto target = GetOppositeIndex(user); if (scoredChoices.empty()) { - return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, target); + return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, GetOppositeIndex(user)); } i32 highest = -1; float highestScore = -std::numeric_limits::infinity(); + CreatureLib::Battling::CreatureIndex highestTarget; + for (auto& option : scoredChoices) { - if (std::get<1>(option) > highestScore) { - highestScore = std::get<1>(option); - highest = std::get<0>(option); + auto v = option.Score; + if (!v.has_value()) { + continue; + } + if (v.value() > highestScore) { + highestScore = v.value(); + highest = option.OptionIndex; + highestTarget = option.Target; } } if (highest == -1) { - return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, target); + return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, GetOppositeIndex(user)); } if (highest < 4) { - return new CreatureLib::Battling::AttackTurnChoice(user, user->GetMoves()[highest].GetValue(), target); + return new CreatureLib::Battling::AttackTurnChoice(user, user->GetMoves()[highest].GetValue(), + highestTarget); } else { return new CreatureLib::Battling::SwitchTurnChoice(user, party.At(highest - 4)); } diff --git a/test_runner/BuildData/BuildLibrary.hpp b/test_runner/BuildData/BuildLibrary.hpp new file mode 100644 index 0000000..5f52e5c --- /dev/null +++ b/test_runner/BuildData/BuildLibrary.hpp @@ -0,0 +1,61 @@ +#ifndef PKMNLIB_AI_BUILDLIBRARY_HPP +#define PKMNLIB_AI_BUILDLIBRARY_HPP + +#include +#include +#include "BuildMoves.hpp" +#include "BuildNatures.hpp" +#include "BuildSpecies.hpp" +#include "BuildTypes.hpp" +#include "GrowthRatesBuilder.hpp" + +class BuildLibrary { + static PkmnLib::Library::TimeOfDay GetTime() { return PkmnLib::Library::TimeOfDay::Morning; } + +public: + static PkmnLib::Battling::BattleLibrary* Build(const std::string& pathString) { + auto path = std::filesystem::path(pathString); + auto* typesLibrary = BuildTypes::Build(path / "Types.csv"); + auto* natureLibrary = BuildNatures::Build(path / "Natures.csv"); + auto* movesLibrary = BuildMoves::Build(path / "Moves.json", typesLibrary); + auto* speciesLibrary = BuildSpecies::BuildLibrary(path / "Pokemon.json", typesLibrary, movesLibrary); + auto* itemsLibrary = BuildItems::Build(path / "Items.json"); + auto* growthRates = GrowthRatesBuilder::Build(path / "GrowthRates.json"); + auto scriptsPath = path / "Scripts"; + + if (typesLibrary == nullptr || speciesLibrary == nullptr || natureLibrary == nullptr || + movesLibrary == nullptr || itemsLibrary == nullptr || growthRates == nullptr) { + return nullptr; + } + + auto settings = new PkmnLib::Library::LibrarySettings(100, 4, 4096); + auto staticLibrary = new PkmnLib::Library::PokemonLibrary(settings, speciesLibrary, movesLibrary, itemsLibrary, + growthRates, typesLibrary, natureLibrary); + + auto scriptResolver = PkmnLib::Battling::BattleLibrary::CreateScriptResolver(); + + auto battleLib = new PkmnLib::Battling::BattleLibrary( + staticLibrary, new PkmnLib::Battling::StatCalculator(), new PkmnLib::Battling::DamageLibrary(), + new PkmnLib::Battling::ExperienceLibrary(), scriptResolver, new PkmnLib::Battling::MiscLibrary(GetTime)); + + scriptResolver->Initialize(battleLib); + + auto asScriptResolver = dynamic_cast(scriptResolver); + + for (const auto& dirEntry : std::filesystem::recursive_directory_iterator(scriptsPath)) { + if (dirEntry.is_directory()) + continue; + if (dirEntry.path().parent_path().stem() == "Interfaces") + continue; + if (dirEntry.path().extension() != ".as") + continue; + std::ifstream in(dirEntry.path()); + std::string contents((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + asScriptResolver->CreateScript(dirEntry.path().c_str(), contents.c_str()); + } + asScriptResolver->FinalizeModule(); + return battleLib; + } +}; + +#endif // PKMNLIB_AI_BUILDLIBRARY_HPP diff --git a/test_runner/Runner.cpp b/test_runner/Runner.cpp index 1dbd398..48fc554 100644 --- a/test_runner/Runner.cpp +++ b/test_runner/Runner.cpp @@ -70,7 +70,17 @@ u8 RunBattle(ArbUt::Random& rand, const PkmnLib::Battling::BattleLibrary* librar aiTwo->GetChoice(battle, static_cast(battle->GetCreature(1, 0).GetValue())); EnsureNotNull(c1); EnsureNotNull(c2); - Ensure(battle->TrySetChoice(c1)); + if (!battle->TrySetChoice(c1)) { + if (c1->GetKind() == CreatureLib::Battling::TurnChoiceKind::Attack) { + auto a = (CreatureLib::Battling::AttackTurnChoice*)c1; + THROW("Failed to set move choice of move: '" + << a->GetAttack()->GetAttack()->GetName() << "' targeting side " + << (i32)a->GetTarget().GetSideIndex() << " and index " << (i32)a->GetTarget().GetCreatureIndex()); + } else { + THROW("Failed to set choice of kind: " + << CreatureLib::Battling::TurnChoiceKindHelper::ToString(c1->GetKind())); + } + } Ensure(battle->TrySetChoice(c2)); if (battle->GetCurrentTurn() >= 2000) { std::cout << ((CreatureLib::Battling::AttackTurnChoice*)c1)->GetAttack()->GetAttack()->GetName() diff --git a/test_runner/main.cpp b/test_runner/main.cpp index a93d9ee..7c9de4d 100644 --- a/test_runner/main.cpp +++ b/test_runner/main.cpp @@ -4,6 +4,7 @@ #include "../extern/args.hpp" #include "AIResolver.hpp" #include "BuildData/BuildItems.hpp" +#include "BuildData/BuildLibrary.hpp" #include "BuildData/BuildMoves.hpp" #include "BuildData/BuildNatures.hpp" #include "BuildData/BuildSpecies.hpp" @@ -11,20 +12,11 @@ #include "BuildData/GrowthRatesBuilder.hpp" #include "Runner.hpp" -static const char* ScriptsPath = nullptr; - int main(int argc, char** argv) { args::ArgumentParser parser("PkmnLib AI Runner.", ""); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); std::string workingDirectory; - std::string typesFile = "Types.csv"; - std::string naturesFile = "Natures.csv"; - std::string pokemonFile = "Pokemon.json"; - std::string moveFile = "Moves.json"; - std::string itemsFile = "Items.json"; - std::string growthRatesFile = "GrowthRates.json"; - std::string scriptsPath = "Scripts"; args::HelpFlag helpFlag(parser, "help", "Display this help menu", {'h', "help"}); args::ValueFlag workingDirFlag(parser, "Working Directory", "Which work directory to use.", @@ -52,47 +44,11 @@ int main(int argc, char** argv) { if (!workingDirectory.empty()) { chdir(std::filesystem::path(workingDirectory).c_str()); } - - auto* typesLibrary = BuildTypes::Build(typesFile); - auto* natureLibrary = BuildNatures::Build(naturesFile); - auto* movesLibrary = BuildMoves::Build(moveFile, typesLibrary); - auto* speciesLibrary = BuildSpecies::BuildLibrary(pokemonFile, typesLibrary, movesLibrary); - auto* itemsLibrary = BuildItems::Build(itemsFile); - auto* growthRates = GrowthRatesBuilder::Build(growthRatesFile); - ScriptsPath = scriptsPath.c_str(); - - if (typesLibrary == nullptr || speciesLibrary == nullptr || natureLibrary == nullptr || movesLibrary == nullptr || - itemsLibrary == nullptr || growthRates == nullptr) { + auto battleLib = BuildLibrary::Build(""); + if (battleLib == nullptr) { return 1; } - auto settings = new PkmnLib::Library::LibrarySettings(100, 4, 4096); - auto staticLibrary = new PkmnLib::Library::PokemonLibrary(settings, speciesLibrary, movesLibrary, itemsLibrary, - growthRates, typesLibrary, natureLibrary); - - auto scriptResolver = PkmnLib::Battling::BattleLibrary::CreateScriptResolver(); - - auto battleLib = new PkmnLib::Battling::BattleLibrary( - staticLibrary, new PkmnLib::Battling::StatCalculator(), new PkmnLib::Battling::DamageLibrary(), - new PkmnLib::Battling::ExperienceLibrary(), scriptResolver, new PkmnLib::Battling::MiscLibrary()); - - scriptResolver->Initialize(battleLib); - - auto asScriptResolver = dynamic_cast(scriptResolver); - - for (const auto& dirEntry : std::filesystem::recursive_directory_iterator(ScriptsPath)) { - if (dirEntry.is_directory()) - continue; - if (dirEntry.path().parent_path().stem() == "Interfaces") - continue; - if (dirEntry.path().extension() != ".as") - continue; - std::ifstream in(dirEntry.path()); - std::string contents((std::istreambuf_iterator(in)), std::istreambuf_iterator()); - asScriptResolver->CreateScript(dirEntry.path().c_str(), contents.c_str()); - } - asScriptResolver->FinalizeModule(); - auto ai1 = AIResolver::Resolve(ArbUt::StringView(ai1Flag.Get().c_str(), ai1Flag.Get().size())); auto ai2 = AIResolver::Resolve(ArbUt::StringView(ai2Flag.Get().c_str(), ai1Flag.Get().size())); @@ -100,7 +56,9 @@ int main(int argc, char** argv) { Runner::Run(battleLib, ai1, ai2, runsFlag.Get()); } catch (ArbUt::Exception& e) { std::cout << std::endl; - std::cout << "Exception with message: " << std::endl << e.what() << std::endl << e.GetStacktrace(10) << std::endl; + std::cout << "Exception with message: " << std::endl + << e.what() << std::endl + << e.GetStacktrace(10) << std::endl; return 1; }