Initial Commit.
This commit is contained in:
commit
554879a8c2
|
@ -0,0 +1,124 @@
|
||||||
|
# ClangFormatConfigureSource: 'LLVM'
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 120
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Merge
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: All
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Left
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
- Ensure
|
||||||
|
- EnsureNotNull
|
||||||
|
- Try
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
...
|
|
@ -0,0 +1,11 @@
|
||||||
|
Checks: 'readability-*,clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,performance-*,cppcoreguidelines-*,
|
||||||
|
bugprone-*,modernize-*,-modernize-use-trailing-return-type,-cppcoreguidelines-non-private-member-variables-in-classes'
|
||||||
|
HeaderFilterRegex: ''
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
|
CheckOptions:
|
||||||
|
- key: readability-identifier-naming.ClassCase
|
||||||
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.PrivateMemberCase
|
||||||
|
value: camelBack
|
||||||
|
- key: readability-identifier-naming.PrivateMemberPrefix
|
||||||
|
value: '_'
|
|
@ -0,0 +1,5 @@
|
||||||
|
/cmake-build-debug/
|
||||||
|
/cmake-build-release/
|
||||||
|
/build-release-windows/
|
||||||
|
/.idea/
|
||||||
|
/docs/
|
|
@ -0,0 +1,25 @@
|
||||||
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
project(pkmnlib_ai)
|
||||||
|
|
||||||
|
# Enable all warnings, and make them error when occurring.
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
set(STATICC TRUE)
|
||||||
|
|
||||||
|
include(CMakeLists.txt.in)
|
||||||
|
include_pkmnlib()
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC_FILES src/*.cpp src/*.hpp)
|
||||||
|
add_library(pkmnlib_ai SHARED ${SRC_FILES})
|
||||||
|
target_precompile_headers(pkmnlib_ai PUBLIC src/Precompiled.hxx)
|
||||||
|
set_target_properties(pkmnlib_ai PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
|
add_definitions(-DLEVEL_U8)
|
||||||
|
|
||||||
|
SET(_LINKS Arbutils CreatureLib pkmnLib -lbfd -ldl -lpthread)
|
||||||
|
target_link_libraries(pkmnlib_ai PUBLIC ${_LINKS})
|
||||||
|
target_compile_options(pkmnlib_ai PRIVATE -Wall -Wextra -Werror -fstandalone-debug)
|
||||||
|
|
||||||
|
|
||||||
|
file(GLOB_RECURSE RUNNER_SRC_FILES test_runner/*.cpp test_runner/*.hpp)
|
||||||
|
add_executable(pkmnlib_ai_runner ${RUNNER_SRC_FILES})
|
||||||
|
target_link_libraries(pkmnlib_ai_runner PUBLIC ${_LINKS} pkmnlib_ai)
|
|
@ -0,0 +1,50 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8.12)
|
||||||
|
|
||||||
|
project(pkmnlib_ai NONE)
|
||||||
|
|
||||||
|
include(ExternalProject)
|
||||||
|
ExternalProject_Add(pkmnlib
|
||||||
|
GIT_REPOSITORY https://git.p-epsilon.com/Deukhoofd/PkmnLib
|
||||||
|
GIT_TAG master
|
||||||
|
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/PkmnLib"
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND ""
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
TEST_COMMAND ""
|
||||||
|
CMAKE_ARGS "-DSHARED=${SHARED} -DWINDOWS=${WINDOWS} -DSTATICC=${STATICC}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
function(include_pkmnlib)
|
||||||
|
# Download and unpack googletest at configure time
|
||||||
|
configure_file(CMakeLists.txt.in PkmnLib/download/CMakeLists.txt)
|
||||||
|
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/download)
|
||||||
|
if (result)
|
||||||
|
message(FATAL_ERROR "CMake step for pkmnlib failed: ${result}")
|
||||||
|
endif ()
|
||||||
|
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/download)
|
||||||
|
if (result)
|
||||||
|
message(FATAL_ERROR "Build step for pkmnlib failed: ${result}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Add googletest directly to our build. This defines
|
||||||
|
# the gtest and gtest_main targets.
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/src/pkmnlib
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/bin
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
execute_process(COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/include)
|
||||||
|
execute_process(COMMAND ln -s ${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/src/pkmnlib/src
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/include/PkmnLib)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/include
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/bin/CreatureLib/include
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/bin/Angelscript/src/AngelscriptProj/angelscript/include
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/PkmnLib/bin/CreatureLib/bin/Arbutils/include)
|
||||||
|
|
||||||
|
|
||||||
|
endfunction()
|
|
@ -0,0 +1,19 @@
|
||||||
|
from conans import ConanFile, CMake
|
||||||
|
from conans.errors import ConanInvalidConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
class PkmnLibConan(ConanFile):
|
||||||
|
name = "PkmnLibAI"
|
||||||
|
license = "TODO"
|
||||||
|
url = "https://git.p-epsilon.com/Deukhoofd/PkmnLib"
|
||||||
|
description = ""
|
||||||
|
settings = "os", "compiler", "build_type"
|
||||||
|
generators = "cmake"
|
||||||
|
exports_sources = "*"
|
||||||
|
compiler = "clang"
|
||||||
|
|
||||||
|
def requirements(self):
|
||||||
|
self.requires("Arbutils/latest@epsilon/master")
|
||||||
|
self.requires("CreatureLib/latest@epsilon/master")
|
||||||
|
self.requires("PkmnLib/latest@epsilon/master")
|
||||||
|
self.requires("AngelScript/2.35@AngelScript/Deukhoofd")
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,207 @@
|
||||||
|
#ifndef PKMNLIB_AI_DEPTHSEARCHAI_HPP
|
||||||
|
#define PKMNLIB_AI_DEPTHSEARCHAI_HPP
|
||||||
|
#include <CreatureLib/Battling/TurnChoices/AttackTurnChoice.hpp>
|
||||||
|
#include <CreatureLib/Battling/TurnChoices/PassTurnChoice.hpp>
|
||||||
|
#include <CreatureLib/Battling/TurnChoices/SwitchTurnChoice.hpp>
|
||||||
|
#include <PkmnLib/Battling/Pokemon/LearnedMove.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <future>
|
||||||
|
#include <thread>
|
||||||
|
#include "../cmake-build-release/PkmnLib/src/pkmnlib/src/Battling/Pokemon/PokemonParty.hpp"
|
||||||
|
#include "NaiveAI.hpp"
|
||||||
|
#include "PokemonAI.hpp"
|
||||||
|
|
||||||
|
namespace PkmnLibAI {
|
||||||
|
class DepthSearchAI : public PokemonAI {
|
||||||
|
NaiveAI _naive;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float ScoreBattle(PkmnLib::Battling::Battle* battle, PkmnLib::Battling::Pokemon* user) {
|
||||||
|
auto side = user->GetBattleSide().GetValue();
|
||||||
|
if (battle->HasEnded()) {
|
||||||
|
if (battle->GetResult().GetWinningSide() == side->GetSideIndex()) {
|
||||||
|
return std::numeric_limits<float>::max();
|
||||||
|
} else {
|
||||||
|
return std::numeric_limits<float>::min();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float score = 0.0;
|
||||||
|
auto opposite = GetOppositeIndex(user);
|
||||||
|
for (auto* party : battle->GetParties()) {
|
||||||
|
if (party->IsResponsibleForIndex(side->GetSideIndex(), 0)) {
|
||||||
|
for (auto& mon : party->GetParty()->GetParty()) {
|
||||||
|
score += mon->GetCurrentHealth() / (float)mon->GetMaxHealth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (party->IsResponsibleForIndex(opposite)) {
|
||||||
|
for (auto& mon : party->GetParty()->GetParty()) {
|
||||||
|
score -= (mon->GetCurrentHealth() / (float)mon->GetMaxHealth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BattlePointerWrapper {
|
||||||
|
PkmnLib::Battling::Battle* Battle;
|
||||||
|
|
||||||
|
BattlePointerWrapper(PkmnLib::Battling::Battle* battle) : Battle(battle) {}
|
||||||
|
|
||||||
|
inline PkmnLib::Battling::Battle* operator->() const noexcept { return Battle; }
|
||||||
|
|
||||||
|
~BattlePointerWrapper() {
|
||||||
|
std::vector<const CreatureLib::Battling::CreatureParty*> parties;
|
||||||
|
for (auto party : Battle->GetParties()) {
|
||||||
|
parties.push_back(party->GetParty().GetRaw());
|
||||||
|
}
|
||||||
|
delete Battle;
|
||||||
|
for (auto party : parties) {
|
||||||
|
delete party;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::tuple<u8, float>> ScoreChoicesThreaded(PkmnLib::Battling::Battle* battle,
|
||||||
|
PkmnLib::Battling::Pokemon* user, uint8_t depth) {
|
||||||
|
std::vector<std::future<std::tuple<u8, float>>> threadPool;
|
||||||
|
auto side = user->GetBattleSide().GetValue();
|
||||||
|
for (u8 moveIndex = 0; moveIndex < (u8)user->GetMoves().Count(); ++moveIndex) {
|
||||||
|
auto move = user->GetMoves()[moveIndex];
|
||||||
|
if (move->GetRemainingUses() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
threadPool.push_back(std::async([=] {
|
||||||
|
auto v = std::tuple(moveIndex, SimulateTurn(battle, side->GetSideIndex(), 0, moveIndex, depth));
|
||||||
|
asThreadCleanup();
|
||||||
|
return v;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
auto& party = battle->GetParties()[side->GetSideIndex()]->GetParty()->GetParty();
|
||||||
|
for (u8 i = 0; i < party.Count(); ++i) {
|
||||||
|
auto mon = party[i];
|
||||||
|
if (!mon.HasValue()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mon.GetValue()->IsFainted()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
threadPool.push_back(std::async([=] {
|
||||||
|
auto v = std::tuple((u8)(i + 4), SimulateTurn(battle, side->GetSideIndex(), 0, i + 4, depth));
|
||||||
|
asThreadCleanup();
|
||||||
|
return v;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
std::vector<std::tuple<u8, float>> results(threadPool.size());
|
||||||
|
for (int i = 0; i < threadPool.size(); ++i) {
|
||||||
|
results[i] = threadPool[i].get();
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::tuple<int, float>> ScoreChoices(PkmnLib::Battling::Battle* battle,
|
||||||
|
PkmnLib::Battling::Pokemon* user, uint8_t depth) {
|
||||||
|
std::vector<std::tuple<int, float>> scoredMoves;
|
||||||
|
auto side = user->GetBattleSide().GetValue();
|
||||||
|
for (u8 moveIndex = 0; moveIndex < (u8)user->GetMoves().Count(); ++moveIndex) {
|
||||||
|
auto move = user->GetMoves()[moveIndex];
|
||||||
|
if (move->GetRemainingUses() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto scored = SimulateTurn(battle, side->GetSideIndex(), 0, moveIndex, depth);
|
||||||
|
scoredMoves.emplace_back(moveIndex, scored);
|
||||||
|
}
|
||||||
|
auto& party = battle->GetParties()[side->GetSideIndex()]->GetParty()->GetParty();
|
||||||
|
for (int i = 0; i < party.Count(); ++i) {
|
||||||
|
auto mon = party[i];
|
||||||
|
if (!mon.HasValue()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mon.GetValue()->IsFainted()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto scored = SimulateTurn(battle, side->GetSideIndex(), 0, i + 4, depth);
|
||||||
|
scoredMoves.emplace_back(i + 4, scored);
|
||||||
|
}
|
||||||
|
return scoredMoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SimulateTurn(PkmnLib::Battling::Battle* originalBattle, u8 sideIndex, u8 pokemonIndex, u8 index,
|
||||||
|
u8 depth) {
|
||||||
|
auto battle = BattlePointerWrapper(originalBattle->Clone());
|
||||||
|
auto user =
|
||||||
|
dynamic_cast<PkmnLib::Battling::Pokemon*>(battle->GetCreature(sideIndex, pokemonIndex).GetValue());
|
||||||
|
auto target = GetOppositeIndex(user);
|
||||||
|
if (index < 4) {
|
||||||
|
auto move = user->GetMoves()[index];
|
||||||
|
auto choice = new CreatureLib::Battling::AttackTurnChoice(user, move, target);
|
||||||
|
if (!battle->TrySetChoice(choice)) {
|
||||||
|
delete choice;
|
||||||
|
return std::numeric_limits<float>::min();
|
||||||
|
}
|
||||||
|
} 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<float>::min();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
battle->TrySetChoice(_naive.GetChoice(
|
||||||
|
battle.Battle, dynamic_cast<PkmnLib::Battling::Pokemon*>(battle->GetCreature(target).GetValue())));
|
||||||
|
|
||||||
|
float score;
|
||||||
|
if (depth <= 1) {
|
||||||
|
score = ScoreBattle(battle.Battle, user);
|
||||||
|
} else {
|
||||||
|
auto scoredChoices = ScoreChoices(battle.Battle, user, depth - 1);
|
||||||
|
float summedScore = 0;
|
||||||
|
size_t amount = 0;
|
||||||
|
for (auto& option : scoredChoices) {
|
||||||
|
summedScore += std::get<1>(option);
|
||||||
|
amount++;
|
||||||
|
}
|
||||||
|
if (amount == 0) {
|
||||||
|
return std::numeric_limits<float>::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
score = summedScore / amount;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string GetName() const noexcept override { return "depthsearch"; }
|
||||||
|
|
||||||
|
CreatureLib::Battling::BaseTurnChoice* GetChoice(PkmnLib::Battling::Battle* battle,
|
||||||
|
PkmnLib::Battling::Pokemon* user) override {
|
||||||
|
auto scoredChoices = ScoreChoicesThreaded(battle, user, 2);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 highest = -1;
|
||||||
|
float highestScore = -std::numeric_limits<float>::infinity();
|
||||||
|
for (auto& option : scoredChoices) {
|
||||||
|
if (std::get<1>(option) > highestScore) {
|
||||||
|
highestScore = std::get<1>(option);
|
||||||
|
highest = std::get<0>(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (highest == -1) {
|
||||||
|
return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highest < 4) {
|
||||||
|
return new CreatureLib::Battling::AttackTurnChoice(user, user->GetMoves()[highest], target);
|
||||||
|
} else {
|
||||||
|
return new CreatureLib::Battling::SwitchTurnChoice(user, party.At(highest - 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PKMNLIB_AI_DEPTHSEARCHAI_HPP
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef PKMNLIB_DOWNLOAD_NAIVEAI_HPP
|
||||||
|
#define PKMNLIB_DOWNLOAD_NAIVEAI_HPP
|
||||||
|
#include "PokemonAI.hpp"
|
||||||
|
|
||||||
|
namespace PkmnLibAI {
|
||||||
|
class NaiveAI : public PokemonAI {
|
||||||
|
public:
|
||||||
|
std::string GetName() const noexcept override { return "naive"; }
|
||||||
|
|
||||||
|
CreatureLib::Battling::BaseTurnChoice* GetChoice(PkmnLib::Battling::Battle* battle,
|
||||||
|
PkmnLib::Battling::Pokemon* user) override {
|
||||||
|
auto target = GetOppositeIndex(user);
|
||||||
|
auto c = battle->GetCreature(target).GetValue();
|
||||||
|
|
||||||
|
auto highestScore = -1;
|
||||||
|
PkmnLib::Battling::LearnedMove* bestMove;
|
||||||
|
for (auto move : user->GetMoves()) {
|
||||||
|
if (move->GetRemainingUses() <= 0)
|
||||||
|
continue;
|
||||||
|
auto naiveDamage =
|
||||||
|
move->GetMoveData()->GetBasePower() * battle->GetLibrary()->GetTypeLibrary()->GetEffectiveness(
|
||||||
|
move->GetMoveData()->GetType(), c->GetTypes());
|
||||||
|
if (naiveDamage > highestScore){
|
||||||
|
highestScore = naiveDamage;
|
||||||
|
bestMove = move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highestScore == -1){
|
||||||
|
return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CreatureLib::Battling::AttackTurnChoice(user, bestMove, target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PKMNLIB_DOWNLOAD_NAIVEAI_HPP
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef PKMNLIB_AI_POKEMONAI_HPP
|
||||||
|
#define PKMNLIB_AI_POKEMONAI_HPP
|
||||||
|
#include <CreatureLib/Battling/TurnChoices/BaseTurnChoice.hpp>
|
||||||
|
#include <PkmnLib/Battling/Battle/Battle.hpp>
|
||||||
|
#include <PkmnLib/Battling/Pokemon/Pokemon.hpp>
|
||||||
|
|
||||||
|
namespace PkmnLibAI {
|
||||||
|
class PokemonAI {
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
virtual ~PokemonAI() = default;
|
||||||
|
|
||||||
|
virtual CreatureLib::Battling::BaseTurnChoice* GetChoice(PkmnLib::Battling::Battle* battle,
|
||||||
|
PkmnLib::Battling::Pokemon* user) = 0;
|
||||||
|
|
||||||
|
virtual std::string GetName() const noexcept = 0;
|
||||||
|
|
||||||
|
CreatureLib::Battling::CreatureIndex GetOppositeIndex(PkmnLib::Battling::Pokemon* user) {
|
||||||
|
auto side = user->GetBattleSide().GetValue();
|
||||||
|
if (side->GetSideIndex() == 0) {
|
||||||
|
return CreatureLib::Battling::CreatureIndex(1, 0);
|
||||||
|
} else {
|
||||||
|
return CreatureLib::Battling::CreatureIndex(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PKMNLIB_AI_POKEMONAI_HPP
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef PKMNLIB_AI_PRECOMPILED_HXX
|
||||||
|
#define PKMNLIB_AI_PRECOMPILED_HXX
|
||||||
|
|
||||||
|
#include <PkmnLib/Precompiled.hxx>
|
||||||
|
|
||||||
|
#endif //PKMNLIB_AI_PRECOMPILED_HXX
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef PKMNLIB_AI_RANDOMAI_HPP
|
||||||
|
#define PKMNLIB_AI_RANDOMAI_HPP
|
||||||
|
#include <CreatureLib/Battling/TurnChoices/AttackTurnChoice.hpp>
|
||||||
|
#include <PkmnLib/Battling/Pokemon/LearnedMove.hpp>
|
||||||
|
|
||||||
|
namespace PkmnLibAI {
|
||||||
|
// AI that returns a random valid move.
|
||||||
|
class RandomAI : public PokemonAI {
|
||||||
|
private:
|
||||||
|
ArbUt::Random rand;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string GetName() const noexcept override { return "random"; }
|
||||||
|
|
||||||
|
CreatureLib::Battling::BaseTurnChoice* GetChoice([[maybe_unused]] PkmnLib::Battling::Battle* battle,
|
||||||
|
[[maybe_unused]] PkmnLib::Battling::Pokemon* user) override {
|
||||||
|
auto moves = user->GetMoves();
|
||||||
|
ArbUt::List<PkmnLib::Battling::LearnedMove*> validMoves;
|
||||||
|
for (auto move : moves) {
|
||||||
|
if (move->GetRemainingUses() > 0) {
|
||||||
|
validMoves.Append(move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto target = GetOppositeIndex(user);
|
||||||
|
|
||||||
|
if (validMoves.Count() == 0) {
|
||||||
|
return battle->GetLibrary()->GetMiscLibrary()->ReplacementAttack(user, target);
|
||||||
|
}
|
||||||
|
auto moveIndex = rand.Get(validMoves.Count());
|
||||||
|
return new CreatureLib::Battling::AttackTurnChoice(user, validMoves[moveIndex], target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PKMNLIB_AI_RANDOMAI_HPP
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef PKMNLIB_AI_AIRESOLVER_HPP
|
||||||
|
#define PKMNLIB_AI_AIRESOLVER_HPP
|
||||||
|
|
||||||
|
#include "../src/DepthSearchAI.hpp"
|
||||||
|
#include "../src/NaiveAI.hpp"
|
||||||
|
#include "../src/PokemonAI.hpp"
|
||||||
|
#include "../src/RandomAI.hpp"
|
||||||
|
|
||||||
|
class AIResolver {
|
||||||
|
public:
|
||||||
|
static PkmnLibAI::PokemonAI* Resolve(const ArbUt::StringView& name) {
|
||||||
|
switch (name) {
|
||||||
|
case "random"_cnc: return new PkmnLibAI::RandomAI();
|
||||||
|
case "depthsearch"_cnc: return new PkmnLibAI::DepthSearchAI();
|
||||||
|
case "naive"_cnc: return new PkmnLibAI::NaiveAI();
|
||||||
|
default: throw ArbUt::Exception("Unknown AI name.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PKMNLIB_AI_AIRESOLVER_HPP
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include "BuildItems.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include "../../extern/json.hpp"
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
#define GET(o, objectKey, key) \
|
||||||
|
auto _##objectKey = o[#objectKey]; \
|
||||||
|
if (_##objectKey.is_null()) { \
|
||||||
|
std::cout << "Failed to retrieve key '" << #objectKey << "' for object with value '" << key << "' in file '" \
|
||||||
|
<< path << "'\n"; \
|
||||||
|
return nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static CreatureLib::Library::ItemCategory ParseItemCategory(std::string& in) {
|
||||||
|
std::transform(in.begin(), in.end(), in.end(), tolower);
|
||||||
|
if (in == "item")
|
||||||
|
return CreatureLib::Library::ItemCategory::MiscItem;
|
||||||
|
if (in == "medicine")
|
||||||
|
return CreatureLib::Library::ItemCategory::Medicine;
|
||||||
|
if (in == "berry")
|
||||||
|
return CreatureLib::Library::ItemCategory::Berry;
|
||||||
|
if (in == "mail")
|
||||||
|
return CreatureLib::Library::ItemCategory::Mail;
|
||||||
|
if (in == "key")
|
||||||
|
return CreatureLib::Library::ItemCategory::KeyItem;
|
||||||
|
if (in == "pokeball")
|
||||||
|
return CreatureLib::Library::ItemCategory::CaptureDevice;
|
||||||
|
if (in == "tm")
|
||||||
|
return CreatureLib::Library::ItemCategory::MoveLearner;
|
||||||
|
std::cout << "Unknown Item Type: '" << in << "'\n";
|
||||||
|
return static_cast<CreatureLib::Library::ItemCategory>(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
PkmnLib::Library::ItemLibrary* BuildItems::Build(const std::string& path) {
|
||||||
|
std::ifstream fileStream(path.c_str());
|
||||||
|
if (fileStream.fail()) {
|
||||||
|
std::cout << "Failed to load Items data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto lib = new PkmnLib::Library::ItemLibrary();
|
||||||
|
json j;
|
||||||
|
fileStream >> j;
|
||||||
|
for (const auto& i : j.items()) {
|
||||||
|
if (i.key().starts_with("$")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto val = i.value();
|
||||||
|
GET(val, name, i);
|
||||||
|
GET(val, itemType, i);
|
||||||
|
GET(val, flags, i);
|
||||||
|
GET(val, price, i);
|
||||||
|
GET(val, flingPower, i);
|
||||||
|
auto itemTypeStr = _itemType.get<std::string>();
|
||||||
|
CreatureLib::Library::ItemCategory itemType = ParseItemCategory(itemTypeStr);
|
||||||
|
if (static_cast<int>(itemType) == 255)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto flags = std::unordered_set<uint>();
|
||||||
|
for (auto flagIndex : _flags.items()) {
|
||||||
|
flags.insert(ArbUt::StringView(flagIndex.value().get<std::string>().c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureLib::Library::SecondaryEffect* effect = nullptr;
|
||||||
|
auto effectJson = val["effect"];
|
||||||
|
if (effectJson != nullptr) {
|
||||||
|
auto effectName = effectJson["name"];
|
||||||
|
auto parametersJson = effectJson["parameters"];
|
||||||
|
ArbUt::List<CreatureLib::Library::EffectParameter*> parameters;
|
||||||
|
if (parametersJson != nullptr) {
|
||||||
|
for (auto& kv : parametersJson.items()) {
|
||||||
|
auto& p = kv.value();
|
||||||
|
auto t = p.type();
|
||||||
|
switch (t) {
|
||||||
|
case json::value_t::boolean:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<bool>()));
|
||||||
|
break;
|
||||||
|
case json::value_t::number_integer:
|
||||||
|
case json::value_t::number_unsigned:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<int64_t>()));
|
||||||
|
break;
|
||||||
|
case json::value_t::number_float:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<float>()));
|
||||||
|
break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
effect = new CreatureLib::Library::SecondaryEffect(
|
||||||
|
100, ArbUt::StringView(effectName.get<std::string>().c_str()), parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto item = new PkmnLib::Library::Item(ArbUt::StringView(_name.get<std::string>().c_str()), itemType,
|
||||||
|
CreatureLib::Library::BattleItemCategory::None, _price.get<int32_t>(),
|
||||||
|
effect, flags, _flingPower.get<uint8_t>());
|
||||||
|
lib->Insert(item->GetName(), item);
|
||||||
|
}
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef GET
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef GEN7TESTS_BUILDITEMS_HPP
|
||||||
|
#define GEN7TESTS_BUILDITEMS_HPP
|
||||||
|
|
||||||
|
#include <PkmnLib/Library/Items/ItemLibrary.hpp>
|
||||||
|
class BuildItems {
|
||||||
|
public:
|
||||||
|
static PkmnLib::Library::ItemLibrary* Build(const std::string& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_BUILDITEMS_HPP
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include "BuildMoves.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include "../../extern/json.hpp"
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
#define GET(o, objectKey, key) \
|
||||||
|
auto _##objectKey = o[#objectKey]; \
|
||||||
|
if (_##objectKey.is_null()) { \
|
||||||
|
std::cout << "Failed to retrieve key '" << #objectKey << "' for object with value '" << key << "' in file '" \
|
||||||
|
<< path << "'\n"; \
|
||||||
|
return nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static PkmnLib::Library::MoveCategory ParseCategory(const std::string& input) {
|
||||||
|
if (input == "physical")
|
||||||
|
return PkmnLib::Library::MoveCategory::Physical;
|
||||||
|
else if (input == "special")
|
||||||
|
return PkmnLib::Library::MoveCategory::Special;
|
||||||
|
else if (input == "status")
|
||||||
|
return PkmnLib::Library::MoveCategory::Status;
|
||||||
|
std::cout << "Invalid move category '" << input << ".\n";
|
||||||
|
return static_cast<PkmnLib::Library::MoveCategory>(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
PkmnLib::Library::MoveLibrary* BuildMoves::Build(const std::string& path,
|
||||||
|
const CreatureLib::Library::TypeLibrary* types) {
|
||||||
|
std::ifstream fileStream(path.c_str());
|
||||||
|
if (fileStream.fail()) {
|
||||||
|
std::cout << "Failed to load Move data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto lib = new PkmnLib::Library::MoveLibrary();
|
||||||
|
json j;
|
||||||
|
fileStream >> j;
|
||||||
|
for (const auto& i : j["data"].items()) {
|
||||||
|
auto val = i.value();
|
||||||
|
GET(val, name, i);
|
||||||
|
GET(val, type, i);
|
||||||
|
GET(val, power, i);
|
||||||
|
GET(val, pp, i);
|
||||||
|
GET(val, accuracy, i);
|
||||||
|
GET(val, priority, i);
|
||||||
|
GET(val, target, i);
|
||||||
|
GET(val, category, i);
|
||||||
|
GET(val, flags, i);
|
||||||
|
if (_pp.get<uint8_t>() == 0)
|
||||||
|
continue;
|
||||||
|
auto type = types->GetTypeId(ArbUt::StringView(_type.get<std::string>().c_str()));
|
||||||
|
auto category = ParseCategory(_category.get<std::string>());
|
||||||
|
if (static_cast<int>(category) == 255)
|
||||||
|
return nullptr;
|
||||||
|
CreatureLib::Library::AttackTarget target;
|
||||||
|
if (!CreatureLib::Library::AttackTargetHelper::TryParse(_target.get<std::string>().c_str(), target)) {
|
||||||
|
std::cout << "Invalid target: '" << _target.get<std::string>() << "' for move with name '"
|
||||||
|
<< _name.get<std::string>() << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto flags = std::unordered_set<uint32_t>();
|
||||||
|
for (auto flagIndex : _flags.items()) {
|
||||||
|
flags.insert(ArbUt::StringView(flagIndex.value().get<std::string>().c_str()));
|
||||||
|
}
|
||||||
|
CreatureLib::Library::SecondaryEffect* effect = nullptr;
|
||||||
|
auto jsonEffect = val["effect"];
|
||||||
|
if (jsonEffect != nullptr) {
|
||||||
|
auto name = jsonEffect["name"];
|
||||||
|
auto chanceJson = jsonEffect["chance"];
|
||||||
|
auto parametersJson = jsonEffect["parameters"];
|
||||||
|
if (name != nullptr) {
|
||||||
|
ArbUt::List<CreatureLib::Library::EffectParameter*> parameters;
|
||||||
|
auto chance = -1.0f;
|
||||||
|
if (chanceJson != nullptr) {
|
||||||
|
chance = chanceJson.get<float>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parametersJson != nullptr) {
|
||||||
|
for (auto& kv : parametersJson.items()) {
|
||||||
|
auto& p = kv.value();
|
||||||
|
auto t = p.type();
|
||||||
|
switch (t) {
|
||||||
|
case json::value_t::boolean:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<bool>()));
|
||||||
|
break;
|
||||||
|
case json::value_t::number_integer:
|
||||||
|
case json::value_t::number_unsigned:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<int64_t>()));
|
||||||
|
break;
|
||||||
|
case json::value_t::number_float:
|
||||||
|
parameters.Append(new CreatureLib::Library::EffectParameter(p.get<float>()));
|
||||||
|
break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
effect = new CreatureLib::Library::SecondaryEffect(
|
||||||
|
chance, ArbUt::StringView(name.get<std::string>().c_str()), parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (effect == nullptr) {
|
||||||
|
effect = new CreatureLib::Library::SecondaryEffect();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto move = new PkmnLib::Library::MoveData(ArbUt::StringView(_name.get<std::string>().c_str()), type, category,
|
||||||
|
_power.get<uint8_t>(), _accuracy.get<uint8_t>(), _pp.get<uint8_t>(),
|
||||||
|
target, _priority.get<int8_t>(), effect, flags);
|
||||||
|
|
||||||
|
lib->Insert(ArbUt::StringView(move->GetName().c_str()), move);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef GET
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef GEN7TESTS_BUILDMOVES_HPP
|
||||||
|
#define GEN7TESTS_BUILDMOVES_HPP
|
||||||
|
|
||||||
|
#include <CreatureLib/Library/TypeLibrary.hpp>
|
||||||
|
#include <PkmnLib/Library/Moves/MoveLibrary.hpp>
|
||||||
|
class BuildMoves {
|
||||||
|
public:
|
||||||
|
static PkmnLib::Library::MoveLibrary* Build(const std::string& path, const CreatureLib::Library::TypeLibrary* types);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_BUILDMOVES_HPP
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "BuildNatures.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static CreatureLib::Library::Statistic ParseStatistic(const std::string& stat) {
|
||||||
|
if (stat.empty())
|
||||||
|
return static_cast<CreatureLib::Library::Statistic>(255);
|
||||||
|
if (stat == "Attack")
|
||||||
|
return CreatureLib::Library::Statistic::PhysicalAttack;
|
||||||
|
if (stat == "Defense")
|
||||||
|
return CreatureLib::Library::Statistic::PhysicalDefense;
|
||||||
|
if (stat == "SpecialAttack")
|
||||||
|
return CreatureLib::Library::Statistic::MagicalAttack;
|
||||||
|
if (stat == "SpecialDefense")
|
||||||
|
return CreatureLib::Library::Statistic::MagicalDefense;
|
||||||
|
if (stat == "Speed")
|
||||||
|
return CreatureLib::Library::Statistic::Speed;
|
||||||
|
std::cout << "Invalid stat was given: '" << stat << "'.\n";
|
||||||
|
return static_cast<CreatureLib::Library::Statistic>(254);
|
||||||
|
}
|
||||||
|
|
||||||
|
PkmnLib::Library::NatureLibrary* BuildNatures::Build(const std::string& path) {
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (file.fail()) {
|
||||||
|
std::cout << "Failed to load Natures data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
if (!std::getline(file, line)) {
|
||||||
|
std::cout << "Failed to read Natures data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto divider = ',';
|
||||||
|
if (strncmp(line.c_str(), "sep=", 4) == 0) {
|
||||||
|
divider = line[4];
|
||||||
|
std::getline(file, line);
|
||||||
|
}
|
||||||
|
auto library = new PkmnLib::Library::NatureLibrary();
|
||||||
|
std::string substr;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
size_t lastStart = 0;
|
||||||
|
uint8_t current = 0;
|
||||||
|
std::string natureName;
|
||||||
|
std::string increasedStat;
|
||||||
|
std::string decreasedStat;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < line.length(); i++) {
|
||||||
|
if (line[i] == divider) {
|
||||||
|
switch (current) {
|
||||||
|
case 0: natureName = line.substr(lastStart, i - lastStart); break;
|
||||||
|
case 1: increasedStat = line.substr(lastStart, i - lastStart); break;
|
||||||
|
case 2: decreasedStat = line.substr(lastStart, i - lastStart); break;
|
||||||
|
default:
|
||||||
|
std::cout << "Unknown field in nature data: '" << line.substr(lastStart, i - lastStart)
|
||||||
|
<< "'.\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
lastStart = i + 1;
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current == 2)
|
||||||
|
decreasedStat = line.substr(lastStart, line.length() - lastStart);
|
||||||
|
|
||||||
|
auto parsedIncreased = ParseStatistic(increasedStat);
|
||||||
|
float increasedModifier = 1.1;
|
||||||
|
if (static_cast<int>(parsedIncreased) == 254)
|
||||||
|
return nullptr;
|
||||||
|
if (static_cast<int>(parsedIncreased) == 255)
|
||||||
|
increasedModifier = 1.0;
|
||||||
|
|
||||||
|
auto parsedDecreased = ParseStatistic(decreasedStat);
|
||||||
|
float decreasedModifier = 0.9;
|
||||||
|
if (static_cast<int>(parsedDecreased) == 254)
|
||||||
|
return nullptr;
|
||||||
|
if (static_cast<int>(parsedDecreased) == 255)
|
||||||
|
decreasedModifier = 1.0;
|
||||||
|
|
||||||
|
library->LoadNature(
|
||||||
|
ArbUt::StringView(natureName.c_str()),
|
||||||
|
new PkmnLib::Library::Nature(parsedIncreased, parsedDecreased, increasedModifier, decreasedModifier));
|
||||||
|
}
|
||||||
|
return library;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef GEN7TESTS_BUILDNATURES_HPP
|
||||||
|
#define GEN7TESTS_BUILDNATURES_HPP
|
||||||
|
|
||||||
|
#include <PkmnLib/Library/Natures/NatureLibrary.hpp>
|
||||||
|
class BuildNatures {
|
||||||
|
public:
|
||||||
|
static PkmnLib::Library::NatureLibrary* Build(const std::string& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_BUILDNATURES_HPP
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "BuildSpecies.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define GET(o, objectKey, key) \
|
||||||
|
auto _##objectKey = o[#objectKey]; \
|
||||||
|
if (_##objectKey.is_null()) { \
|
||||||
|
std::cout << "Failed to retrieve key '" << #objectKey << "' for object with key '" << key << "' in file '" \
|
||||||
|
<< path << "'\n"; \
|
||||||
|
return nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
PkmnLib::Library::SpeciesLibrary* BuildSpecies::BuildLibrary(const std::string& path,
|
||||||
|
const CreatureLib::Library::TypeLibrary* types,
|
||||||
|
const PkmnLib::Library::MoveLibrary* moveLibrary) {
|
||||||
|
std::ifstream fileStream(path.c_str());
|
||||||
|
if (fileStream.fail()) {
|
||||||
|
std::cout << "Failed to load Pokemon data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto lib = new PkmnLib::Library::SpeciesLibrary();
|
||||||
|
json j;
|
||||||
|
fileStream >> j;
|
||||||
|
|
||||||
|
for (json::iterator it = j.begin(); it != j.end(); ++it) {
|
||||||
|
if (it.key().starts_with("$")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto val = it.value();
|
||||||
|
GET(val, id, it.key());
|
||||||
|
GET(val, species, it.key());
|
||||||
|
GET(val, genderRatio, it.key());
|
||||||
|
GET(val, growthRate, it.key());
|
||||||
|
GET(val, baseHappiness, it.key());
|
||||||
|
GET(val, catchRate, it.key());
|
||||||
|
GET(val, color, it.key());
|
||||||
|
GET(val, genderDifference, it.key());
|
||||||
|
GET(val, eggGroups, it.key());
|
||||||
|
GET(val, eggCycles, it.key());
|
||||||
|
GET(val, tags, it.key());
|
||||||
|
GET(val, formes, it.key());
|
||||||
|
|
||||||
|
PkmnLib::Library::PokemonSpecies* species = nullptr;
|
||||||
|
|
||||||
|
ArbUt::List<ArbUt::StringView> eggGroups;
|
||||||
|
for (const auto& eg : _eggGroups) {
|
||||||
|
eggGroups.Append(eg.get<std::string>().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultForme = _formes["default"];
|
||||||
|
if (!defaultForme.is_null()) {
|
||||||
|
auto forme = BuildForme("default", defaultForme, it.key(), path, types, moveLibrary);
|
||||||
|
species = new PkmnLib::Library::PokemonSpecies(
|
||||||
|
_id.get<uint16_t>(), ArbUt::StringView(_species.get<std::string>().c_str()), forme,
|
||||||
|
_genderRatio.get<int8_t>() / static_cast<float>(100),
|
||||||
|
ArbUt::StringView(_growthRate.get<std::string>().c_str()), _catchRate.get<uint8_t>(),
|
||||||
|
_baseHappiness.get<uint8_t>(), eggGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (json::iterator formeIt = _formes.begin(); formeIt != _formes.end(); ++formeIt) {
|
||||||
|
if (formeIt.key() == "default") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto forme = BuildForme(formeIt.key(), formeIt.value(), it.key(), path, types, moveLibrary);
|
||||||
|
if (forme == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
if (species == nullptr) {
|
||||||
|
species = new PkmnLib::Library::PokemonSpecies(
|
||||||
|
_id.get<uint16_t>(), ArbUt::StringView(_species.get<std::string>().c_str()), forme,
|
||||||
|
static_cast<float>(_genderRatio.get<int8_t>()) / static_cast<float>(100),
|
||||||
|
ArbUt::StringView(_growthRate.get<std::string>().c_str()), _catchRate.get<uint8_t>(),
|
||||||
|
_baseHappiness.get<uint8_t>(), eggGroups);
|
||||||
|
} else {
|
||||||
|
if (species->HasForme(ArbUt::StringView(formeIt.key().c_str()))) {
|
||||||
|
std::cout << "Species '" << it.key() << "' has duplicate forme '" << formeIt.key()
|
||||||
|
<< "'. Skipping.\n";
|
||||||
|
delete forme;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
species->SetVariant(ArbUt::StringView(formeIt.key().c_str()), forme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (species == nullptr) {
|
||||||
|
std::cout << "Pokemon with key '" << it.key() << "' does not have any formes.\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
lib->Insert(ArbUt::StringView(it.key().c_str()), species);
|
||||||
|
}
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CreatureLib::Library::StatisticSet<uint16_t> ParseStatistics(json& json) {
|
||||||
|
return CreatureLib::Library::StatisticSet<uint16_t>(
|
||||||
|
json["hp"].get<uint16_t>(), json["attack"].get<uint16_t>(), json["defense"].get<uint16_t>(),
|
||||||
|
json["specialAttack"].get<uint16_t>(), json["specialDefense"].get<uint16_t>(), json["speed"].get<uint16_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
const PkmnLib::Library::PokemonForme* BuildSpecies::BuildForme(const std::string& name, json& forme,
|
||||||
|
const std::string& baseKeyName, const std::string& path,
|
||||||
|
const CreatureLib::Library::TypeLibrary* typeLibrary,
|
||||||
|
const PkmnLib::Library::MoveLibrary* moveLibrary) {
|
||||||
|
GET(forme, abilities, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, hiddenAbilities, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, baseStats, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, evReward, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, types, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, height, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, weight, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, baseExp, baseKeyName << " -> " << name);
|
||||||
|
GET(forme, moves, baseKeyName << " -> " << name);
|
||||||
|
|
||||||
|
auto typeStrings = _types.get<std::vector<std::string>>();
|
||||||
|
auto types = ArbUt::List<uint8_t>(typeStrings.size());
|
||||||
|
for (size_t i = 0; i < typeStrings.size(); i++) {
|
||||||
|
types.Append(typeLibrary->GetTypeId(ArbUt::StringView(typeStrings[i].c_str())));
|
||||||
|
}
|
||||||
|
auto stats = ParseStatistics(_baseStats);
|
||||||
|
|
||||||
|
auto abilityStrings = _abilities.get<std::vector<std::string>>();
|
||||||
|
auto abilities = ArbUt::List<ArbUt::StringView>(abilityStrings.size());
|
||||||
|
for (size_t i = 0; i < abilityStrings.size(); i++) {
|
||||||
|
abilities.Append(ArbUt::StringView(abilityStrings[i].c_str()));
|
||||||
|
}
|
||||||
|
auto hiddenAbilityStrings = _abilities.get<std::vector<std::string>>();
|
||||||
|
auto hiddenAbilities = ArbUt::List<ArbUt::StringView>(hiddenAbilityStrings.size());
|
||||||
|
for (size_t i = 0; i < hiddenAbilityStrings.size(); i++) {
|
||||||
|
hiddenAbilities.Append(ArbUt::StringView(abilityStrings[i].c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moves = new PkmnLib::Library::LearnableMoves(100);
|
||||||
|
auto movesJson = forme["moves"];
|
||||||
|
auto levelMovesJson = movesJson["levelMoves"];
|
||||||
|
for (auto& levelMoveObj : levelMovesJson) {
|
||||||
|
auto levelMoveName = levelMoveObj["name"].get<std::string>();
|
||||||
|
auto level = levelMoveObj["level"].get<u8>();
|
||||||
|
auto move = moveLibrary->Get(ArbUt::StringView(levelMoveName.c_str(), levelMoveName.size()));
|
||||||
|
moves->AddLevelAttack(level, move.ForceAs<const CreatureLib::Library::AttackData>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PkmnLib::Library::PokemonForme(ArbUt::StringView(name.c_str()), _height.get<float>(),
|
||||||
|
_weight.get<float>(), _baseExp.get<uint32_t>(), types, stats, abilities,
|
||||||
|
hiddenAbilities, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef GET
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef GEN7TESTS_BUILDSPECIES_HPP
|
||||||
|
#define GEN7TESTS_BUILDSPECIES_HPP
|
||||||
|
#define LEVEL_U8 1
|
||||||
|
#include <CreatureLib/Library/TypeLibrary.hpp>
|
||||||
|
#include <PkmnLib/Library/PokemonLibrary.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include "../../extern/json.hpp"
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
class BuildSpecies {
|
||||||
|
static const PkmnLib::Library::PokemonForme* BuildForme(const std::string& name, json& forme,
|
||||||
|
const std::string& baseKeyName, const std::string& path,
|
||||||
|
const CreatureLib::Library::TypeLibrary* typeLibrary,
|
||||||
|
const PkmnLib::Library::MoveLibrary* moveLibrary);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static PkmnLib::Library::SpeciesLibrary* BuildLibrary(const std::string& path,
|
||||||
|
const CreatureLib::Library::TypeLibrary* types,
|
||||||
|
const PkmnLib::Library::MoveLibrary* moveLibrary);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_BUILDSPECIES_HPP
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "BuildTypes.hpp"
|
||||||
|
CreatureLib::Library::TypeLibrary* BuildTypes::Build(const std::string& path) {
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (file.fail()) {
|
||||||
|
std::cout << "Failed to load Types data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
if (!std::getline(file, line)) {
|
||||||
|
std::cout << "Failed to read Types data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto divider = ',';
|
||||||
|
if (strncmp(line.c_str(), "sep=", 4) == 0) {
|
||||||
|
divider = line[4];
|
||||||
|
std::getline(file, line);
|
||||||
|
}
|
||||||
|
auto* library = new CreatureLib::Library::TypeLibrary();
|
||||||
|
|
||||||
|
bool hasSkippedFirst = false;
|
||||||
|
size_t lastStart = 0;
|
||||||
|
std::vector<uint8_t> types;
|
||||||
|
for (size_t i = 0; i < line.length(); i++) {
|
||||||
|
if (line[i] == divider) {
|
||||||
|
auto substr = line.substr(lastStart, i - lastStart);
|
||||||
|
lastStart = i + 1;
|
||||||
|
if (hasSkippedFirst) {
|
||||||
|
auto val = library->RegisterType(ArbUt::StringView(substr.c_str()));
|
||||||
|
types.push_back(val);
|
||||||
|
} else {
|
||||||
|
hasSkippedFirst = true;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto substr = line.substr(lastStart, line.length() - lastStart);
|
||||||
|
auto val = library->RegisterType(ArbUt::StringView(substr.c_str()));
|
||||||
|
types.push_back(val);
|
||||||
|
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
uint8_t attackingType = 0;
|
||||||
|
bool gotType = false;
|
||||||
|
lastStart = 0;
|
||||||
|
int current = 0;
|
||||||
|
for (size_t i = 0; i < line.length(); i++) {
|
||||||
|
if (line[i] == divider) {
|
||||||
|
substr = line.substr(lastStart, i - lastStart);
|
||||||
|
lastStart = i + 1;
|
||||||
|
if (gotType) {
|
||||||
|
auto eff = std::atof(substr.c_str());
|
||||||
|
library->SetEffectiveness(attackingType, types[current], eff);
|
||||||
|
current++;
|
||||||
|
} else {
|
||||||
|
gotType = true;
|
||||||
|
attackingType = library->GetTypeId(ArbUt::StringView(substr.c_str()));
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
substr = line.substr(lastStart, line.length() - lastStart);
|
||||||
|
auto eff = std::atof(substr.c_str());
|
||||||
|
library->SetEffectiveness(attackingType, types[current], eff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return library;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef GEN7TESTS_BUILDTYPES_HPP
|
||||||
|
#define GEN7TESTS_BUILDTYPES_HPP
|
||||||
|
#include <CreatureLib/Library/TypeLibrary.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class BuildTypes {
|
||||||
|
public:
|
||||||
|
static CreatureLib::Library::TypeLibrary* Build(const std::string& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_BUILDTYPES_HPP
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "GrowthRatesBuilder.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include "../../extern/json.hpp"
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
CreatureLib::Library::GrowthRateLibrary* GrowthRatesBuilder::Build(const std::string& path) {
|
||||||
|
std::ifstream fileStream(path.c_str());
|
||||||
|
if (fileStream.fail()) {
|
||||||
|
std::cout << "Failed to load Pokemon data file at '" << path << "'\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto lib = new CreatureLib::Library::GrowthRateLibrary();
|
||||||
|
json j;
|
||||||
|
fileStream >> j;
|
||||||
|
|
||||||
|
for (const auto& i : j.items()) {
|
||||||
|
const auto& name = i.key();
|
||||||
|
auto values = i.value();
|
||||||
|
lib->AddGrowthRate(ArbUt::StringView(name.c_str()),
|
||||||
|
new LookupGrowthRate(values.get<std::vector<uint32_t>>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lib;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef GEN7TESTS_GROWTHRATESBUILDER_HPP
|
||||||
|
#define GEN7TESTS_GROWTHRATESBUILDER_HPP
|
||||||
|
|
||||||
|
#define LEVEL_U8 1
|
||||||
|
#include <CreatureLib/Library/GrowthRates/GrowthRate.hpp>
|
||||||
|
#include <CreatureLib/Library/GrowthRates/GrowthRateLibrary.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class LookupGrowthRate : public CreatureLib::Library::GrowthRate {
|
||||||
|
std::vector<uint32_t> _experience;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LookupGrowthRate(std::vector<uint32_t> experience) : _experience(std::move(experience)) {}
|
||||||
|
|
||||||
|
[[nodiscard]] uint8_t CalculateLevel(uint32_t experience) const override {
|
||||||
|
for (size_t i = 0; i < _experience.size(); i++) {
|
||||||
|
if (_experience[i] > experience) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _experience[_experience.size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] uint32_t CalculateExperience(uint8_t level) const override { return _experience[level - 1]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GrowthRatesBuilder {
|
||||||
|
public:
|
||||||
|
static CreatureLib::Library::GrowthRateLibrary* Build(const std::string& file);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GEN7TESTS_GROWTHRATESBUILDER_HPP
|
|
@ -0,0 +1,115 @@
|
||||||
|
#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);
|
||||||
|
Ensure(battle->TrySetChoice(c1));
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef PKMNLIB_AI_RUNNER_HPP
|
||||||
|
#define PKMNLIB_AI_RUNNER_HPP
|
||||||
|
|
||||||
|
#include <PkmnLib/Battling/Library/BattleLibrary.hpp>
|
||||||
|
#include "../src/PokemonAI.hpp"
|
||||||
|
|
||||||
|
class Runner {
|
||||||
|
public:
|
||||||
|
static void Run(const PkmnLib::Battling::BattleLibrary* library, PkmnLibAI::PokemonAI* aiOne,
|
||||||
|
PkmnLibAI::PokemonAI* aiTwo, size_t runs);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PKMNLIB_AI_RUNNER_HPP
|
|
@ -0,0 +1,112 @@
|
||||||
|
#include <PkmnLib/ScriptResolving/AngelScript/AngelScriptResolver.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include "../extern/args.hpp"
|
||||||
|
#include "AIResolver.hpp"
|
||||||
|
#include "BuildData/BuildItems.hpp"
|
||||||
|
#include "BuildData/BuildMoves.hpp"
|
||||||
|
#include "BuildData/BuildNatures.hpp"
|
||||||
|
#include "BuildData/BuildSpecies.hpp"
|
||||||
|
#include "BuildData/BuildTypes.hpp"
|
||||||
|
#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<std::string> workingDirFlag(parser, "Working Directory", "Which work directory to use.",
|
||||||
|
{"workdir"});
|
||||||
|
args::ValueFlag<size_t> runsFlag(parser, "Runs", "How many battle to runs.", {"runs", 'r'}, 1000);
|
||||||
|
args::ValueFlag<std::string> ai1Flag(parser, "AI 1", "What AI to use for side 1.", {"ai1", '1'}, "depthsearch");
|
||||||
|
args::ValueFlag<std::string> ai2Flag(parser, "AI 2", "What AI to use for side 2.", {"ai2", '2'}, "naive");
|
||||||
|
try {
|
||||||
|
parser.ParseCLI(argc, argv);
|
||||||
|
} catch (args::Help) {
|
||||||
|
std::cout << parser;
|
||||||
|
return 0;
|
||||||
|
} catch (args::ParseError e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
std::cerr << parser;
|
||||||
|
return 1;
|
||||||
|
} catch (args::ValidationError e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
std::cerr << parser;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (workingDirFlag) {
|
||||||
|
workingDirectory = args::get(workingDirFlag);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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<AngelScriptResolver*>(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<char>(in)), std::istreambuf_iterator<char>());
|
||||||
|
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()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
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;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete battleLib;
|
||||||
|
delete ai1;
|
||||||
|
delete ai2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue