commit 8755548d08794bb2fa98d08ec2338cab4ecc481b Author: A Date: Mon Apr 10 18:31:02 2023 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..443c5f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/Debug +/KoraBotTests +/Release +/.vs \ No newline at end of file diff --git a/KoraBot.sln b/KoraBot.sln new file mode 100644 index 0000000..e23dde9 --- /dev/null +++ b/KoraBot.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KoraBot", "KoraBot.vcxproj", "{2E63AE74-758A-4607-9DE4-D28E814A6E13}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BWAPILIB", "..\BWAPILIB\BWAPILIB.vcxproj", "{843656FD-9BFD-47BF-8460-7BFE9710EA2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KoraBotTests", "KoraBotTests\KoraBotTests.vcxproj", "{472D636A-6F55-49D1-856F-92C527D58ECC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KoraBotTest", "KoraBotTest\KoraBotTest.vcxproj", "{E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Debug|x64.ActiveCfg = Debug|Win32 + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Debug|x86.ActiveCfg = Debug|Win32 + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Debug|x86.Build.0 = Debug|Win32 + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Release|x64.ActiveCfg = Release|Win32 + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Release|x86.ActiveCfg = Release|Win32 + {2E63AE74-758A-4607-9DE4-D28E814A6E13}.Release|x86.Build.0 = Release|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Debug|x64.ActiveCfg = Debug|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Debug|x86.ActiveCfg = Debug|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Debug|x86.Build.0 = Debug|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Release|x64.ActiveCfg = Release|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Release|x86.ActiveCfg = Release|Win32 + {843656FD-9BFD-47BF-8460-7BFE9710EA2C}.Release|x86.Build.0 = Release|Win32 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Debug|x64.ActiveCfg = Debug|x64 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Debug|x64.Build.0 = Debug|x64 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Debug|x86.ActiveCfg = Debug|Win32 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Debug|x86.Build.0 = Debug|Win32 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Release|x64.ActiveCfg = Release|x64 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Release|x64.Build.0 = Release|x64 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Release|x86.ActiveCfg = Release|Win32 + {472D636A-6F55-49D1-856F-92C527D58ECC}.Release|x86.Build.0 = Release|Win32 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Debug|x64.ActiveCfg = Debug|x64 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Debug|x64.Build.0 = Debug|x64 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Debug|x86.ActiveCfg = Debug|Win32 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Debug|x86.Build.0 = Debug|Win32 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Release|x64.ActiveCfg = Release|x64 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Release|x64.Build.0 = Release|x64 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Release|x86.ActiveCfg = Release|Win32 + {E2B33FB7-A23E-4C7A-9A9D-02CCD70CFC56}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {71D7DBBD-1E53-43D6-B962-298144D4435C} + EndGlobalSection +EndGlobal diff --git a/KoraBot.vcxproj b/KoraBot.vcxproj new file mode 100644 index 0000000..b73a437 --- /dev/null +++ b/KoraBot.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {2E63AE74-758A-4607-9DE4-D28E814A6E13} + KoraBot + Win32Proj + 8.1 + + + + DynamicLibrary + true + v141_xp + + + DynamicLibrary + Unicode + v141_xp + + + 10.0.18362.0 + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + false + false + + + + Disabled + ../include;%(AdditionalIncludeDirectories) + NOMINMAX;WIN32;_WIN32_WINNT=0x0501;NTDDI_VERSION=0x05010300;_DEBUG;_WINDOWS;_USRDLL;KORABOT_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + true + /Zc:threadSafeInit- %(AdditionalOptions) + + + + true + Windows + $(IntDir)$(TargetName).lib + MachineX86 + + + + + MaxSpeed + true + ../include;%(AdditionalIncludeDirectories) + NOMINMAX;WIN32;_WIN32_WINNT=0x0501;NTDDI_VERSION=0x05010300;NDEBUG;_WINDOWS;_USRDLL;KORABOT_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + true + /Zc:threadSafeInit- %(AdditionalOptions) + + + + true + Windows + true + true + $(IntDir)$(TargetName).lib + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {843656fd-9bfd-47bf-8460-7bfe9710ea2c} + + + + + + \ No newline at end of file diff --git a/KoraBot.vcxproj.filters b/KoraBot.vcxproj.filters new file mode 100644 index 0000000..501f7b5 --- /dev/null +++ b/KoraBot.vcxproj.filters @@ -0,0 +1,158 @@ + + + + + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + + + + + + + + + + + + + + + + + + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + BWEM + + + + + + + + + + + + + + + + + + + + + {dd2a7d56-7d9f-49ea-86d2-178e8c60dd0b} + + + \ No newline at end of file diff --git a/KoraBot.vcxproj.user b/KoraBot.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/KoraBot.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/KoraBotTest/AnalysisTest.cpp b/KoraBotTest/AnalysisTest.cpp new file mode 100644 index 0000000..9a568bb --- /dev/null +++ b/KoraBotTest/AnalysisTest.cpp @@ -0,0 +1,150 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Analysis.h"; + +using namespace BWAPI; + +namespace AnalysisTest +{ + BWAPI::UnitType drone = BWAPI::UnitTypes::Zerg_Drone; + BWAPI::TechType tech = BWAPI::TechTypes::Burrowing; + BWAPI::UpgradeType upgrade = BWAPI::UpgradeTypes::Zerg_Missile_Attacks; + TEST(AnalysisTest, analyzeTech) + { + EXPECT_EQ(Analysis::analyze(tech), 0); + } + + TEST(AnalysisTest, DISABLED_analyzeUnit) + { + EXPECT_GE(Analysis::analyze(drone), 0); + } + + TEST(AnalysisTest, analyzeUpgrade) + { + EXPECT_GE(Analysis::analyze(upgrade), 0); + } + + TEST(AnalysisTest, DISABLED_analyzeDrone) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, DISABLED_analyzeEvoChamber) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, analyzeGas) + { + EXPECT_EQ(Analysis::analyzeGas(), 1); + } + + TEST(AnalysisTest, analyzeGroundArmor) + { + EXPECT_EQ(Analysis::analyzeGroundArmor(), 1); + } + + TEST(AnalysisTest, DISABLED_analyzeHatchery) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, analyzeHive) + { + EXPECT_EQ(Analysis::analyzeHive(), 0.8); + } + + TEST(AnalysisTest, DISABLED_analyzeHydraDen) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, DISABLED_analyzeHydralisk) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, analyzeHydraRange) + { + EXPECT_EQ(Analysis::analyzeHydraRange(), 1); + } + + TEST(AnalysisTest, analyzeHydraSpeed) + { + EXPECT_EQ(Analysis::analyzeHydraSpeed(), 1); + } + + TEST(AnalysisTest, analyzeLair) + { + EXPECT_EQ(Analysis::analyzeLair(), 0.8); + } + + TEST(AnalysisTest, analyzeLingSpeed) + { + EXPECT_EQ(Analysis::analyzeLingSpeed(), 1); + } + + TEST(AnalysisTest, analyzeMissile) + { + EXPECT_EQ(Analysis::analyzeMissile(), 1); + } + + TEST(AnalysisTest, DISABLED_analyzeMutalisk) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, DISABLED_analyzeOverlord) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, analyzeOverlordSpeed) + { + EXPECT_EQ(Analysis::analyzeOverlordSpeed(), 1); + } + + TEST(AnalysisTest, analyzeQueensNest) + { + EXPECT_EQ(Analysis::analyzeQueensNest(), 1); + } + + TEST(AnalysisTest, DISABLED_analyzeSpawningPool) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, analyzeSpire) + { + EXPECT_EQ(Analysis::analyzeSpire(), 1); + } + + TEST(AnalysisTest, analyzeSpore) + { + EXPECT_EQ(Analysis::analyzeSpore(), 1); + } + + TEST(AnalysisTest, analyzeSunken) + { + EXPECT_EQ(Analysis::analyzeSunken(), 1.3); + } + + TEST(AnalysisTest, analyzeUltraCavern) + { + EXPECT_EQ(Analysis::analyzeUltraCavern(), 1); + } + + TEST(AnalysisTest, DISABLED_analyzeUltralisk) + { + EXPECT_EQ(0, 0); + } + + TEST(AnalysisTest, DISABLED_analyzeZergling) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/BuildingPlacementTest.cpp b/KoraBotTest/BuildingPlacementTest.cpp new file mode 100644 index 0000000..b37b98b --- /dev/null +++ b/KoraBotTest/BuildingPlacementTest.cpp @@ -0,0 +1,32 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace BuildingPlacementTest +{ + TEST(BuildingPlacementTest, DISABLED_build) + { + EXPECT_EQ(0, 0); + } + + TEST(BuildingPlacementTest, DISABLED_checkBounds) + { + EXPECT_EQ(0, 0); + } + + TEST(BuildingPlacementTest, DISABLED_getDirection) + { + EXPECT_EQ(0, 0); + } + + TEST(BuildingPlacementTest, DISABLED_getPosition) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/CBaseTest.cpp b/KoraBotTest/CBaseTest.cpp new file mode 100644 index 0000000..6459413 --- /dev/null +++ b/KoraBotTest/CBaseTest.cpp @@ -0,0 +1,88 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace CBaseTest +{ + TEST(CBaseTest, DISABLED_addWorker) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_desiredSporeCount) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_desiredSunkenCount) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_extractorsBuilt) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_getDefense) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_getResources) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_initialize) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_initializeResources) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_isExposedToCenter) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_isSaturated) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_isStartLocation) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_setDefenseLocation) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_subtractWorker) + { + EXPECT_EQ(0, 0); + } + + TEST(CBaseTest, DISABLED_techCount) + { + EXPECT_EQ(0, 0); + } +} + +class MockCBase : public CBase +{ + public: + +}; \ No newline at end of file diff --git a/KoraBotTest/CUnitTest.cpp b/KoraBotTest/CUnitTest.cpp new file mode 100644 index 0000000..90fcd26 --- /dev/null +++ b/KoraBotTest/CUnitTest.cpp @@ -0,0 +1,46 @@ +#include "pch.h" +#include + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h" +#include "../kora-bot/Source/CUnit.h" + +using namespace BWAPI; + +namespace CUnitTest +{ + TEST(CUnitTest, initializeBlank) + { + CUnit unit = CUnit(); + EXPECT_EQ(unit.action, ""); + EXPECT_EQ(unit.lastSeen, TilePosition(0,0)); + EXPECT_EQ(unit.miningBase, -1); + EXPECT_EQ(unit.target, TilePosition(-1, -1)); + } + + TEST(CUnitTest, DISABLED_initializeExistingUnit) + { + //MockBWAPIUnit BWAPIUnit; + //CUnit unit = CUnit(BWAPIUnit); + } + + TEST(CUnitTest, DISABLED_isIdle) + { + EXPECT_EQ(0, 0); + } + + TEST(CUnitTest, DISABLED_isInSquad) + { + EXPECT_EQ(0, 0); + } +} + +class MockBWAPIUnit : public UnitInterface +{ + public: + // in the same order they are in the BWAPI::UnitInterface definition + MOCK_METHOD(int, getID, (), (const, override)); + MOCK_METHOD(bool, exists, (), (const, override)); +}; diff --git a/KoraBotTest/CheckItemTest.cpp b/KoraBotTest/CheckItemTest.cpp new file mode 100644 index 0000000..fd8f3c9 --- /dev/null +++ b/KoraBotTest/CheckItemTest.cpp @@ -0,0 +1,142 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace CheckItemTest +{ + TEST(CheckItemTest, DISABLED_checkBases) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkEvoChamber) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkGas) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkHive) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkHydraDen) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkHydralisk) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkMacroHatch) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkMutalisk) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkNewBase) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkQueensNest) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkSpawningPool) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkSpire) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkSpore) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkSunken) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkSupply) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkWorkers) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkUltraCavern) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkUltralisk) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkZergling) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkHydraRange) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkHydraSpeed) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkLair) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkLingSpeed) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkOverlordSpeed) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkGroundArmor) + { + EXPECT_EQ(0, 0); + } + + TEST(CheckItemTest, DISABLED_checkMissile) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/KoraBotTest.vcxproj b/KoraBotTest/KoraBotTest.vcxproj new file mode 100644 index 0000000..acd89ef --- /dev/null +++ b/KoraBotTest/KoraBotTest.vcxproj @@ -0,0 +1,156 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {e2b33fb7-a23e-4c7a-9a9d-02ccd70cfc56} + Win32Proj + 8.1 + Application + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + {843656fd-9bfd-47bf-8460-7bfe9710ea2c} + + + {2e63ae74-758a-4607-9de4-d28e814a6e13} + + + + + + + + + + + + + NotUsing + pch.h + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ../../include;$(SolutionDir)Release\;%(AdditionalIncludeDirectories) + + + true + Console + $(SolutionDir)$(IntDir)*.obj;%(AdditionalLibraryDirectories) + $(SolutionDir)$(IntDir)*.obj;%(AdditionalDependencies) + + + + + NotUsing + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ../../include;$(SolutionDir)Release\;%(AdditionalIncludeDirectories) + + + true + Console + $(SolutionDir)$(IntDir)*.obj;%(AdditionalLibraryDirectories) + $(SolutionDir)$(IntDir)*.obj;%(AdditionalDependencies) + + + + + NotUsing + pch.h + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ../../include;$(SolutionDir)Release\;%(AdditionalIncludeDirectories) + + + true + Console + true + true + $(SolutionDir)$(IntDir)*.obj;%(AdditionalLibraryDirectories) + $(SolutionDir)$(IntDir)*.obj;%(AdditionalDependencies) + + + + + NotUsing + pch.h + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ../../include;$(SolutionDir)Release\;%(AdditionalIncludeDirectories) + + + true + Console + true + true + $(SolutionDir)$(IntDir)*.obj;%(AdditionalLibraryDirectories) + $(SolutionDir)$(IntDir)*.obj;%(AdditionalDependencies) + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/KoraBotTest/KoraBotTest.vcxproj.user b/KoraBotTest/KoraBotTest.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/KoraBotTest/KoraBotTest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/KoraBotTest/MacroTest.cpp b/KoraBotTest/MacroTest.cpp new file mode 100644 index 0000000..8c17071 --- /dev/null +++ b/KoraBotTest/MacroTest.cpp @@ -0,0 +1,228 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Macro.h"; + +using namespace BWAPI; + + +namespace MacroTest +{ + namespace { auto& map = BWEM::Map::Instance(); } + + int selfID = 1; + + TilePosition tp1 = TilePosition(1, 1); + + TEST(MacroTest, DISABLED_addBase) + { + EXPECT_EQ(int(Macro::bases.size()), 0); + Macro::addBase(tp1); + EXPECT_EQ(int(Macro::bases.size()), 1); + } + + TEST(MacroTest, addLocation) + { + EXPECT_EQ(int(Macro::locations.size()), 0); + Macro::addLocation(tp1, false); + EXPECT_EQ(int(Macro::locations.size()), 1); + } + + TEST(MacroTest, addPlayer) + { + BWAPI::Player p = BWAPI::Player(); + + EXPECT_EQ(int(Macro::players.size()), 0); + Macro::addPlayer(p); + EXPECT_EQ(int(Macro::players.size()), 1); + } + + TEST(MacroTest, addSquad) + { + EXPECT_EQ(int(Macro::squads.size()), 0); + Macro::addSquad(); + EXPECT_EQ(int(Macro::squads.size()), 1); + } + + TEST(MacroTest, DISABLED_analyzeQueue) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_assignBase) + { + BWAPI::Player p = BWAPI::Player(); + + CBase c = CBase(); + Macro::addPlayer(p); + + Macro::assignBase(p->getID(), c); + + EXPECT_EQ(int(Macro::players.front().bases.size()), 1); + } + + TEST(MacroTest, DISABLED_findMiningBase) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_freeUnit) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_getTrainingBase) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_morphCreepColonies) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitComplete) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitCreate) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitDestroy) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitDiscover) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitMorph) + { + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, DISABLED_onUnitShow) + { + Unit u = Unit(); + EXPECT_EQ(1, 0); + } + + TEST(MacroTest, queueCount) + { + BWAPI::UnitType unit = BWAPI::UnitTypes::Zerg_Drone; + EXPECT_EQ(Macro::queueCount(unit), 0); + + Macro::queue.push_back(QueueEntry(unit, false, 0.0)); + Macro::queue.push_back(QueueEntry(unit, false, 0.0)); + EXPECT_EQ(Macro::queueCount(unit), 2); + } + + TEST(MacroTest, queueHasTech) + { + BWAPI::TechType tech = BWAPI::TechTypes::Burrowing; + EXPECT_FALSE(Macro::queueHas(tech)); + + Macro::queue.push_back(QueueEntry(tech, false, 0.0)); + EXPECT_TRUE(Macro::queueHas(tech)); + } + + TEST(MacroTest, queueHasUnit) + { + Macro::queue.clear(); + BWAPI::UnitType unit = BWAPI::UnitTypes::Zerg_Drone; + EXPECT_FALSE(Macro::queueHas(unit)); + + Macro::queue.push_back(QueueEntry(unit, false, 0.0)); + EXPECT_TRUE(Macro::queueHas(unit)); + } + + TEST(MacroTest, queueHasUpgrade) + { + BWAPI::UpgradeType upgrade = BWAPI::UpgradeTypes::Adrenal_Glands; + EXPECT_FALSE(Macro::queueHas(upgrade)); + + Macro::queue.push_back(QueueEntry(upgrade, false, 0.0)); + EXPECT_TRUE(Macro::queueHas(upgrade)); + } + + TEST(MacroTest, setEnemyId) + { + int enemyId = 2; + Macro::setEnemyID(enemyId); + // check if the enemy id is in the enemyIDs vector + EXPECT_TRUE(std::find(Macro::enemyIDs.begin(), Macro::enemyIDs.end(), enemyId) != Macro::enemyIDs.end()); + } + + TEST(MacroTest, DISABLED_setLastAttack) + { + int lastAttackFrame = 2; + Macro::setLastAttack(lastAttackFrame); + EXPECT_EQ(Macro::lastAttack, lastAttackFrame); + } + + TEST(MacroTest, DISABLED_setLastBuildLocation) + { + Macro::setLastBuildLocation(tp1); + EXPECT_EQ(Macro::lastBuildLocation.x, tp1.x); + EXPECT_EQ(Macro::lastBuildLocation.y, tp1.y); + } + + TEST(MacroTest, DISABLED_setMacroHatch) + { + Macro::setMacroHatch(false); + EXPECT_EQ(Macro::macroHatch, false); + + Macro::setMacroHatch(true); + EXPECT_EQ(Macro::macroHatch, true); + } + + TEST(MacroTest, DISABLED_setNewBase) + { + Macro::setNewBase(false); + EXPECT_EQ(Macro::newBase, false); + + Macro::setNewBase(true); + EXPECT_EQ(Macro::newBase, true); + } + + TEST(MacroTest, DISABLED_setScouts) + { + EXPECT_EQ(int(Macro::scouts.size()), 0); + + Macro::setSelfID(selfID); + CUnit overlord = CUnit(); + Unit u = Unit(); + } + + TEST(MacroTest, setSelfID) + { + Macro::setSelfID(selfID); + EXPECT_EQ(Macro::selfID, selfID); + } + + TEST(MacroTest, setStopProduction) + { + Macro::setStopProduction(false); + EXPECT_EQ(Macro::stopProduction, false); + + Macro::setStopProduction(true); + EXPECT_EQ(Macro::stopProduction, true); + } +} + +class MacroTestFixture : ::testing::Test +{ + protected: + MacroTestFixture() + { + + } +}; \ No newline at end of file diff --git a/KoraBotTest/MilitaryTest.cpp b/KoraBotTest/MilitaryTest.cpp new file mode 100644 index 0000000..353a53e --- /dev/null +++ b/KoraBotTest/MilitaryTest.cpp @@ -0,0 +1,87 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace MilitaryTest +{ + TEST(MilitaryTest, DISABLED_addSquadlessUnits) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_checkAttack) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_checkDefense) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_checkEnemiesAt) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_checkSquads) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_getAttackLocation) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_getBaseDefense) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_getClosestSquad) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_getIdleSquad) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_getMostVulnerableBase) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_inThreatRange) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_isThreatened) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_likelihoodToAttack) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_sendDefense) + { + EXPECT_EQ(0, 0); + } + + TEST(MilitaryTest, DISABLED_shouldAttackNonMaxed) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/MiningTest.cpp b/KoraBotTest/MiningTest.cpp new file mode 100644 index 0000000..0131810 --- /dev/null +++ b/KoraBotTest/MiningTest.cpp @@ -0,0 +1,17 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Mining.h"; + +using namespace BWAPI; + +namespace MiningTest +{ + TEST(MiningTest, resourceToMine) + { + EXPECT_TRUE(Mining::resourceToMine()); + } +} \ No newline at end of file diff --git a/KoraBotTest/PlayerAssessmentTest.cpp b/KoraBotTest/PlayerAssessmentTest.cpp new file mode 100644 index 0000000..d08fc40 --- /dev/null +++ b/KoraBotTest/PlayerAssessmentTest.cpp @@ -0,0 +1,32 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace PlayerAssessmentTest +{ + TEST(PlayerAssessmentTest, DISABLED_estimateBaseMinedMinerals) + { + EXPECT_EQ(0, 0); + } + + TEST(PlayerAssessmentTest, DISABLED_estimateTotalGas) + { + EXPECT_EQ(0, 0); + } + + TEST(PlayerAssessmentTest, DISABLED_estimateTotalMinerals) + { + EXPECT_EQ(0, 0); + } + + TEST(PlayerAssessmentTest, DISABLED_estimateUnitCount) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/QueueEntryTest.cpp b/KoraBotTest/QueueEntryTest.cpp new file mode 100644 index 0000000..0be1855 --- /dev/null +++ b/KoraBotTest/QueueEntryTest.cpp @@ -0,0 +1,41 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/QueueEntry.h"; + +using namespace BWAPI; + +namespace QueueEntryTest +{ + BWAPI::TechType tech = BWAPI::TechTypes::Burrowing; + BWAPI::UnitType unit = BWAPI::UnitTypes::Zerg_Drone; + BWAPI::UpgradeType upgrade = BWAPI::UpgradeTypes::Zerg_Missile_Attacks; + + QueueEntry techEntry = QueueEntry(tech, false, 0); + QueueEntry unitEntry = QueueEntry(unit, false, 0); + QueueEntry upgradeEntry = QueueEntry(upgrade, false, 0); + + TEST(QueueEntryTest, isTech) + { + EXPECT_TRUE(techEntry.isTech()); + EXPECT_FALSE(techEntry.isUnit()); + EXPECT_FALSE(techEntry.isUpgrade()); + } + + TEST(QueueEntryTest, isUnit) + { + EXPECT_FALSE(unitEntry.isTech()); + EXPECT_TRUE(unitEntry.isUnit()); + EXPECT_FALSE(unitEntry.isUpgrade()); + } + + TEST(QueueEntryTest, isUpgrade) + { + EXPECT_FALSE(upgradeEntry.isTech()); + EXPECT_FALSE(upgradeEntry.isUnit()); + EXPECT_TRUE(upgradeEntry.isUpgrade()); + } +} \ No newline at end of file diff --git a/KoraBotTest/Release/AnalysisTest.obj b/KoraBotTest/Release/AnalysisTest.obj new file mode 100644 index 0000000..936f3c7 Binary files /dev/null and b/KoraBotTest/Release/AnalysisTest.obj differ diff --git a/KoraBotTest/Release/BuildingPlacementTest.obj b/KoraBotTest/Release/BuildingPlacementTest.obj new file mode 100644 index 0000000..9b7ebb2 Binary files /dev/null and b/KoraBotTest/Release/BuildingPlacementTest.obj differ diff --git a/KoraBotTest/Release/CBaseTest.obj b/KoraBotTest/Release/CBaseTest.obj new file mode 100644 index 0000000..d05b12a Binary files /dev/null and b/KoraBotTest/Release/CBaseTest.obj differ diff --git a/KoraBotTest/Release/CUnitTest.obj b/KoraBotTest/Release/CUnitTest.obj new file mode 100644 index 0000000..ccf00bf Binary files /dev/null and b/KoraBotTest/Release/CUnitTest.obj differ diff --git a/KoraBotTest/Release/CheckItemTest.obj b/KoraBotTest/Release/CheckItemTest.obj new file mode 100644 index 0000000..a5c5105 Binary files /dev/null and b/KoraBotTest/Release/CheckItemTest.obj differ diff --git a/KoraBotTest/Release/KoraBotTest.Build.CppClean.log b/KoraBotTest/Release/KoraBotTest.Build.CppClean.log new file mode 100644 index 0000000..83d077e --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.Build.CppClean.log @@ -0,0 +1,20 @@ +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\korabottest.pch +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\vc142.pdb +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\pch.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\utiltest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\squadtest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\scoutingtest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\queueentrytest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\playerassessmenttest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\miningtest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\militarytest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\macrotest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\cunittest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\checkitemtest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\cbasetest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\buildingplacementtest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\analysistest.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\gtest_main.obj +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\korabottest.tlog\cl.command.1.tlog +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\korabottest.tlog\cl.read.1.tlog +d:\_programs\starcraft-old\bwapi-4_4\kora-bot\korabottest\release\korabottest.tlog\cl.write.1.tlog diff --git a/KoraBotTest/Release/KoraBotTest.exe.recipe b/KoraBotTest/Release/KoraBotTest.exe.recipe new file mode 100644 index 0000000..5f3667b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.exe.recipe @@ -0,0 +1,18 @@ + + + + + D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\Release\KoraBotTest.exe + + + + + + + D:\_programs\Starcraft-old\BWAPI-4_4\BWAPILIB\BWAPILIB.vcxproj + + + D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\KoraBot.vcxproj + + + \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.log b/KoraBotTest/Release/KoraBotTest.log new file mode 100644 index 0000000..6da6d0f --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.log @@ -0,0 +1,7 @@ + Analysis.obj : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance + Creating library D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\Release\KoraBotTest.lib and object D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\Release\KoraBotTest.exp +BuildingPlacement.obj : error LNK2001: unresolved external symbol "public: static class BWEM::Map & __cdecl BWEM::Map::Instance(void)" (?Instance@Map@BWEM@@SAAAV12@XZ) +MacroTest.obj : error LNK2001: unresolved external symbol "public: static class BWEM::Map & __cdecl BWEM::Map::Instance(void)" (?Instance@Map@BWEM@@SAAAV12@XZ) +examples.obj : error LNK2001: unresolved external symbol "bool __cdecl BWEM::utils::seaSide(class BWAPI::Point,class BWEM::Map const *)" (?seaSide@utils@BWEM@@YA_NV?$Point@H$07@BWAPI@@PBVMap@2@@Z) +Util.obj : error LNK2001: unresolved external symbol "public: static class BWAPI::Point __cdecl Util::getAreaAverage(class BWEM::Area const *)" (?getAreaAverage@Util@@SA?AV?$Point@H$0CA@@BWAPI@@PBVArea@BWEM@@@Z) +D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\Release\KoraBotTest.exe : fatal error LNK1120: 3 unresolved externals diff --git a/KoraBotTest/Release/KoraBotTest.pch b/KoraBotTest/Release/KoraBotTest.pch new file mode 100644 index 0000000..a4039a1 Binary files /dev/null and b/KoraBotTest/Release/KoraBotTest.pch differ diff --git a/KoraBotTest/Release/KoraBotTest.tlog/CL.command.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/CL.command.1.tlog new file mode 100644 index 0000000..962e4db Binary files /dev/null and b/KoraBotTest/Release/KoraBotTest.tlog/CL.command.1.tlog differ diff --git a/KoraBotTest/Release/KoraBotTest.tlog/CL.read.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/CL.read.1.tlog new file mode 100644 index 0000000..e4f5b49 Binary files /dev/null and b/KoraBotTest/Release/KoraBotTest.tlog/CL.read.1.tlog differ diff --git a/KoraBotTest/Release/KoraBotTest.tlog/CL.write.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/CL.write.1.tlog new file mode 100644 index 0000000..14e8473 Binary files /dev/null and b/KoraBotTest/Release/KoraBotTest.tlog/CL.write.1.tlog differ diff --git a/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.lastbuildstate b/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.lastbuildstate new file mode 100644 index 0000000..3660ff2 --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.29.30133:TargetPlatformVersion=8.1: +Release|Win32|D:\_programs\Starcraft-old\BWAPI-4_4\kora-bot\| diff --git a/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.write.1u.tlog b/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.write.1u.tlog new file mode 100644 index 0000000..641d922 Binary files /dev/null and b/KoraBotTest/Release/KoraBotTest.tlog/KoraBotTest.write.1u.tlog differ diff --git a/KoraBotTest/Release/KoraBotTest.tlog/link.18488.read.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/link.18488.read.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/link.18488.read.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.tlog/link.18488.write.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/link.18488.write.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/link.18488.write.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.tlog/link.command.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/link.command.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/link.command.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.tlog/link.read.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/link.read.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/link.read.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.tlog/link.write.1.tlog b/KoraBotTest/Release/KoraBotTest.tlog/link.write.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/KoraBotTest/Release/KoraBotTest.tlog/link.write.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/KoraBotTest/Release/KoraBotTest.tlog/unsuccessfulbuild b/KoraBotTest/Release/KoraBotTest.tlog/unsuccessfulbuild new file mode 100644 index 0000000..e69de29 diff --git a/KoraBotTest/Release/KoraBotTest.vcxproj.FileListAbsolute.txt b/KoraBotTest/Release/KoraBotTest.vcxproj.FileListAbsolute.txt new file mode 100644 index 0000000..e69de29 diff --git a/KoraBotTest/Release/MacroTest.obj b/KoraBotTest/Release/MacroTest.obj new file mode 100644 index 0000000..d58a3e1 Binary files /dev/null and b/KoraBotTest/Release/MacroTest.obj differ diff --git a/KoraBotTest/Release/MilitaryTest.obj b/KoraBotTest/Release/MilitaryTest.obj new file mode 100644 index 0000000..294a40a Binary files /dev/null and b/KoraBotTest/Release/MilitaryTest.obj differ diff --git a/KoraBotTest/Release/MiningTest.obj b/KoraBotTest/Release/MiningTest.obj new file mode 100644 index 0000000..4770117 Binary files /dev/null and b/KoraBotTest/Release/MiningTest.obj differ diff --git a/KoraBotTest/Release/PlayerAssessmentTest.obj b/KoraBotTest/Release/PlayerAssessmentTest.obj new file mode 100644 index 0000000..e378448 Binary files /dev/null and b/KoraBotTest/Release/PlayerAssessmentTest.obj differ diff --git a/KoraBotTest/Release/QueueEntryTest.obj b/KoraBotTest/Release/QueueEntryTest.obj new file mode 100644 index 0000000..ab6dad5 Binary files /dev/null and b/KoraBotTest/Release/QueueEntryTest.obj differ diff --git a/KoraBotTest/Release/ScoutingTest.obj b/KoraBotTest/Release/ScoutingTest.obj new file mode 100644 index 0000000..766307a Binary files /dev/null and b/KoraBotTest/Release/ScoutingTest.obj differ diff --git a/KoraBotTest/Release/SquadTest.obj b/KoraBotTest/Release/SquadTest.obj new file mode 100644 index 0000000..e2e1420 Binary files /dev/null and b/KoraBotTest/Release/SquadTest.obj differ diff --git a/KoraBotTest/Release/UtilTest.obj b/KoraBotTest/Release/UtilTest.obj new file mode 100644 index 0000000..c621ea3 Binary files /dev/null and b/KoraBotTest/Release/UtilTest.obj differ diff --git a/KoraBotTest/Release/gmock-all.obj b/KoraBotTest/Release/gmock-all.obj new file mode 100644 index 0000000..0adf77b Binary files /dev/null and b/KoraBotTest/Release/gmock-all.obj differ diff --git a/KoraBotTest/Release/gtest-all.obj b/KoraBotTest/Release/gtest-all.obj new file mode 100644 index 0000000..5d5b528 Binary files /dev/null and b/KoraBotTest/Release/gtest-all.obj differ diff --git a/KoraBotTest/Release/gtest_main.obj b/KoraBotTest/Release/gtest_main.obj new file mode 100644 index 0000000..0db9911 Binary files /dev/null and b/KoraBotTest/Release/gtest_main.obj differ diff --git a/KoraBotTest/Release/pch.obj b/KoraBotTest/Release/pch.obj new file mode 100644 index 0000000..27d9232 Binary files /dev/null and b/KoraBotTest/Release/pch.obj differ diff --git a/KoraBotTest/Release/vc142.pdb b/KoraBotTest/Release/vc142.pdb new file mode 100644 index 0000000..b0f8002 Binary files /dev/null and b/KoraBotTest/Release/vc142.pdb differ diff --git a/KoraBotTest/ScoutingTest.cpp b/KoraBotTest/ScoutingTest.cpp new file mode 100644 index 0000000..1b108ea --- /dev/null +++ b/KoraBotTest/ScoutingTest.cpp @@ -0,0 +1,32 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace ScoutingTest +{ + TEST(ScoutingTest, DISABLED_checkNextBase) + { + EXPECT_EQ(0, 0); + } + + TEST(ScoutingTest, DISABLED_getScoutingLocation) + { + EXPECT_EQ(0, 0); + } + + TEST(ScoutingTest, DISABLED_isScout) + { + EXPECT_EQ(0, 0); + } + + TEST(ScoutingTest, DISABLED_locationScouted) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/SquadTest.cpp b/KoraBotTest/SquadTest.cpp new file mode 100644 index 0000000..5f00a7b --- /dev/null +++ b/KoraBotTest/SquadTest.cpp @@ -0,0 +1,42 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace SquadTest +{ + TEST(SquadTest, DISABLED_addUnit) + { + EXPECT_EQ(0, 0); + } + + TEST(SquadTest, DISABLED_attack) + { + EXPECT_EQ(0, 0); + } + + TEST(SquadTest, DISABLED_checkCenter) + { + EXPECT_EQ(0, 0); + } + + TEST(SquadTest, DISABLED_checkDamage) + { + EXPECT_EQ(0, 0); + } + + TEST(SquadTest, DISABLED_contains) + { + EXPECT_EQ(0, 0); + } + + TEST(SquadTest, DISABLED_containsType) + { + EXPECT_EQ(0, 0); + } +} \ No newline at end of file diff --git a/KoraBotTest/UtilTest.cpp b/KoraBotTest/UtilTest.cpp new file mode 100644 index 0000000..9dfb5a2 --- /dev/null +++ b/KoraBotTest/UtilTest.cpp @@ -0,0 +1,155 @@ +#include "pch.h" + +#pragma once +#include + +#include "../Source/BWEM/src/bwem.h"; +#include "../Source/CBase.h"; +#include "../Source/Util.h"; + +using namespace BWAPI; + +namespace UtilTest +{ + TilePosition tp1 = TilePosition(0, 0); + TilePosition tp2 = TilePosition(3, 4); + + Location l1 = Location(tp1, false); + Location l2 = Location(tp2, false); + + BWAPI::UnitType drone = BWAPI::UnitTypes::Zerg_Drone; + BWAPI::UnitType zergling = BWAPI::UnitTypes::Zerg_Zergling; + + TEST(UtilTest, DISABLED_buildWorkerIndex) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_completedBuilding) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_countUnits) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_eggCount) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_findUpgradeBuilding) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getAreaAverage) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getBaseIndex) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getClosestOwnedBase) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, getDistance) + { + EXPECT_EQ(Util::getDistance(tp1, tp2), 5); + } + + TEST(UtilTest, getLocation) + { + Macro::locations.clear(); + Macro::locations.push_back(l1); + Macro::locations.push_back(l2); + EXPECT_EQ(Util::getLocation(tp2), 1); + } + + TEST(UtilTest, DISABLED_getNeighborArea) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getNextExpand) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getPath) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, getQueueIndex) + { + Macro::queue.clear(); + QueueEntry q1 = QueueEntry(drone, false, 0); + QueueEntry q2 = QueueEntry(zergling, false, 0); + + Macro::queue.push_back(q1); + Macro::queue.push_back(q2); + EXPECT_EQ(Util::getQueueIndex(zergling), 1); + } + + TEST(UtilTest, DISABLED_getTargetArea) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getUnitIndex) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_getWalkDistance) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_isAttackingUnit) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_isBasePosition) + { + Macro::bases.clear(); + CBase base = CBase(tp1); + Macro::bases.push_back(base); + EXPECT_TRUE(Util::isBasePosition(tp1)); + EXPECT_FALSE(Util::isBasePosition(tp2)); + } + + TEST(UtilTest, DISABLED_isOwned) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_ownedBy) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_reconstructPath) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_unitCounted) + { + EXPECT_EQ(0, 0); + } + + TEST(UtilTest, DISABLED_workerBuildingTargetCount) + { + EXPECT_EQ(0, 0); + } +} diff --git a/KoraBotTest/packages.config b/KoraBotTest/packages.config new file mode 100644 index 0000000..dbb6954 --- /dev/null +++ b/KoraBotTest/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/KoraBotTest/pch.cpp b/KoraBotTest/pch.cpp new file mode 100644 index 0000000..ef288d8 --- /dev/null +++ b/KoraBotTest/pch.cpp @@ -0,0 +1,6 @@ +// +// pch.cpp +// Include the standard header and generate the precompiled header. +// + +#include "pch.h" diff --git a/KoraBotTest/pch.h b/KoraBotTest/pch.h new file mode 100644 index 0000000..c7a1219 --- /dev/null +++ b/KoraBotTest/pch.h @@ -0,0 +1,7 @@ +// +// pch.h +// Header for standard system include files. +// + +#include +#include \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Source/Analysis.cpp b/Source/Analysis.cpp new file mode 100644 index 0000000..2ebe911 --- /dev/null +++ b/Source/Analysis.cpp @@ -0,0 +1,296 @@ +#include "Analysis.h" + +double Analysis::analyze(TechType tech) +{ + double score = 0; + + return score; +} + +double Analysis::analyze(BWAPI::UnitType unit) +{ + double score = 0; + + switch (unit) + { + case UnitTypes::Zerg_Drone: + score = analyzeDrone(); + break; + case UnitTypes::Zerg_Evolution_Chamber: + score = analyzeEvoChamber(); + break; + case UnitTypes::Zerg_Extractor: + score = analyzeGas(); + break; + case UnitTypes::Zerg_Hatchery: + score = analyzeHatchery(); + break; + case UnitTypes::Zerg_Hive: + score = analyzeHive(); + break; + case UnitTypes::Zerg_Hydralisk: + score = analyzeHydralisk(); + break; + case UnitTypes::Zerg_Hydralisk_Den: + score = analyzeHydraDen(); + break; + case UnitTypes::Zerg_Lair: + score = analyzeLair(); + break; + case UnitTypes::Zerg_Mutalisk: + score = analyzeMutalisk(); + break; + case UnitTypes::Zerg_Overlord: + score = analyzeOverlord(); + break; + case UnitTypes::Zerg_Queens_Nest: + score = analyzeQueensNest(); + break; + case UnitTypes::Zerg_Spawning_Pool: + score = analyzeSpawningPool(); + break; + case UnitTypes::Zerg_Spire: + score = analyzeSpire(); + break; + case UnitTypes::Zerg_Spore_Colony: + score = analyzeSpore(); + break; + case UnitTypes::Zerg_Sunken_Colony: + score = analyzeSunken(); + break; + case UnitTypes::Zerg_Ultralisk: + score = analyzeUltralisk(); + break; + case UnitTypes::Zerg_Ultralisk_Cavern: + score = analyzeUltraCavern(); + break; + case UnitTypes::Zerg_Zergling: + score = analyzeZergling(); + break; + } + + return score; +} + +double Analysis::analyze(UpgradeType upgrade) +{ + double score = 0; + switch (upgrade) + { + case UpgradeTypes::Grooved_Spines: + score = analyzeHydraRange(); + break; + case UpgradeTypes::Metabolic_Boost: + score = analyzeLingSpeed(); + break; + case UpgradeTypes::Muscular_Augments: + score = analyzeHydraSpeed(); + break; + case UpgradeTypes::Pneumatized_Carapace: + score = analyzeOverlordSpeed(); + break; + case UpgradeTypes::Zerg_Carapace: + score = analyzeGroundArmor(); + break; + case UpgradeTypes::Zerg_Missile_Attacks: + score = analyzeMissile(); + break; + } + + return score; +} + +double Analysis::analyzeDrone() +{ + double score = 0; + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Drone) < 65) + { + score = 1; + } + + score -= Util::eggCount(UnitTypes::Zerg_Drone) * 0.02; + + if (Military::likelihoodToAttack() > 0.7) score -= 0.4; + + return score; +} + +double Analysis::analyzeEvoChamber() +{ + double score = 1.0; + if (Military::likelihoodToAttack() > 0.7) score -= 0.4; + return 1.0; +} + +double Analysis::analyzeGas() +{ + return 1.0; +} + +double Analysis::analyzeGroundArmor() +{ + return 1.0; +} + +double Analysis::analyzeHatchery() +{ + double score = 0; + + if (Macro::newBase) + { + score = 2; + } + else + { + score = 2; + } + + return score - Military::likelihoodToAttack(); +} + +double Analysis::analyzeHive() +{ + return 0.8; +} + +double Analysis::analyzeHydraDen() +{ + double score = 0; + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hydralisk_Den) == 0) + { + score += 0.7; + if (Broodwar->self()->supplyUsed() >= 38) score += 0.5; + } + return score; +} + +double Analysis::analyzeHydralisk() +{ + if (Broodwar->self()->supplyUsed() > Broodwar->self()->supplyTotal()) return 0; + + double score = 0.8; + score -= Util::eggCount(UnitTypes::Zerg_Hydralisk) * 0.1; + //if (Military::likelihoodToAttack() > 0.7) score += 0.3; + if (Military::likelihoodToAttack() < 0.2) score -= 0.4; + + if (!Macro::enemyIDs.empty()) + { + score += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Zealot) * 0.02; + score += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Firebat) * 0.02; + } + return score; +} + +double Analysis::analyzeHydraRange() +{ + return 1.0; +} + +double Analysis::analyzeHydraSpeed() +{ + return 1.0; +} + +double Analysis::analyzeLair() +{ + return 0.8; +} + +double Analysis::analyzeLingSpeed() +{ + return 1.0; +} + +double Analysis::analyzeMissile() +{ + return 1.0; +} + +double Analysis::analyzeMutalisk() +{ + double score = 0.7; + score -= Util::eggCount(UnitTypes::Zerg_Mutalisk) * 0.1; + if (!Macro::enemyIDs.empty()) + { + score -= Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Corsair) * 0.05; + score -= Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Valkyrie) * 0.05; + } + return score; +} + +double Analysis::analyzeOverlord() +{ + double score = 1.5; + + if (Military::likelihoodToAttack() > 0.7) score -= 0.1; + + return score; +} + +double Analysis::analyzeOverlordSpeed() +{ + return 1.0; +} + +double Analysis::analyzeQueensNest() +{ + return 1.0; +} + +double Analysis::analyzeSpawningPool() +{ + double score = 0; + + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Spawning_Pool) == 0) + { + score += 0.7; + if (Broodwar->self()->supplyUsed() >= 24) score += 0.5; + } + + return score; +} + +double Analysis::analyzeSpire() +{ + return 1.0; +} + +double Analysis::analyzeSpore() +{ + return 1.0; +} + +double Analysis::analyzeSunken() +{ + return 1.3; +} + +double Analysis::analyzeUltraCavern() +{ + return 1.0; +} + +double Analysis::analyzeUltralisk() +{ + double score = 0.6; + score -= Util::eggCount(UnitTypes::Zerg_Ultralisk) * 0.1; + return score; +} + +double Analysis::analyzeZergling() +{ + double score = 0.6; + score -= Util::eggCount(UnitTypes::Zerg_Zergling) * 0.1; + if (!Macro::enemyIDs.empty()) + { + score -= Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Zealot) * 0.05; + score -= Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Firebat) * 0.05; + } + else + { + score += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Dragoon) * 0.05; + score += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Siege_Tank_Siege_Mode) * 0.05; + score += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Siege_Tank_Tank_Mode) * 0.05; + } + return score; +} diff --git a/Source/Analysis.h b/Source/Analysis.h new file mode 100644 index 0000000..c56b15b --- /dev/null +++ b/Source/Analysis.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Macro.h" +#include "Util.h" + +using namespace BWAPI; + +class Analysis +{ +public: + static double analyze(BWAPI::TechType tech); + static double analyze(BWAPI::UnitType unit); + static double analyze(BWAPI::UpgradeType upgrade); + static double analyzeDrone(); + static double analyzeEvoChamber(); + static double analyzeGas(); + static double analyzeHatchery(); + static double analyzeHive(); + static double analyzeHydraDen(); + static double analyzeHydralisk(); + static double analyzeLair(); + static double analyzeMutalisk(); + static double analyzeOverlord(); + static double analyzeQueensNest(); + static double analyzeSpawningPool(); + static double analyzeSpire(); + static double analyzeSpore(); + static double analyzeSunken(); + static double analyzeUltraCavern(); + static double analyzeUltralisk(); + static double analyzeZergling(); + + static double analyzeGroundArmor(); + static double analyzeHydraRange(); + static double analyzeHydraSpeed(); + static double analyzeLingSpeed(); + static double analyzeMissile(); + static double analyzeOverlordSpeed(); +}; \ No newline at end of file diff --git a/Source/BWEM/EasyBMP_1.06/BSD_(revised)_license.txt b/Source/BWEM/EasyBMP_1.06/BSD_(revised)_license.txt new file mode 100644 index 0000000..6785262 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/BSD_(revised)_license.txt @@ -0,0 +1,10 @@ +Copyright (c) 2005, The EasyBMP Project (http://easybmp.sourceforge.net) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP.cpp b/Source/BWEM/EasyBMP_1.06/EasyBMP.cpp new file mode 100644 index 0000000..c65f060 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP.cpp @@ -0,0 +1,1911 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMP.cpp * +* date added: 03-31-2006 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Actual source file * +* * +*************************************************/ + +#include "EasyBMP.h" + +/* These functions are defined in EasyBMP.h */ + +bool EasyBMPwarnings = true; + +void SetEasyBMPwarningsOff( void ) +{ EasyBMPwarnings = false; } +void SetEasyBMPwarningsOn( void ) +{ EasyBMPwarnings = true; } +bool GetEasyBMPwarningState( void ) +{ return EasyBMPwarnings; } + +/* These functions are defined in EasyBMP_DataStructures.h */ + +int IntPow( int base, int exponent ) +{ + int i; + int output = 1; + for( i=0 ; i < exponent ; i++ ) + { output *= base; } + return output; +} + +BMFH::BMFH() +{ + bfType = 19778; + bfReserved1 = 0; + bfReserved2 = 0; +} + +void BMFH::SwitchEndianess( void ) +{ + bfType = FlipWORD( bfType ); + bfSize = FlipDWORD( bfSize ); + bfReserved1 = FlipWORD( bfReserved1 ); + bfReserved2 = FlipWORD( bfReserved2 ); + bfOffBits = FlipDWORD( bfOffBits ); + return; +} + +BMIH::BMIH() +{ + biPlanes = 1; + biCompression = 0; + biXPelsPerMeter = DefaultXPelsPerMeter; + biYPelsPerMeter = DefaultYPelsPerMeter; + biClrUsed = 0; + biClrImportant = 0; +} + +void BMIH::SwitchEndianess( void ) +{ + biSize = FlipDWORD( biSize ); + biWidth = FlipDWORD( biWidth ); + biHeight = FlipDWORD( biHeight ); + biPlanes = FlipWORD( biPlanes ); + biBitCount = FlipWORD( biBitCount ); + biCompression = FlipDWORD( biCompression ); + biSizeImage = FlipDWORD( biSizeImage ); + biXPelsPerMeter = FlipDWORD( biXPelsPerMeter ); + biYPelsPerMeter = FlipDWORD( biYPelsPerMeter ); + biClrUsed = FlipDWORD( biClrUsed ); + biClrImportant = FlipDWORD( biClrImportant ); + return; +} + +void BMIH::display( void ) +{ + using namespace std; + cout << "biSize: " << (int) biSize << endl + << "biWidth: " << (int) biWidth << endl + << "biHeight: " << (int) biHeight << endl + << "biPlanes: " << (int) biPlanes << endl + << "biBitCount: " << (int) biBitCount << endl + << "biCompression: " << (int) biCompression << endl + << "biSizeImage: " << (int) biSizeImage << endl + << "biXPelsPerMeter: " << (int) biXPelsPerMeter << endl + << "biYPelsPerMeter: " << (int) biYPelsPerMeter << endl + << "biClrUsed: " << (int) biClrUsed << endl + << "biClrImportant: " << (int) biClrImportant << endl << endl; +} + +void BMFH::display( void ) +{ + using namespace std; + cout << "bfType: " << (int) bfType << endl + << "bfSize: " << (int) bfSize << endl + << "bfReserved1: " << (int) bfReserved1 << endl + << "bfReserved2: " << (int) bfReserved2 << endl + << "bfOffBits: " << (int) bfOffBits << endl << endl; +} + +/* These functions are defined in EasyBMP_BMP.h */ + +RGBApixel BMP::GetPixel( int i, int j ) const +{ + using namespace std; + bool Warn = false; + if( i >= Width ) + { i = Width-1; Warn = true; } + if( i < 0 ) + { i = 0; Warn = true; } + if( j >= Height ) + { j = Height-1; Warn = true; } + if( j < 0 ) + { j = 0; Warn = true; } + if( Warn && EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl + << " Truncating request to fit in the range [0," + << Width-1 << "] x [0," << Height-1 << "]." << endl; + } + return Pixels[i][j]; +} + +bool BMP::SetPixel( int i, int j, RGBApixel NewPixel ) +{ + Pixels[i][j] = NewPixel; + return true; +} + + +bool BMP::SetColor( int ColorNumber , RGBApixel NewColor ) +{ + using namespace std; + if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to change color table for a BMP object" << endl + << " that lacks a color table. Ignoring request." << endl; + } + return false; + } + if( !Colors ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to set a color, but the color table" << endl + << " is not defined. Ignoring request." << endl; + } + return false; + } + if( ColorNumber >= TellNumberOfColors() ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Requested color number " + << ColorNumber << " is outside the allowed" << endl + << " range [0," << TellNumberOfColors()-1 + << "]. Ignoring request to set this color." << endl; + } + return false; + } + Colors[ColorNumber] = NewColor; + return true; +} + +// RGBApixel BMP::GetColor( int ColorNumber ) const +RGBApixel BMP::GetColor( int ColorNumber ) +{ + RGBApixel Output; + Output.Red = 255; + Output.Green = 255; + Output.Blue = 255; + Output.Alpha = 0; + + using namespace std; + if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to access color table for a BMP object" << endl + << " that lacks a color table. Ignoring request." << endl; + } + return Output; + } + if( !Colors ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Requested a color, but the color table" << endl + << " is not defined. Ignoring request." << endl; + } + return Output; + } + if( ColorNumber >= TellNumberOfColors() ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Requested color number " + << ColorNumber << " is outside the allowed" << endl + << " range [0," << TellNumberOfColors()-1 + << "]. Ignoring request to get this color." << endl; + } + return Output; + } + Output = Colors[ColorNumber]; + return Output; +} + +BMP::BMP() +{ + Width = 1; + Height = 1; + BitDepth = 24; + Pixels = new RGBApixel* [Width]; + Pixels[0] = new RGBApixel [Height]; + Colors = NULL; + + XPelsPerMeter = 0; + YPelsPerMeter = 0; + + MetaData1 = NULL; + SizeOfMetaData1 = 0; + MetaData2 = NULL; + SizeOfMetaData2 = 0; +} + +// BMP::BMP( const BMP& Input ) +BMP::BMP( BMP& Input ) +{ + // first, make the image empty. + + Width = 1; + Height = 1; + BitDepth = 24; + Pixels = new RGBApixel* [Width]; + Pixels[0] = new RGBApixel [Height]; + Colors = NULL; + XPelsPerMeter = 0; + YPelsPerMeter = 0; + + MetaData1 = NULL; + SizeOfMetaData1 = 0; + MetaData2 = NULL; + SizeOfMetaData2 = 0; + + // now, set the correct bit depth + + SetBitDepth( Input.TellBitDepth() ); + + // set the correct pixel size + + SetSize( Input.TellWidth() , Input.TellHeight() ); + + // set the DPI information from Input + + SetDPI( Input.TellHorizontalDPI() , Input.TellVerticalDPI() ); + + // if there is a color table, get all the colors + + if( BitDepth == 1 || BitDepth == 4 || + BitDepth == 8 ) + { + for( int k=0 ; k < TellNumberOfColors() ; k++ ) + { + SetColor( k, Input.GetColor( k )); + } + } + + // get all the pixels + + for( int j=0; j < Height ; j++ ) + { + for( int i=0; i < Width ; i++ ) + { + Pixels[i][j] = *Input(i,j); +// Pixels[i][j] = Input.GetPixel(i,j); // *Input(i,j); + } + } +} + +BMP::~BMP() +{ + int i; + for(i=0;i= Width ) + { i = Width-1; Warn = true; } + if( i < 0 ) + { i = 0; Warn = true; } + if( j >= Height ) + { j = Height-1; Warn = true; } + if( j < 0 ) + { j = 0; Warn = true; } + if( Warn && EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl + << " Truncating request to fit in the range [0," + << Width-1 << "] x [0," << Height-1 << "]." << endl; + } + return &(Pixels[i][j]); +} + +// int BMP::TellBitDepth( void ) const +int BMP::TellBitDepth( void ) +{ return BitDepth; } + +// int BMP::TellHeight( void ) const +int BMP::TellHeight( void ) +{ return Height; } + +// int BMP::TellWidth( void ) const +int BMP::TellWidth( void ) +{ return Width; } + +// int BMP::TellNumberOfColors( void ) const +int BMP::TellNumberOfColors( void ) +{ + int output = IntPow( 2, BitDepth ); + if( BitDepth == 32 ) + { output = IntPow( 2, 24 ); } + return output; +} + +bool BMP::SetBitDepth( int NewDepth ) +{ + using namespace std; + if( NewDepth != 1 && NewDepth != 4 && + NewDepth != 8 && NewDepth != 16 && + NewDepth != 24 && NewDepth != 32 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: User attempted to set unsupported bit depth " + << NewDepth << "." << endl + << " Bit depth remains unchanged at " + << BitDepth << "." << endl; + } + return false; + } + + BitDepth = NewDepth; + if( Colors ) + { delete [] Colors; } + int NumberOfColors = IntPow( 2, BitDepth ); + if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 ) + { Colors = new RGBApixel [NumberOfColors]; } + else + { Colors = NULL; } + if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 ) + { CreateStandardColorTable(); } + + return true; +} + +bool BMP::SetSize(int NewWidth , int NewHeight ) +{ + using namespace std; + if( NewWidth <= 0 || NewHeight <= 0 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: User attempted to set a non-positive width or height." << endl + << " Size remains unchanged at " + << Width << " x " << Height << "." << endl; + } + return false; + } + + int i,j; + + for(i=0;i -1 ) + { + bool Success = false; + if( BitDepth == 32 ) + { Success = Write32bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 24 ) + { Success = Write24bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 8 ) + { Success = Write8bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 4 ) + { Success = Write4bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 1 ) + { Success = Write1bitRow( Buffer, BufferSize, j ); } + if( Success ) + { + int BytesWritten = (int) fwrite( (char*) Buffer, 1, BufferSize, fp ); + if( BytesWritten != BufferSize ) + { Success = false; } + } + if( !Success ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Could not write proper amount of data." << endl; + } + j = -1; + } + j--; + } + + delete [] Buffer; + } + + if( BitDepth == 16 ) + { + // write the bit masks + + ebmpWORD BlueMask = 31; // bits 12-16 + ebmpWORD GreenMask = 2016; // bits 6-11 + ebmpWORD RedMask = 63488; // bits 1-5 + ebmpWORD ZeroWORD; + + if( IsBigEndian() ) + { RedMask = FlipWORD( RedMask ); } + fwrite( (char*) &RedMask , 2 , 1 , fp ); + fwrite( (char*) &ZeroWORD , 2 , 1 , fp ); + + if( IsBigEndian() ) + { GreenMask = FlipWORD( GreenMask ); } + fwrite( (char*) &GreenMask , 2 , 1 , fp ); + fwrite( (char*) &ZeroWORD , 2 , 1 , fp ); + + if( IsBigEndian() ) + { BlueMask = FlipWORD( BlueMask ); } + fwrite( (char*) &BlueMask , 2 , 1 , fp ); + fwrite( (char*) &ZeroWORD , 2 , 1 , fp ); + + int DataBytes = Width*2; + int PaddingBytes = ( 4 - DataBytes % 4 ) % 4; + + // write the actual pixels + + for( j=Height-1 ; j >= 0 ; j-- ) + { + // write all row pixel data + i=0; + int WriteNumber = 0; + while( WriteNumber < DataBytes ) + { + ebmpWORD TempWORD; + + ebmpWORD RedWORD = (ebmpWORD) ((Pixels[i][j]).Red / 8); + ebmpWORD GreenWORD = (ebmpWORD) ((Pixels[i][j]).Green / 4); + ebmpWORD BlueWORD = (ebmpWORD) ((Pixels[i][j]).Blue / 8); + + TempWORD = (RedWORD<<11) + (GreenWORD<<5) + BlueWORD; + if( IsBigEndian() ) + { TempWORD = FlipWORD( TempWORD ); } + + fwrite( (char*) &TempWORD , 2, 1, fp); + WriteNumber += 2; + i++; + } + // write any necessary row padding + WriteNumber = 0; + while( WriteNumber < PaddingBytes ) + { + ebmpBYTE TempBYTE; + fwrite( (char*) &TempBYTE , 1, 1, fp); + WriteNumber++; + } + } + + } + + fclose(fp); + return true; +} + +bool BMP::ReadFromFile( const char* FileName ) +{ + using namespace std; + if( !EasyBMPcheckDataSize() ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Data types are wrong size!" << endl + << " You may need to mess with EasyBMP_DataTypes.h" << endl + << " to fix these errors, and then recompile." << endl + << " All 32-bit and 64-bit machines should be" << endl + << " supported, however." << endl << endl; + } + return false; + } + + FILE* fp = fopen( FileName, "rb" ); + if( fp == NULL ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Cannot open file " + << FileName << " for input." << endl; + } + SetBitDepth(1); + SetSize(1,1); + return false; + } + + // read the file header + + BMFH bmfh; + bool NotCorrupted = true; + + NotCorrupted &= SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD), 1, fp); + + bool IsBitmap = false; + + if( IsBigEndian() && bmfh.bfType == 16973 ) + { IsBitmap = true; } + if( !IsBigEndian() && bmfh.bfType == 19778 ) + { IsBitmap = true; } + + if( !IsBitmap ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName + << " is not a Windows BMP file!" << endl; + } + fclose( fp ); + return false; + } + + NotCorrupted &= SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1, fp); + NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1, fp); + NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1, fp); + NotCorrupted &= SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp); + + if( IsBigEndian() ) + { bmfh.SwitchEndianess(); } + + // read the info header + + BMIH bmih; + + NotCorrupted &= SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1, fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1, fp); + + NotCorrupted &= SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp); + NotCorrupted &= SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp); + + if( IsBigEndian() ) + { bmih.SwitchEndianess(); } + + // a safety catch: if any of the header information didn't read properly, abort + // future idea: check to see if at least most is self-consistent + + if( !NotCorrupted ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName + << " is obviously corrupted." << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + + XPelsPerMeter = bmih.biXPelsPerMeter; + YPelsPerMeter = bmih.biYPelsPerMeter; + + // if bmih.biCompression 1 or 2, then the file is RLE compressed + + if( bmih.biCompression == 1 || bmih.biCompression == 2 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName << " is (RLE) compressed." << endl + << " EasyBMP does not support compression." << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + + // if bmih.biCompression > 3, then something strange is going on + // it's probably an OS2 bitmap file. + + if( bmih.biCompression > 3 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName << " is in an unsupported format." + << endl + << " (bmih.biCompression = " + << bmih.biCompression << ")" << endl + << " The file is probably an old OS2 bitmap or corrupted." + << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + + if( bmih.biCompression == 3 && bmih.biBitCount != 16 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName + << " uses bit fields and is not a" << endl + << " 16-bit file. This is not supported." << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + + // set the bit depth + + int TempBitDepth = (int) bmih.biBitCount; + if( TempBitDepth != 1 && TempBitDepth != 4 + && TempBitDepth != 8 && TempBitDepth != 16 + && TempBitDepth != 24 && TempBitDepth != 32 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName << " has unrecognized bit depth." << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + SetBitDepth( (int) bmih.biBitCount ); + + // set the size + + if( (int) bmih.biWidth <= 0 || (int) bmih.biHeight <= 0 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: " << FileName + << " has a non-positive width or height." << endl; + } + SetSize(1,1); + SetBitDepth(1); + fclose(fp); + return false; + } + SetSize( (int) bmih.biWidth , (int) bmih.biHeight ); + + // some preliminaries + + double dBytesPerPixel = ( (double) BitDepth ) / 8.0; + double dBytesPerRow = dBytesPerPixel * (Width+0.0); + dBytesPerRow = ceil(dBytesPerRow); + + int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4; + if( BytePaddingPerRow == 4 ) + { BytePaddingPerRow = 0; } + + // if < 16 bits, read the palette + + if( BitDepth < 16 ) + { + // determine the number of colors specified in the + // color table + + int NumberOfColorsToRead = ((int) bmfh.bfOffBits - 54 )/4; + if( NumberOfColorsToRead > IntPow(2,BitDepth) ) + { NumberOfColorsToRead = IntPow(2,BitDepth); } + + if( NumberOfColorsToRead < TellNumberOfColors() ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: file " << FileName << " has an underspecified" << endl + << " color table. The table will be padded with extra" << endl + << " white (255,255,255,0) entries." << endl; + } + } + + int n; + for( n=0; n < NumberOfColorsToRead ; n++ ) + { + SafeFread( (char*) &(Colors[n]) , 4 , 1 , fp); + } + for( n=NumberOfColorsToRead ; n < TellNumberOfColors() ; n++ ) + { + RGBApixel WHITE; + WHITE.Red = 255; + WHITE.Green = 255; + WHITE.Blue = 255; + WHITE.Alpha = 0; + SetColor( n , WHITE ); + } + + + } + + // skip blank data if bfOffBits so indicates + + int BytesToSkip = bmfh.bfOffBits - 54;; + if( BitDepth < 16 ) + { BytesToSkip -= 4*IntPow(2,BitDepth); } + if( BitDepth == 16 && bmih.biCompression == 3 ) + { BytesToSkip -= 3*4; } + if( BytesToSkip < 0 ) + { BytesToSkip = 0; } + if( BytesToSkip > 0 && BitDepth != 16 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Extra meta data detected in file " << FileName << endl + << " Data will be skipped." << endl; + } + ebmpBYTE* TempSkipBYTE; + TempSkipBYTE = new ebmpBYTE [BytesToSkip]; + SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp); + delete [] TempSkipBYTE; + } + + // This code reads 1, 4, 8, 24, and 32-bpp files + // with a more-efficient buffered technique. + + int i,j; + if( BitDepth != 16 ) + { + int BufferSize = (int) ( (Width*BitDepth) / 8.0 ); + while( 8*BufferSize < Width*BitDepth ) + { BufferSize++; } + while( BufferSize % 4 ) + { BufferSize++; } + ebmpBYTE* Buffer; + Buffer = new ebmpBYTE [BufferSize]; + j= Height-1; + while( j > -1 ) + { + int BytesRead = (int) fread( (char*) Buffer, 1, BufferSize, fp ); + if( BytesRead < BufferSize ) + { + j = -1; + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Could not read proper amount of data." << endl; + } + } + else + { + bool Success = false; + if( BitDepth == 1 ) + { Success = Read1bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 4 ) + { Success = Read4bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 8 ) + { Success = Read8bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 24 ) + { Success = Read24bitRow( Buffer, BufferSize, j ); } + if( BitDepth == 32 ) + { Success = Read32bitRow( Buffer, BufferSize, j ); } + if( !Success ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Could not read enough pixel data!" << endl; + } + j = -1; + } + } + j--; + } + delete [] Buffer; + } + + if( BitDepth == 16 ) + { + int DataBytes = Width*2; + int PaddingBytes = ( 4 - DataBytes % 4 ) % 4; + + // set the default mask + + ebmpWORD BlueMask = 31; // bits 12-16 + ebmpWORD GreenMask = 992; // bits 7-11 + ebmpWORD RedMask = 31744; // bits 2-6 + + // read the bit fields, if necessary, to + // override the default 5-5-5 mask + + if( bmih.biCompression != 0 ) + { + // read the three bit masks + + ebmpWORD TempMaskWORD; +// ebmpWORD ZeroWORD; + + SafeFread( (char*) &RedMask , 2 , 1 , fp ); + if( IsBigEndian() ) + { RedMask = FlipWORD(RedMask); } + SafeFread( (char*) &TempMaskWORD , 2, 1, fp ); + + SafeFread( (char*) &GreenMask , 2 , 1 , fp ); + if( IsBigEndian() ) + { GreenMask = FlipWORD(GreenMask); } + SafeFread( (char*) &TempMaskWORD , 2, 1, fp ); + + SafeFread( (char*) &BlueMask , 2 , 1 , fp ); + if( IsBigEndian() ) + { BlueMask = FlipWORD(BlueMask); } + SafeFread( (char*) &TempMaskWORD , 2, 1, fp ); + } + + // read and skip any meta data + + if( BytesToSkip > 0 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Extra meta data detected in file " + << FileName << endl + << " Data will be skipped." << endl; + } + ebmpBYTE* TempSkipBYTE; + TempSkipBYTE = new ebmpBYTE [BytesToSkip]; + SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp); + delete [] TempSkipBYTE; + } + + // determine the red, green and blue shifts + + int GreenShift = 0; + ebmpWORD TempShiftWORD = GreenMask; + while( TempShiftWORD > 31 ) + { TempShiftWORD = TempShiftWORD>>1; GreenShift++; } + int BlueShift = 0; + TempShiftWORD = BlueMask; + while( TempShiftWORD > 31 ) + { TempShiftWORD = TempShiftWORD>>1; BlueShift++; } + int RedShift = 0; + TempShiftWORD = RedMask; + while( TempShiftWORD > 31 ) + { TempShiftWORD = TempShiftWORD>>1; RedShift++; } + + // read the actual pixels + + for( j=Height-1 ; j >= 0 ; j-- ) + { + i=0; + int ReadNumber = 0; + while( ReadNumber < DataBytes ) + { + ebmpWORD TempWORD; + SafeFread( (char*) &TempWORD , 2 , 1 , fp ); + if( IsBigEndian() ) + { TempWORD = FlipWORD(TempWORD); } + ReadNumber += 2; + + ebmpWORD Red = RedMask & TempWORD; + ebmpWORD Green = GreenMask & TempWORD; + ebmpWORD Blue = BlueMask & TempWORD; + + ebmpBYTE BlueBYTE = (ebmpBYTE) (8*(Blue>>BlueShift)); + ebmpBYTE GreenBYTE = (ebmpBYTE) (8*(Green>>GreenShift)); + ebmpBYTE RedBYTE = (ebmpBYTE) (8*(Red>>RedShift)); + + (Pixels[i][j]).Red = RedBYTE; + (Pixels[i][j]).Green = GreenBYTE; + (Pixels[i][j]).Blue = BlueBYTE; + + i++; + } + ReadNumber = 0; + while( ReadNumber < PaddingBytes ) + { + ebmpBYTE TempBYTE; + SafeFread( (char*) &TempBYTE , 1, 1, fp); + ReadNumber++; + } + } + + } + + fclose(fp); + return true; +} + +bool BMP::CreateStandardColorTable( void ) +{ + using namespace std; + if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl + << " depth that does not require a color table." << endl + << " Ignoring request." << endl; + } + return false; + } + + if( BitDepth == 1 ) + { + int i; + for( i=0 ; i < 2 ; i++ ) + { + Colors[i].Red = ebmpBYTE(i * 255); + Colors[i].Green = ebmpBYTE(i * 255); + Colors[i].Blue = ebmpBYTE(i * 255); + Colors[i].Alpha = 0; + } + return true; + } + + if( BitDepth == 4 ) + { + int i = 0; + int j,k,ell; + + // simplify the code for the first 8 colors + for( ell=0 ; ell < 2 ; ell++ ) + { + for( k=0 ; k < 2 ; k++ ) + { + for( j=0 ; j < 2 ; j++ ) + { + Colors[i].Red = ebmpBYTE(j * 128); + Colors[i].Green = ebmpBYTE(k * 128); + Colors[i].Blue = ebmpBYTE(ell * 128); + i++; + } + } + } + + // simplify the code for the last 8 colors + for( ell=0 ; ell < 2 ; ell++ ) + { + for( k=0 ; k < 2 ; k++ ) + { + for( j=0 ; j < 2 ; j++ ) + { + Colors[i].Red = ebmpBYTE(j * 255); + Colors[i].Green = ebmpBYTE(k * 255); + Colors[i].Blue = ebmpBYTE(ell * 255); + i++; + } + } + } + + // overwrite the duplicate color + i=8; + Colors[i].Red = 192; + Colors[i].Green = 192; + Colors[i].Blue = 192; + + for( i=0 ; i < 16 ; i++ ) + { Colors[i].Alpha = 0; } + return true; + } + + if( BitDepth == 8 ) + { + int i=0; + int j,k,ell; + + // do an easy loop, which works for all but colors + // 0 to 9 and 246 to 255 + for( ell=0 ; ell < 4 ; ell++ ) + { + for( k=0 ; k < 8 ; k++ ) + { + for( j=0; j < 8 ; j++ ) + { + Colors[i].Red = ebmpBYTE(j * 32); + Colors[i].Green = ebmpBYTE(k * 32); + Colors[i].Blue = ebmpBYTE(ell * 64); + Colors[i].Alpha = ebmpBYTE(0); + i++; + } + } + } + + // now redo the first 8 colors + i=0; + for( ell=0 ; ell < 2 ; ell++ ) + { + for( k=0 ; k < 2 ; k++ ) + { + for( j=0; j < 2 ; j++ ) + { + Colors[i].Red = ebmpBYTE(j * 128); + Colors[i].Green = ebmpBYTE(k * 128); + Colors[i].Blue = ebmpBYTE(ell * 128); + i++; + } + } + } + + // overwrite colors 7, 8, 9 + i=7; + Colors[i].Red = 192; + Colors[i].Green = 192; + Colors[i].Blue = 192; + i++; // 8 + Colors[i].Red = 192; + Colors[i].Green = 220; + Colors[i].Blue = 192; + i++; // 9 + Colors[i].Red = 166; + Colors[i].Green = 202; + Colors[i].Blue = 240; + + // overwrite colors 246 to 255 + i=246; + Colors[i].Red = 255; + Colors[i].Green = 251; + Colors[i].Blue = 240; + i++; // 247 + Colors[i].Red = 160; + Colors[i].Green = 160; + Colors[i].Blue = 164; + i++; // 248 + Colors[i].Red = 128; + Colors[i].Green = 128; + Colors[i].Blue = 128; + i++; // 249 + Colors[i].Red = 255; + Colors[i].Green = 0; + Colors[i].Blue = 0; + i++; // 250 + Colors[i].Red = 0; + Colors[i].Green = 255; + Colors[i].Blue = 0; + i++; // 251 + Colors[i].Red = 255; + Colors[i].Green = 255; + Colors[i].Blue = 0; + i++; // 252 + Colors[i].Red = 0; + Colors[i].Green = 0; + Colors[i].Blue = 255; + i++; // 253 + Colors[i].Red = 255; + Colors[i].Green = 0; + Colors[i].Blue = 255; + i++; // 254 + Colors[i].Red = 0; + Colors[i].Green = 255; + Colors[i].Blue = 255; + i++; // 255 + Colors[i].Red = 255; + Colors[i].Green = 255; + Colors[i].Blue = 255; + + return true; + } + return true; +} + +bool SafeFread( char* buffer, int size, int number, FILE* fp ) +{ + using namespace std; + int ItemsRead; + if( feof(fp) ) + { return false; } + ItemsRead = (int) fread( buffer , size , number , fp ); + if( ItemsRead < number ) + { return false; } + return true; +} + +void BMP::SetDPI( int HorizontalDPI, int VerticalDPI ) +{ + XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 ); + YPelsPerMeter = (int) ( VerticalDPI * 39.37007874015748 ); +} + +// int BMP::TellVerticalDPI( void ) const +int BMP::TellVerticalDPI( void ) +{ + if( !YPelsPerMeter ) + { YPelsPerMeter = DefaultYPelsPerMeter; } + return (int) ( YPelsPerMeter / (double) 39.37007874015748 ); +} + +// int BMP::TellHorizontalDPI( void ) const +int BMP::TellHorizontalDPI( void ) +{ + if( !XPelsPerMeter ) + { XPelsPerMeter = DefaultXPelsPerMeter; } + return (int) ( XPelsPerMeter / (double) 39.37007874015748 ); +} + +/* These functions are defined in EasyBMP_VariousBMPutilities.h */ + +BMFH GetBMFH( const char* szFileNameIn ) +{ + using namespace std; + BMFH bmfh; + + FILE* fp; + fp = fopen( szFileNameIn,"rb"); + + if( !fp ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Cannot initialize from file " + << szFileNameIn << "." << endl + << " File cannot be opened or does not exist." + << endl; + } + bmfh.bfType = 0; + return bmfh; + } + + SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp ); + SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp ); + SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp ); + SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp ); + + fclose( fp ); + + if( IsBigEndian() ) + { bmfh.SwitchEndianess(); } + + return bmfh; +} + +BMIH GetBMIH( const char* szFileNameIn ) +{ + using namespace std; + BMFH bmfh; + BMIH bmih; + + FILE* fp; + fp = fopen( szFileNameIn,"rb"); + + if( !fp ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Cannot initialize from file " + << szFileNameIn << "." << endl + << " File cannot be opened or does not exist." + << endl; + } + return bmih; + } + + // read the bmfh, i.e., first 14 bytes (just to get it out of the way); + + ebmpBYTE TempBYTE; + int i; + for( i = 14 ; i > 0 ; i-- ) + { SafeFread( (char*) &TempBYTE , sizeof(ebmpBYTE) , 1, fp ); } + + // read the bmih + + SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp ); + + SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); + + SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp ); + SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp ); + + fclose( fp ); + + if( IsBigEndian() ) + { bmih.SwitchEndianess(); } + + return bmih; +} + +void DisplayBitmapInfo( const char* szFileNameIn ) +{ + using namespace std; + FILE* fp; + fp = fopen( szFileNameIn,"rb"); + + if( !fp ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: Cannot initialize from file " + << szFileNameIn << "." << endl + << " File cannot be opened or does not exist." + << endl; + } + return; + } + fclose( fp ); + + // don't duplicate work! Just use the functions from above! + + BMFH bmfh = GetBMFH(szFileNameIn); + BMIH bmih = GetBMIH(szFileNameIn); + + cout << "File information for file " << szFileNameIn + << ":" << endl << endl; + + cout << "BITMAPFILEHEADER:" << endl + << "bfType: " << bmfh.bfType << endl + << "bfSize: " << bmfh.bfSize << endl + << "bfReserved1: " << bmfh.bfReserved1 << endl + << "bfReserved2: " << bmfh.bfReserved2 << endl + << "bfOffBits: " << bmfh.bfOffBits << endl << endl; + + cout << "BITMAPINFOHEADER:" << endl + << "biSize: " << bmih.biSize << endl + << "biWidth: " << bmih.biWidth << endl + << "biHeight: " << bmih.biHeight << endl + << "biPlanes: " << bmih.biPlanes << endl + << "biBitCount: " << bmih.biBitCount << endl + << "biCompression: " << bmih.biCompression << endl + << "biSizeImage: " << bmih.biSizeImage << endl + << "biXPelsPerMeter: " << bmih.biXPelsPerMeter << endl + << "biYPelsPerMeter: " << bmih.biYPelsPerMeter << endl + << "biClrUsed: " << bmih.biClrUsed << endl + << "biClrImportant: " << bmih.biClrImportant << endl << endl; + return; +} + +int GetBitmapColorDepth( const char* szFileNameIn ) +{ + BMIH bmih = GetBMIH( szFileNameIn ); + return (int) bmih.biBitCount; +} + +void PixelToPixelCopy( BMP& From, int FromX, int FromY, + BMP& To, int ToX, int ToY) +{ + *To(ToX,ToY) = *From(FromX,FromY); + return; +} + +void PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY, + BMP& To, int ToX, int ToY, + RGBApixel& Transparent ) +{ + if( From(FromX,FromY)->Red != Transparent.Red || + From(FromX,FromY)->Green != Transparent.Green || + From(FromX,FromY)->Blue != Transparent.Blue ) + { *To(ToX,ToY) = *From(FromX,FromY); } + return; +} + +void RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT, + BMP& To, int ToX, int ToY ) +{ + // make sure the conventions are followed + if( FromB < FromT ) + { int Temp = FromT; FromT = FromB; FromB = Temp; } + + // make sure that the copied regions exist in both bitmaps + if( FromR >= From.TellWidth() ) + { FromR = From.TellWidth()-1; } + if( FromL < 0 ){ FromL = 0; } + + if( FromB >= From.TellHeight() ) + { FromB = From.TellHeight()-1; } + if( FromT < 0 ){ FromT = 0; } + + if( ToX+(FromR-FromL) >= To.TellWidth() ) + { FromR = To.TellWidth()-1+FromL-ToX; } + if( ToY+(FromB-FromT) >= To.TellHeight() ) + { FromB = To.TellHeight()-1+FromT-ToY; } + + int i,j; + for( j=FromT ; j <= FromB ; j++ ) + { + for( i=FromL ; i <= FromR ; i++ ) + { + PixelToPixelCopy( From, i,j, + To, ToX+(i-FromL), ToY+(j-FromT) ); + } + } + + return; +} + +void RangedPixelToPixelCopyTransparent( + BMP& From, int FromL , int FromR, int FromB, int FromT, + BMP& To, int ToX, int ToY , + RGBApixel& Transparent ) +{ + // make sure the conventions are followed + if( FromB < FromT ) + { int Temp = FromT; FromT = FromB; FromB = Temp; } + + // make sure that the copied regions exist in both bitmaps + if( FromR >= From.TellWidth() ) + { FromR = From.TellWidth()-1; } + if( FromL < 0 ){ FromL = 0; } + + if( FromB >= From.TellHeight() ) + { FromB = From.TellHeight()-1; } + if( FromT < 0 ){ FromT = 0; } + + if( ToX+(FromR-FromL) >= To.TellWidth() ) + { FromR = To.TellWidth()-1+FromL-ToX; } + if( ToY+(FromB-FromT) >= To.TellHeight() ) + { FromB = To.TellHeight()-1+FromT-ToY; } + + int i,j; + for( j=FromT ; j <= FromB ; j++ ) + { + for( i=FromL ; i <= FromR ; i++ ) + { + PixelToPixelCopyTransparent( From, i,j, + To, ToX+(i-FromL), ToY+(j-FromT) , + Transparent); + } + } + + return; +} + +bool CreateGrayscaleColorTable( BMP& InputImage ) +{ + using namespace std; + int BitDepth = InputImage.TellBitDepth(); + if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl + << " depth that does not require a color table." << endl + << " Ignoring request." << endl; + } + return false; + } + int i; + int NumberOfColors = InputImage.TellNumberOfColors(); + + ebmpBYTE StepSize; + if( BitDepth != 1 ) + { + StepSize = ebmpBYTE(255 / (NumberOfColors - 1)); + } + else + { StepSize = 255; } + + for( i=0 ; i < NumberOfColors ; i++ ) + { + ebmpBYTE TempBYTE = ebmpBYTE(i*StepSize); + RGBApixel TempColor; + TempColor.Red = TempBYTE; + TempColor.Green = TempBYTE; + TempColor.Blue = TempBYTE; + TempColor.Alpha = 0; + InputImage.SetColor( i , TempColor ); + } + return true; +} + +bool BMP::Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width*4 > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { memcpy( (char*) &(Pixels[i][Row]), (char*) Buffer+4*i, 4 ); } + return true; +} + +bool BMP::Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width*3 > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { memcpy( (char*) &(Pixels[i][Row]), Buffer+3*i, 3 ); } + return true; +} + +bool BMP::Read8bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { + int Index = Buffer[i]; + *( this->operator()(i,Row) )= GetColor(Index); + } + return true; +} + +bool BMP::Read4bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int Shifts[2] = {4 ,0 }; + int Masks[2] = {240,15}; + + int i=0; + int j; + int k=0; + if( Width > 2*BufferSize ) + { return false; } + while( i < Width ) + { + j=0; + while( j < 2 && i < Width ) + { + int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]); + *( this->operator()(i,Row) )= GetColor(Index); + i++; j++; + } + k++; + } + return true; +} +bool BMP::Read1bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int Shifts[8] = {7 ,6 ,5 ,4 ,3,2,1,0}; + int Masks[8] = {128,64,32,16,8,4,2,1}; + + int i=0; + int j; + int k=0; + + if( Width > 8*BufferSize ) + { return false; } + while( i < Width ) + { + j=0; + while( j < 8 && i < Width ) + { + int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]); + *( this->operator()(i,Row) )= GetColor(Index); + i++; j++; + } + k++; + } + return true; +} + +bool BMP::Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width*4 > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { memcpy( (char*) Buffer+4*i, (char*) &(Pixels[i][Row]), 4 ); } + return true; +} + +bool BMP::Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width*3 > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { memcpy( (char*) Buffer+3*i, (char*) &(Pixels[i][Row]), 3 ); } + return true; +} + +bool BMP::Write8bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int i; + if( Width > BufferSize ) + { return false; } + for( i=0 ; i < Width ; i++ ) + { Buffer[i] = FindClosestColor( Pixels[i][Row] ); } + return true; +} + +bool BMP::Write4bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int PositionWeights[2] = {16,1}; + + int i=0; + int j; + int k=0; + if( Width > 2*BufferSize ) + { return false; } + while( i < Width ) + { + j=0; + int Index = 0; + while( j < 2 && i < Width ) + { + Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); + i++; j++; + } + Buffer[k] = (ebmpBYTE) Index; + k++; + } + return true; +} + +bool BMP::Write1bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ) +{ + int PositionWeights[8] = {128,64,32,16,8,4,2,1}; + + int i=0; + int j; + int k=0; + if( Width > 8*BufferSize ) + { return false; } + while( i < Width ) + { + j=0; + int Index = 0; + while( j < 8 && i < Width ) + { + Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); + i++; j++; + } + Buffer[k] = (ebmpBYTE) Index; + k++; + } + return true; +} + +ebmpBYTE BMP::FindClosestColor( RGBApixel& input ) +{ + using namespace std; + + int i=0; + int NumberOfColors = TellNumberOfColors(); + ebmpBYTE BestI = 0; + int BestMatch = 999999; + + while( i < NumberOfColors ) + { + RGBApixel Attempt = GetColor( i ); + int TempMatch = IntSquare( (int) Attempt.Red - (int) input.Red ) + + IntSquare( (int) Attempt.Green - (int) input.Green ) + + IntSquare( (int) Attempt.Blue - (int) input.Blue ); + if( TempMatch < BestMatch ) + { BestI = (ebmpBYTE) i; BestMatch = TempMatch; } + if( BestMatch < 1 ) + { i = NumberOfColors; } + i++; + } + return BestI; +} + + +bool EasyBMPcheckDataSize( void ) +{ + __pragma(warning(push)) + __pragma(warning(disable:4127)) + using namespace std; + bool ReturnValue = true; + if (sizeof(ebmpBYTE) != 1) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: ebmpBYTE has the wrong size (" + << sizeof( ebmpBYTE ) << " bytes)," << endl + << " Compared to the expected 1 byte value" << endl; + } + ReturnValue = false; + } + if( sizeof( ebmpWORD ) != 2 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: ebmpWORD has the wrong size (" + << sizeof( ebmpWORD ) << " bytes)," << endl + << " Compared to the expected 2 byte value" << endl; + } + ReturnValue = false; + } + if( sizeof( ebmpDWORD ) != 4 ) + { + if( EasyBMPwarnings ) + { + cout << "EasyBMP Error: ebmpDWORD has the wrong size (" + << sizeof( ebmpDWORD ) << " bytes)," << endl + << " Compared to the expected 4 byte value" << endl; + } + ReturnValue = false; + } + return ReturnValue; + __pragma(warning(pop)) +} + +bool Rescale( BMP& InputImage , char mode, int NewDimension ) +{ + using namespace std; + int CapMode = toupper( mode ); + + BMP OldImage( InputImage ); + + if( CapMode != 'P' && + CapMode != 'W' && + CapMode != 'H' && + CapMode != 'F' ) + { + if( EasyBMPwarnings ) + { + char ErrorMessage [1024]; + sprintf( ErrorMessage, "EasyBMP Error: Unknown rescale mode %c requested\n" , mode ); + cout << ErrorMessage; + } + return false; + } + + int NewWidth =0; + int NewHeight =0; + + int OldWidth = OldImage.TellWidth(); + int OldHeight= OldImage.TellHeight(); + + if( CapMode == 'P' ) + { + NewWidth = (int) floor( OldWidth * NewDimension / 100.0 ); + NewHeight = (int) floor( OldHeight * NewDimension / 100.0 ); + } + if( CapMode == 'F' ) + { + if( OldWidth > OldHeight ) + { CapMode = 'W'; } + else + { CapMode = 'H'; } + } + + if( CapMode == 'W' ) + { + double percent = (double) NewDimension / (double) OldWidth; + NewWidth = NewDimension; + NewHeight = (int) floor( OldHeight * percent ); + } + if( CapMode == 'H' ) + { + double percent = (double) NewDimension / (double) OldHeight; + NewHeight = NewDimension; + NewWidth = (int) floor( OldWidth * percent ); + } + + if( NewWidth < 1 ) + { NewWidth = 1; } + if( NewHeight < 1 ) + { NewHeight = 1; } + + InputImage.SetSize( NewWidth, NewHeight ); + InputImage.SetBitDepth( 24 ); + + int I,J; + double ThetaI,ThetaJ; + + for( int j=0; j < NewHeight-1 ; j++ ) + { + ThetaJ = (double)(j*(OldHeight-1.0)) + /(double)(NewHeight-1.0); + J = (int) floor( ThetaJ ); + ThetaJ -= J; + + for( int i=0; i < NewWidth-1 ; i++ ) + { + ThetaI = (double)(i*(OldWidth-1.0)) + /(double)(NewWidth-1.0); + I = (int) floor( ThetaI ); + ThetaI -= I; + + InputImage(i,j)->Red = (ebmpBYTE) + ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*(OldImage(I,J)->Red) + +(ThetaI-ThetaI*ThetaJ)*(OldImage(I+1,J)->Red) + +(ThetaJ-ThetaI*ThetaJ)*(OldImage(I,J+1)->Red) + +(ThetaI*ThetaJ)*(OldImage(I+1,J+1)->Red) ); + InputImage(i,j)->Green = (ebmpBYTE) + ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Green + +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Green + +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Green + +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Green ); + InputImage(i,j)->Blue = (ebmpBYTE) + ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Blue + +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Blue + +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Blue + +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Blue ); + } + InputImage(NewWidth-1,j)->Red = (ebmpBYTE) + ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Red) + + ThetaJ*(OldImage(OldWidth-1,J+1)->Red) ); + InputImage(NewWidth-1,j)->Green = (ebmpBYTE) + ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Green) + + ThetaJ*(OldImage(OldWidth-1,J+1)->Green) ); + InputImage(NewWidth-1,j)->Blue = (ebmpBYTE) + ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Blue) + + ThetaJ*(OldImage(OldWidth-1,J+1)->Blue) ); + } + + for( int i=0 ; i < NewWidth-1 ; i++ ) + { + ThetaI = (double)(i*(OldWidth-1.0)) + /(double)(NewWidth-1.0); + I = (int) floor( ThetaI ); + ThetaI -= I; + InputImage(i,NewHeight-1)->Red = (ebmpBYTE) + ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Red) + + ThetaI*(OldImage(I,OldHeight-1)->Red) ); + InputImage(i,NewHeight-1)->Green = (ebmpBYTE) + ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Green) + + ThetaI*(OldImage(I,OldHeight-1)->Green) ); + InputImage(i,NewHeight-1)->Blue = (ebmpBYTE) + ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Blue) + + ThetaI*(OldImage(I,OldHeight-1)->Blue) ); + } + + *InputImage(NewWidth-1,NewHeight-1) = *OldImage(OldWidth-1,OldHeight-1); + return true; +} diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP.h b/Source/BWEM/EasyBMP_1.06/EasyBMP.h new file mode 100644 index 0000000..ead98c1 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP.h @@ -0,0 +1,86 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMP.h * +* date added: 01-31-2005 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Main include file * +* * +*************************************************/ + +#ifdef _MSC_VER +// MS Visual Studio gives warnings when using +// fopen. But fopen_s is not going to work well +// with most compilers, and fopen_s uses different +// syntax than fopen. (i.e., a macro won't work) +// So, we'lll use this: +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#include +#include +#include +#include + +#ifndef EasyBMP +#define EasyBMP + +#ifdef __BCPLUSPLUS__ +// The Borland compiler must use this because something +// is wrong with their cstdio file. +#include +#else +#include +#endif + +#ifdef __GNUC__ +// If g++ specific code is ever required, this is +// where it goes. +#endif + +#ifdef __INTEL_COMPILER +// If Intel specific code is ever required, this is +// where it goes. +#endif + +#ifndef _DefaultXPelsPerMeter_ +#define _DefaultXPelsPerMeter_ +#define DefaultXPelsPerMeter 3780 +// set to a default of 96 dpi +#endif + +#ifndef _DefaultYPelsPerMeter_ +#define _DefaultYPelsPerMeter_ +#define DefaultYPelsPerMeter 3780 +// set to a default of 96 dpi +#endif + +#include "EasyBMP_DataStructures.h" +#include "EasyBMP_BMP.h" +#include "EasyBMP_VariousBMPutilities.h" + +#ifndef _EasyBMP_Version_ +#define _EasyBMP_Version_ 1.06 +#define _EasyBMP_Version_Integer_ 106 +#define _EasyBMP_Version_String_ "1.06" +#endif + +#ifndef _EasyBMPwarnings_ +#define _EasyBMPwarnings_ +#endif + +void SetEasyBMPwarningsOff( void ); +void SetEasyBMPwarningsOn( void ); +bool GetEasyBMPwarningState( void ); + +#endif diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP_BMP.h b/Source/BWEM/EasyBMP_1.06/EasyBMP_BMP.h new file mode 100644 index 0000000..819a976 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP_BMP.h @@ -0,0 +1,86 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMP_VariousBMPutilities.h * +* date added: 05-02-2005 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Defines BMP class * +* * +*************************************************/ + +#ifndef _EasyBMP_BMP_h_ +#define _EasyBMP_BMP_h_ + +bool SafeFread( char* buffer, int size, int number, FILE* fp ); +bool EasyBMPcheckDataSize( void ); + +class BMP +{private: + + int BitDepth; + int Width; + int Height; + RGBApixel** Pixels; + RGBApixel* Colors; + int XPelsPerMeter; + int YPelsPerMeter; + + ebmpBYTE* MetaData1; + int SizeOfMetaData1; + ebmpBYTE* MetaData2; + int SizeOfMetaData2; + + bool Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Read8bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Read4bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Read1bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + + bool Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Write8bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Write4bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + bool Write1bitRow( ebmpBYTE* Buffer, int BufferSize, int Row ); + + ebmpBYTE FindClosestColor( RGBApixel& input ); + + public: + + int TellBitDepth( void ); + int TellWidth( void ); + int TellHeight( void ); + int TellNumberOfColors( void ); + void SetDPI( int HorizontalDPI, int VerticalDPI ); + int TellVerticalDPI( void ); + int TellHorizontalDPI( void ); + + BMP(); + BMP( BMP& Input ); + ~BMP(); + RGBApixel* operator()(int i,int j); + + RGBApixel GetPixel( int i, int j ) const; + bool SetPixel( int i, int j, RGBApixel NewPixel ); + + bool CreateStandardColorTable( void ); + + bool SetSize( int NewWidth, int NewHeight ); + bool SetBitDepth( int NewDepth ); + bool WriteToFile( const char* FileName ); + bool ReadFromFile( const char* FileName ); + + RGBApixel GetColor( int ColorNumber ); + bool SetColor( int ColorNumber, RGBApixel NewColor ); +}; + +#endif diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP_ChangeLog.txt b/Source/BWEM/EasyBMP_1.06/EasyBMP_ChangeLog.txt new file mode 100644 index 0000000..b0a2c14 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP_ChangeLog.txt @@ -0,0 +1,821 @@ +EasyBMP Cross-Platform Windows Bitmap Library: Change Log + +Library Author(s): Paul Macklin + Library License: BSD (revised). See the BSD_(revised)_license.txt + file for further information. + Copyright: 2005-6 by the EasyBMP Project + Email: macklin01@users.sourceforge.net + Support: http://easybmp.sourceforge.net + +All changes by Paul Macklin unless otherwise noted. + +*--------------------------------------------------------------------* + +Version: 0.50 + Date: 1-31-2005 + + None! (first release) + +*--------------------------------------------------------------------* + +Version: 0.51 + Date: 2-14-2005 + + Added full 32-bit BMP file support + + Took out annoying "colors: " message from BMP8 initialization + from scratch + + Added more license and copyright info to each file + + Added change log to library + + To do next: + Should update the error messages for the initializations + Should simplify the reading and writing code + +*--------------------------------------------------------------------* + +Version: 0.52 + Date: 2-19-2005 + + Fixed a minor bug in the MakeGreyscalePalette function where the + 0 color turned out to be (255,255,255), rather than (0,0,0) + + Updated standard colors for 4-bit, 8-bit, and 24-bit + +*--------------------------------------------------------------------* + +Version: 0.53 + Date: 2-27-2005 + + Fixed unsigned / signed problem that VS.net shows + + + Tried fix of line 186 in EasyBMP_BMP4.h file. If it works, + I'll apply it consistently. I think that VS.net wants us + to clear char* blah, then char = new blah [size], just + like the old days for g++. + + Removed EasyBMP_StandardColors.h from standard package + +*--------------------------------------------------------------------* + +Version: 0.54 + Date: 2-27-2005 + + The fix of line 186 in EasyBMP_BMP4.h file appears to have + worked. I applied it through the remainder of the code. + Hopefully, this should ensure Visual Studio.Net compati- + bility. + + Fixed some typos in the comment lines + +*--------------------------------------------------------------------* + +Version: 0.55 + Date: 5-2-2005 + + Introduced RGBApixel struct. + + Introduced BMFH, BMIH, and BMP classes. + + Deprecated all old code to *_legacy.h. + + Rewrote EasyBMP_VariousBMPutilities.h to use the new + BMP class. + +*--------------------------------------------------------------------* + +Version: 0.56 + Date: 5-4-2005 + + Made Width, Height, and BitDepth private members and added + functions for accessing them. + + Made a new function, SetBitDepth, as the only means to + change the bit depth. It will create/resize a palette as + necessary. This simplifies the WriteToFile code, as well as + any palette altering algorithms. (All algorithms can now + assume that a properly-sized palette exists.) This will + help improve code stability greatly. + + Made a new function, SetSize, as the only way to change the + width and height of the image. + + Eliminated useless HasPalette and NumberOfColors members, + and added TellNumberOfColors() function. + + Updated EasyBMP_VariousBMPutilities.h to respect privacy + of data members. + +*--------------------------------------------------------------------* + +Version: 0.57 + Date: 5-8-2005 + + Removed fclose(fp) lines from EasyBMP_BMP.h and + EasyBMP_VariousBMPutilities.h whenever ( !fp ) occurs, + to avoid a crash when trying to close a non-existant file. + + + Added a line to set bmfh.bfType = 0; to getBMFH() routine + in the case where ( !fp ) occurs, so that a nonexistant file + doesn't falsely show up as a bitmap file. + + Made error messages in BMP::ReadFromFile(char*) more meaningful, + since Argh! doesn't help much. :-) + + Made ReadFromFile operations safer: can deal more effectively + with corrupted and/or truncated files by adding the new + SafeFread() wrapper function. + + Moved all change-log entries to the change log to make the source + file tidier. + + Removed all references to Palettes; renamed them to ColorTables. + +*--------------------------------------------------------------------* + +Version: 0.58 + Date: 5-13-2005 + + Rewrote ReadFromFile() to fix program crashes on reading 4-bit + files. (*grumble* I can't believe there was such a bug in such + a late version! */grumble*) + + Added support to ReadFromFile() for reading 1-bit files. + + Rewrote ReadFromFile() to avoid trying to read bitmap files of + depths other than 1, 4, 8, 24, and 32 bits. + + Tested reading 4-bit files of width 0,1,2, and 3 (modulo 4), + and 1-bit files of width 0,1,2,3,4,5,6, and 7 (modulo 8) + +*--------------------------------------------------------------------* + +Version: 0.59 + Date: 5-15-2005 + + Made ReadFromFile() more robust. Evidently, reading to the + same temp variable all the time made it unstable when reading + many files. I would never have guessed. I instead declare BMIH + and BMFH objects and read directly to their members. This appears + to be more stable when dealing with many ReadFromFile() calls. + + On a related note, made sure to not call SetSize( Width,Height), + which is a bit recursive, as well as SetBitDepth( BitDepth ). + This appears to help stability, since these two functions were + create precisely for the purpose of setting those variables + values safely. + + Made use of the boolean return value in SafeFread() to detect + when files are obviously corrupted. Used this to have an early + catch in ReadFromFile() and set it to a 1x1 1-bit image and + exit. + + Made ReadFromFile() stricter, in that it only reads recognized + bit depths (1,4,8,24,32). Any other bit depth will prompt the + routine to terminate and set it to a 1x1 1-bit file. + + Added write support for 1-bit files. + + Rewrote WriteToFile() for 4,8-bit files to match methods used + for reading them. + + Revised CreateStandardColorTable() and + CreateGreyscaleColorTable() to add support for 1-bit files. + + Rewrote WriteToFile() to be stricter in only writing known bit + depths (1,4,8,24,32) and ignoring all others. + +*--------------------------------------------------------------------* + +Version: 0.60 + Date: 5-21-2005 + + Deprecated *_legacy.h files. + + Tested library extensivey in linux with good results. + + Made CreateGreyscaleColorTable() stricter, in that it exits + if supplied a bit depth other than 1, 4, or 8. + + Made cosmetic changes in EasyBMP_DataStructures.h to + improve readability. + + Made SetBitDepth() stricter, in that it will never allow a bitmap + to be set to an unsupported bit depth. Only bit depths of 1, 4, + 8, 24, or 32 are accepted. + + Made SetSize() stricter, in that it will not allow negative + widths or heights. + + Made cosmetic changes in EasyBMP_BMP.h to improve readability. + + Added a check in ReadFromFile() to see if the requested width or + height is negative, a good sign of file corruption. In such a + case, the file is set to a blank 1x1 1-bit file. + + Added code to ReadFromFile() to set size to 1x1 and bit depth to + 1-bit if the file was not found. + +*--------------------------------------------------------------------* + +Version: 0.61 + Date: 5-22-2005 + + Fixed awIndex typo in WriteToFile(). + + Replaced double BestDistance comparisons in WriteToFile() + with int BestDistances (so as to do integer operations, + rather than double operations). This gave a roughly 100% + speedup in 8-bit, 4-bit, and 1-bit write operations on + unoptimized (no compiler flags) code and a 30% speedup + on optimized code. + + Removed checks like if( BestDistance < 1 ){ k=256; } .. from + WriteToFile(), as they give more overhead than savings in my + testing. For 8-bit files, there was a slight gain by putting + it back in with another method: + while( k < 256 && BestDistance > 0 ). + + Redefined StepSize in CreateGreyscaleColorTable() to give a + better range of greys in 4-bit mode. As it was, white was not + in the color table. (Colors were spaced by 256/16 = 16). Now, + colors are spaced by (255-1)/(16-1) = 17, which gives the full + range. + +*--------------------------------------------------------------------* + +Version: 0.62 + Date: 5-25-2005 + + Added endianess check function IsBigEndian() to + EasyBMP_DataStructures.h file. + + Added functions to swap bytes in WORD and DWORD multibyte + variables to EasyBMP_DataStructures.h file for future big-endian + support. + + Added functions to switch endianess to BMFH and BMIH objects + to EasyBMP_DataStructures.h file. + + Added endianess checks to ReadFromFile() and WriteToFile() + functions in EasyBMP_BMP.h file, along with endianess conversions + where necessary. + + Added endianess checks and conversions to GetBMFH() and GetBMIH() + functions in EasyBMP_VariousBMPutilities.h file. + + Rewrote GetBitmapInfo() function to use GetBMFH() and GetBMIH() + functions instead. (In EasyBMP_VariousBMPutilities.h.) This + cuts down on the redundancy in the code. + + Renamed GetBitmapInfo() to DisplayBitmapInfo() in the + EasyBMP_VariousBMPutilities.h file. + + With these changes, big-endian architectures should be supported, + including IBM PowerPC, Sun Sparc, Motorola 86k, etc., and + including Mac OSX. + +*--------------------------------------------------------------------* + +Version: 0.63 + Date: 7-20-2005 + + Added IntPow(int,int) function to help compiling with std + namespace. Besides, integer operations are faster and more + accurate. + + Moved Square(double), IntSquare(int), and IntPow(int,int) to + EasyBMP_DataStructures.h + + Simplified and cleaned up code in + Create4bitColorTable( RGBApixel**). + + Changed safety check in BMP.ReadFromFile(char*) to set size to + 1 x 1 if width or height is non-positive, rather than simply + negative. + + Added bounds checking to BMP.operator()(int,int) to automatically + truncate requested pixel if out of bounds. Also added a warning + to cue the user in. :-) + + Made error messages more consistent in format. + + Simplified and cleaned up code in + Create4bitColorTable( RGBApixel**). + + Added #include to EasyBMP.h, since EasyBMP uses + cout, etc. + + Simplified and cleaned up code in + Create1bitColorTable( RGBApixel**). + + Changed BMP.SetSize(int,int) to disallow non-positive widths and + heights, rather than simply negative widths and heights. Such + function calls are now ignored. + +*--------------------------------------------------------------------* + +Version: 0.64 + Date: 8-2-2005 + + Changed "include " to "include " for + ANSI-C++ compliance, as well as for better compatibility with the + std namespace and VC++. (Thanks, Tommy Li!) + + Added some #ifndef pragmas to each header so that it should be + fine to incluce EasyBMP.h in multiple files in larger projects. + + Added "using namespace std" inside any function that used C++ + math or I/O operations. I avoided putting "using namespace std" + anywhere with global scope for maximum compatibility with C++ + software in the wild. + + Added includes for and to EasyBMP.h + + Removed unused temporary variables (TempWORD and TempDWORD) from + EasyBMP_BMP.h for cleaner compiling. If I see any more such + unused variables, I'll remove them, too. + +*--------------------------------------------------------------------* + +Version: 0.65 + Date: 8-13-2005 + + Moved implementations of BMP::BMP(), BMP::~BMP(), and + BMP::operator()(int,int) outside of the class. This should help + for eventually moving everything into a separate cpp file. + + Made RGBApixel** Pixels a private data member of the class + BMP. + + Added function void BMP::SetColor(int,RGBApixel) to BMP class + to allow safe method of changing a color in the color table. + + Added function RGBApixel BMP::GetColor(int) to BMP class + to allow safe method of retrieving a color in the color + table. + + Cleaned up error messages in EasyBMP_BMP.h + + Cleaned up error messages in EasyBMP_VariousBMPutilities.h + +*--------------------------------------------------------------------* + +Version: 0.66 + Date: 8-18-2005 + + EasyBMP_StandardColorTables.h was removed from the library. + + CreateStandardColorTable(RGBApixel**,int) was changed to + CreateStandardColorTable() and made a member function of BMP. + All other CreateStandardColorTable functions are now unnecessary + and have been removed. + + CreateGreyscaleColorTable(RGBApixel**,int) was changed to + CreateStandardColorTable( BMP& ) and moved to + EasyBMP_VariousBMPutilities.h. + + RGBApixel* Colors was made a private data member of the BMP + class. + + CreateGreyscaleColorTable( BMP& ) was renamed to + CreateGrayscaleColorTable( BMP& ). + +*--------------------------------------------------------------------* + +Version: 0.67 + Date: 9-14-2005 + + Made the EasyBMP custom math functions in + EasyBMP_DataStructures.h inline. (Square,IntSquare,IntPow). + This should make those function calls faster while improving + compatibility with compiling DLL's. + + Separated the code from SafeFread() in EasyBMP_BMP.h to + improve compatibility with compiling DLL's. + + Removed #define _WINGDI_H from EasyBMP_DataStructures.h to + improve compatibility with win32 applications. Instead, + there's an extra #ifndef _SELF_DEFINED_BMP_DATA_TYPES + conditional added. + + _SELF_DEFINED_BMP_DATA_TYPES renamed to _SELF_DEFINED_WINGDI + in EasyBMP_DataStructures.h. + + All bit-flipping functions (IsBigEndian, FlipWORD, + FlipDWORD) in EasyBMP_DataStructures.h were made inline + to improve execution speed and improve compatibility with + compiling DLL's. + + All code was separated from function declarations in + EasyBMP_VariousBMPutilities.h to improve compatibility + with compiling DLL's. + + Updated and cleaned up layout of EasyBMP_ChangeLog.txt. + + Updated contact and support information in library files. + + Corrected the LGPL license version. + +*--------------------------------------------------------------------* + +Version: 0.68 + Date: 10-9-2005 + + Changed references to FILE to std::FILE in the SafeFread function + in EasyBMP_BMP.h to improve compatibility with Borland's compiler. + + Removed a few assignments in EasyBMP_BMP.h that weren't used to + improve efficiency and reduce Borland warnings. + + Changed calls like NotCorrupted = SafeFread() to + NotCorrupted &= SafeFread() in BMP::ReadFromFile() in EasyBMP_BMP.h + to improve robustness. Now, if the NotCorrupted bit is ever set + to false, it stays false, meaning that the function won't "forget" + that it encountered file corruption. + +*--------------------------------------------------------------------* + +Version: 0.69 + Date: 10-19-2005 + + Changed BMP::WriteToFile( char* ) to BMP::WriteToFile(const char*) + in EasyBMP_BMP.h to respond to a feature request. + + Changed BMP::ReadFromFile( char* ) to BMP::ReadToFile(const char*) + in EasyBMP_BMP.h to respond to a feature request. + + Made BMP::ReadFromFile() and BMP::WriteToFile() in EasyBMP_BMP.h + return true/false to indicate success/failure in the operations. + These functions previously returned void. + + Made BMP::SetSize() and BMP::SetBitDepth() in EasyBMP_BMP.h + return true/false to indicate success/failure in the operations. + These functions previously returned void. + + Made BMP::SetColor() and BMP::CreateStandardColorTable() in + EasyBMP_BMP.h return true/false to indicate success/failure in the + operations. These functions previously returned void. + + Made CreateGrayscaleColorTable() in EasyBMP_VariousBMPutilities.h + return true/false to indicate success/failure in the operations. + This function previously returned void. + + Changed the char* argument GetBMFH( char* ), GetBMIH( char* ), + DisplayBitmapInfo( char* ), and GetBitmapColorDepth( char* ) in + EasyBMP_VariousBMPutilities.h to const char* for cleaner, safer + programming. + +*--------------------------------------------------------------------* + +Version: 0.70 + Date: 10-19-2005 + + Found and fixed error in BMP::ReadFromFile() in the check for only + reading support bit depths. + + Changed license from LGPL to BSD (revised/modified) to simplify + licensing issues and resolve any lingering licensing questions. + + Fixed compiler error when using MSVC++. + + Improved fix to allow compiling with Borland without breaking + Borland support. + + Added a few lines to EasyBMP.h to make it easier to tailor code + to specific compilers. (For future use as needed.) + + Added a few lines to EasyBMP_BMP.h (in BMP::ReadFromFile(), + BMP::WriteToFile(), and BMP::SetBitDepth()) to eventually add + support for 16-bit files. + +*--------------------------------------------------------------------* + +Version: 0.71 + Date: 11-01-2005 + + Cleaned up comments in BMP::ReadFromFile() in EasyBMP_BMP.h + + Added endian-safe read support for 16-bit files that are in the + standard 5-5-5 format (not specified in bit fields) + + Added endian-safe read support for 16-bit files that use bit + fields, including 5-6-5 files. + + Added endian-safe write support for 16-bit files. Uses the 5-6-5 + encoding scheme to maximize the utility of the bits used. + + Added a check for compression in BMP::ReadFromFile(). Because + file compression is beyond the scope of EasyBMP, such files are + not supported, and EasyBMP now properly detects these situations + and exits with an error. + + Added a check for files that attempt to use bit fields but are not + 16-bit files to BMP::ReadFromFile(). Such files are not supported. + + Added a check to BMP::ReadFromFile() for files that use unknown + values of bmih.biCompression, such as old OS2 bitmaps. Such files + are not supported. + + Removed "switching endianness" messages from EasyBMP_BMP.h + + Added support for indexed (1, 4, and 8-bit) files that don't + specify all the colors. + + Added support for reading files that include extra meta data before + the pixels. This data is skipped. + + Added enclosing #ifndef EasyBMP ... lines to EasyBMP.h as a + further safeguard when EasyBMP is included in multiple cpp + files. + +*--------------------------------------------------------------------* + +Version: 1.00 + Date: 02-06-2006 + + First Production/Stable release. + + Corrected typographical errors in the comment sections of all + files. + + Updated copyright on all files. + + Removed extraneous comment in BMIH::BMIH() function in + EasyBMP_DataStructures.h file. + + Replaced instances of \n with the more modern endl in + EasyBMP_DataStructures.h, EasyBMP_BMP.h, and + EasyBMP_VariousBMPutilities.h. + + Added placeholder MetaData1 and MetaData2 data members to the + BMP class for potential future use. + + Removed extraneous comments from EasyBMP_BMP.h. + + Removed warning messages for switching endianness from + EasyBMP_VariousBMPutilities.h. + + Updated copyright in EasyBMP_ChangeLog.txt file. + + Fixed formatting issues in EasyBMP_ChangeLog.txt file. + + Added DefaultXpelsPerMeter and DefaultYpelsPerMeter in + EasyBMP.h. These will default to 96 dpi. + + Changed BMP::WriteToFile() to use DefaultXpelsPerMeter and + DefaultYpelsPerMeter when writing the BMIH structure. + + Added XpelsPerMeter and YpelsPerMeter data members to BMP + class so that horizontal and vertical resolution are handled + properly. Currently, upon reading a file, the stated resolutions + are preserved, and upon writing, if no resolutions are given, + the defaults (of 96 DPI) are used. + + Added function void BMP::SetDPI(int,int) to set the horizontal + and vertical resolutions. + + Removed some unnecessary code from GetBitmapColorDepth() in + EasyBMP_VariousBMPutilities.h. + + Fixed a bug in RangedPixelToPixelCopyTransparent() and + RangedPixelToPixelCopy() in EasyBMP_VariousBMPutilities.h which + caused copies to be truncated by an extra row or column in + certain circumstances. + + Fixed a bug in RangedPixelToPixelCopyTransparent() and + RangedPixelToPixelCopy() in EasyBMP_VariousBMPutilities.h which + checked the wrong variable (FromT instead of FromB) to see if + it was out of range. + + Added extra checks to RangedPixelToPixelCopyTransparent() and + RangedPixelToPixelCopy() in EasyBMP_VariousBMPutilities.h to + prevent attempted access of out-of-range pixels. + +*--------------------------------------------------------------------* + +Version: 1.01 + Date: 03-31-2006 + + Made only the short functions Square, IntSquare, IsBigEndian, + FlipWORD, and FlipDWORD inline functions in + EasyBMP_DataStructures.h. + + Moved all code (other than inline functions) to EasyBMP.cpp. + + Changed DefaultXPelsPerMeter and DefaultYPelsPerMeter to #define + lines in EasyBMP.h to make the library compatible with + with the header-code split. + + Removed memory hole in ~BMP() where "delete Colors;" was used + instead of "delete [] Colors;". Likewise with MetaData1 and + MetaData2. + + Fixed memory leak in BMP::SetBitDepth() by changing to + delete [] Colors; + + Removed potential memory leak in BMP::WriteToFile() in 24- and + 32-bit writing where szTemp wasn't delete at the end of a row. + + Fixed bug where XPelsPerMeter and YPelsPerMeter weren't + properly initialized in the BMP::BMP() constructor, leading + to strange horizontal and vertical resolutions. + + Fixed memory leak in BMP::ReadFromFile() where TempSkipBYTE + wasn't deleted. + + Fixed memory leak in BMP::ReadFromFile() where szTemp wasn't + deleted. + + Added BMP::TellVerticalDPI() and BMP::TellHorizontalDPI() + functions to give this information. If those values have + not yet been set, then they are first set to the EasyBMP + defaults of 96 dpi. + + Set uninitialized RGBApixel values to white (255,255,255,0) + in a few functions for the BMP class. + + Added a sample cpp application and makefile. + +*--------------------------------------------------------------------* + +Version: 1.02 + Date: 05-29-2006 + + Inserted a line into EasyBMP.h to suppress the Visual Studio + warnings. We'll keep using the C++ standard fopen for now + until fopen_s becomes a real standard. + + Moved the code sample and makefile to a subdirectory, so that + unzipping EasyBMP#_##.zip into a project directory doesn't + overwrite any crucial makefiles. + + Improved SafeFread() to check if the proper amount of data + could be read. + + Dramatically cleaned up ReadFromFile() code for 1 and 4 + bpp files. + + Fixed a typo (draw.o) in the sample makefile. + + Modified ReadFromFile() to use buffering when reading the pixel + data. This should substantially improve disk access performance. + Only 16 bpp files are read in the old, slower way. + + Changed DWORD from unsigned long to unsigned int. This should + fix the issue where 64-bit machines see DWORD as an 8-byte + data type, rather than 4 bytes. (Thank you to Bas Wegh!) + + Renamed BYTE, WORD, and DWORD data types to ebmpBYTE, ebmpWORD, + and ebmpDWORD to eliminate the possibility of conflict with + windows applications, particularly with 64-bit windows, which + likely uses 8 byte DWORDS. + + Modified WriteToFile() to use buffering when reading the pixel + data. This should substantially improve disk access performance. + Only 16 bpp files are read in the old, slower way. + + Added new function, EasyBMPcheckDataSize(), to check that + the ebmpBYTE, ebmpWORD, and ebmpDWORD types have the correct + type. + + Added some new macros of the EasyBMP version number for easier + version checking. New versions include _EasyBMP_Version_ + (a double), _EasyBMP_Version_String_ (a char* version), and + _EasyBMP_Version_Integer_ (an integer version, e.g., 102). + +*--------------------------------------------------------------------* + +Version: 1.03 + Date: 06-20-2006 + + Inserted a line into EasyBMP.h to suppress the Visual Studio + + Added a check to BMP.SetColor() to ensure that the color table + is defined before attempting to set a color entry. + + Added a check to BMP.GetColor() to ensure that the color table + is defined before attempting to retrieve a color entry. + + Simplified the conditional in BMP.WriteToFile() from + if( BitDepth == 1 || BitDepth == 4 || ... ) to the simpler + if( BitDepth != 16 ). + + Removed the old, unused code for writing 1- and 4-bit files + from BMP.WriteToFile(). + + Removed the line Colors = new RGBApixel [NumberOfColors]; in + BMP.ReadFromFile(). This operation is already covered by the + earlier SetBitDepth() call, and may contribute to a memory + leak. Furthermore, for files that had fewer than expected + number of colors (e.g., an 8-bit file with 236 colors), it + lead to memory access errors in BMP.GetColor() and BMP.SetColor(). + (In fact, this is the main motivation for release 1.03.) + + Added a warning when BMP.ReadFromFile() encounters an under- + specified color table, and code to pad the table with white + entries. + + Added screen output on EasyBMP version and project website to + the code sample. + +*--------------------------------------------------------------------* + +Version: 1.04 + Date: 07-22-2006 + + Removed the assignment to the integer i in IntPow() to eliminate a + Borland compiler warning. + + Removed the assignment to the integer i in the Read##bitRow() + functions to eliminate Borland compiler warnings. + + Removed the assignment to ZeroWORD in line 478 of EasyBMP.cpp in + BMP::WriteToFile() to eliminate Borland compiler warnings. + + Removed the assignment to ZeroWORD in line 825 of EasyBMP.cpp in + BMP::ReadFromFile() to eliminate Borland compiler warnings. + + The Borland warnings about conditions always being false are + incorrect. (Lines 1587, 1594, and 1601.) Likewise, the Borland + warnings about unreachable code (lines 1589, 1596, and 1603) are + incorrect. This code serves as a protection on unexpected hardware + where the data types may not be of the correct size, and helps to + future-proof EasyBMP. The first time this type of error was + encountered was on 64-bit CPUs, where the size of the DWORD was + larger than assumed when writing EasyBMP. Therefore, we will not + "correct" these "errors" detected by Borland. If they bother you, + compile with the -w-8008 and -w-8066 options. + + Borland issues warnings about argc and argv being unused in the + sample project. These are silly warnings and will be ignored. If + this warning bothers you, compile with the -w-8057 option. + + Modified the sample makefile so that EasyBMP.o depends upon + EasyBMP.cpp and EasyBMP*.h in the current working directory, rather + than the parent directory. + + Added a global EasyBMPwarnings boolean variable, and functions + SetEasyBMPwarningsOn() and SetEasyBMPwarningsOff() to enable and + disable EasyBMP warnings and errors. Note that this will not + disable error checking or any other EasyBMP behavior, other than + cout output of the warning and error messages. + + Added the function GetEasyBMPwarningState() to query the EasyBMP + warning state. (Either warnings are enabled or disabled.) + + Removed old commented code (Write1bitRow()) from EasyBMP.cpp. + + Replaced the 24-bit EasyBMPbackground.bmp image in the code sample + with a dithered 8-bit version to reduce the download size of the + core library. + +*--------------------------------------------------------------------* + +Version: 1.05 + Date: 11-01-2006 + + Renamed BytesRead to ItemsRead in the SafeFread() function in + EasyBMP.cpp for greater clarity. + + Added a copy constructor to the BMP class. However, note that + passing by value is not recommended practice. (Passing by refer- + ence is much faster, and consumes less memory.) + + Added a new function: + bool Rescale( BMP& InputImage, char mode, int NewDimension ); + to resize an image. The mode variables are as follows: + 'P': resizes the image to a new percentage of the old size, + e.g., 42%, 13%, etc. + example: Rescale( SomeImage, 'p', 42 ); + 'W': resizes the image such that the new width is as specified. + example: Rescale( SomeImage, 'W', 100 ); + 'H': resizes the image such that the new height is as specified. + example: Rescale( SomeImage, 'H', 100 ); + 'F': resizes the image to fit in a square of specified size. + example: Rescale( SomeImage, 'F', 100 ); // fits in 100x100 + // box + All rescaling is done with bilinear interpolation. + +*--------------------------------------------------------------------* + +Version: 1.06 + Date: 12-01-2006 + + Added includes for and to EasyBMP.h. These are + used and should have been included all along. This should help + with Intel icc compiling. + + Fixed the && bug in the copy constructor. (Thank you to user + fcnature!) + + Added image scaling to the supplied code sample. + + Added GetPixle() and SetPixel() functions for future use. These + will be added to enable more careful use of the const keyword. + +*--------------------------------------------------------------------* \ No newline at end of file diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP_DataStructures.h b/Source/BWEM/EasyBMP_1.06/EasyBMP_DataStructures.h new file mode 100644 index 0000000..82b6179 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP_DataStructures.h @@ -0,0 +1,104 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMP_DataStructures.h * +* date added: 05-02-2005 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Defines basic data structures for * +* the BMP class * +* * +*************************************************/ + +#ifndef _EasyBMP_Custom_Math_Functions_ +#define _EasyBMP_Custom_Math_Functions_ +inline double Square( double number ) +{ return number*number; } + +inline int IntSquare( int number ) +{ return number*number; } +#endif + +int IntPow( int base, int exponent ); + +#ifndef _EasyBMP_Defined_WINGDI +#define _EasyBMP_Defined_WINGDI + typedef unsigned char ebmpBYTE; + typedef unsigned short ebmpWORD; + typedef unsigned int ebmpDWORD; +#endif + +#ifndef _EasyBMP_DataStructures_h_ +#define _EasyBMP_DataStructures_h_ + +inline bool IsBigEndian() +{ + short word = 0x0001; + if((*(char *)& word) != 0x01 ) + { return true; } + return false; +} + +inline ebmpWORD FlipWORD( ebmpWORD in ) +{ return ( (in >> 8) | (in << 8) ); } + +inline ebmpDWORD FlipDWORD( ebmpDWORD in ) +{ + return ( ((in&0xFF000000)>>24) | ((in&0x000000FF)<<24) | + ((in&0x00FF0000)>>8 ) | ((in&0x0000FF00)<<8 ) ); +} + +// it's easier to use a struct than a class +// because we can read/write all four of the bytes +// at once (as we can count on them being continuous +// in memory + +typedef struct RGBApixel { + ebmpBYTE Blue; + ebmpBYTE Green; + ebmpBYTE Red; + ebmpBYTE Alpha; +} RGBApixel; + +class BMFH{ +public: + ebmpWORD bfType; + ebmpDWORD bfSize; + ebmpWORD bfReserved1; + ebmpWORD bfReserved2; + ebmpDWORD bfOffBits; + + BMFH(); + void display( void ); + void SwitchEndianess( void ); +}; + +class BMIH{ +public: + ebmpDWORD biSize; + ebmpDWORD biWidth; + ebmpDWORD biHeight; + ebmpWORD biPlanes; + ebmpWORD biBitCount; + ebmpDWORD biCompression; + ebmpDWORD biSizeImage; + ebmpDWORD biXPelsPerMeter; + ebmpDWORD biYPelsPerMeter; + ebmpDWORD biClrUsed; + ebmpDWORD biClrImportant; + + BMIH(); + void display( void ); + void SwitchEndianess( void ); +}; + +#endif diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP_UserManual.pdf b/Source/BWEM/EasyBMP_1.06/EasyBMP_UserManual.pdf new file mode 100644 index 0000000..0e52b4f Binary files /dev/null and b/Source/BWEM/EasyBMP_1.06/EasyBMP_UserManual.pdf differ diff --git a/Source/BWEM/EasyBMP_1.06/EasyBMP_VariousBMPutilities.h b/Source/BWEM/EasyBMP_1.06/EasyBMP_VariousBMPutilities.h new file mode 100644 index 0000000..349dda6 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/EasyBMP_VariousBMPutilities.h @@ -0,0 +1,43 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMP_VariousBMPutilities.h * +* date added: 05-02-2005 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Various utilities. * +* * +*************************************************/ + +#ifndef _EasyBMP_VariousBMPutilities_h_ +#define _EasyBMP_VariousBMPutilities_h_ + +BMFH GetBMFH( const char* szFileNameIn ); +BMIH GetBMIH( const char* szFileNameIn ); +void DisplayBitmapInfo( const char* szFileNameIn ); +int GetBitmapColorDepth( const char* szFileNameIn ); +void PixelToPixelCopy( BMP& From, int FromX, int FromY, + BMP& To, int ToX, int ToY); +void PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY, + BMP& To, int ToX, int ToY, + RGBApixel& Transparent ); +void RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT, + BMP& To, int ToX, int ToY ); +void RangedPixelToPixelCopyTransparent( + BMP& From, int FromL , int FromR, int FromB, int FromT, + BMP& To, int ToX, int ToY , + RGBApixel& Transparent ); +bool CreateGrayscaleColorTable( BMP& InputImage ); + +bool Rescale( BMP& InputImage , char mode, int NewDimension ); + +#endif diff --git a/Source/BWEM/EasyBMP_1.06/sample/EasyBMPbackground.bmp b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPbackground.bmp new file mode 100644 index 0000000..ae1dfa2 Binary files /dev/null and b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPbackground.bmp differ diff --git a/Source/BWEM/EasyBMP_1.06/sample/EasyBMPsample.cpp b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPsample.cpp new file mode 100644 index 0000000..969bd97 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPsample.cpp @@ -0,0 +1,82 @@ +/************************************************* +* * +* EasyBMP Cross-Platform Windows Bitmap Library * +* * +* Author: Paul Macklin * +* email: macklin01@users.sourceforge.net * +* support: http://easybmp.sourceforge.net * +* * +* file: EasyBMPsample.cpp * +* date added: 03-31-2006 * +* date modified: 12-01-2006 * +* version: 1.06 * +* * +* License: BSD (revised/modified) * +* Copyright: 2005-6 by the EasyBMP Project * +* * +* description: Sample application to demonstrate * +* some functions and capabilities * +* * +*************************************************/ + +#include "EasyBMP.h" +using namespace std; + +int main( int argc, char* argv[] ) +{ + cout << endl + << "Using EasyBMP Version " << _EasyBMP_Version_ << endl << endl + << "Copyright (c) by the EasyBMP Project 2005-6" << endl + << "WWW: http://easybmp.sourceforge.net" << endl << endl; + + BMP Text; + Text.ReadFromFile("EasyBMPtext.bmp"); + + BMP Background; + Background.ReadFromFile("EasyBMPbackground.bmp"); + + BMP Output; + Output.SetSize( Background.TellWidth() , Background.TellHeight() ); + Output.SetBitDepth( 24 ); + + RangedPixelToPixelCopy( Background, 0, Output.TellWidth()-1, + Output.TellHeight()-1 , 0, + Output, 0,0 ); + + RangedPixelToPixelCopyTransparent( Text, 0, 380, + 43, 0, + Output, 110,5, + *Text(0,0) ); + + RangedPixelToPixelCopyTransparent( Text, 0, Text.TellWidth()-1, + Text.TellWidth()-1, 50, + Output, 100,442, + *Text(0,49) ); + + Output.SetBitDepth( 32 ); + cout << "writing 32bpp ... " << endl; + Output.WriteToFile( "EasyBMPoutput32bpp.bmp" ); + + Output.SetBitDepth( 24 ); + cout << "writing 24bpp ... " << endl; + Output.WriteToFile( "EasyBMPoutput24bpp.bmp" ); + + Output.SetBitDepth( 8 ); + cout << "writing 8bpp ... " << endl; + Output.WriteToFile( "EasyBMPoutput8bpp.bmp" ); + + Output.SetBitDepth( 4 ); + cout << "writing 4bpp ... " << endl; + Output.WriteToFile( "EasyBMPoutput4bpp.bmp" ); + + Output.SetBitDepth( 1 ); + cout << "writing 1bpp ... " << endl; + Output.WriteToFile( "EasyBMPoutput1bpp.bmp" ); + + Output.SetBitDepth( 24 ); + Rescale( Output, 'p' , 50 ); + cout << "writing 24bpp scaled image ..." << endl; + Output.WriteToFile( "EasyBMPoutput24bpp_rescaled.bmp" ); + + return 0; +} diff --git a/Source/BWEM/EasyBMP_1.06/sample/EasyBMPtext.bmp b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPtext.bmp new file mode 100644 index 0000000..6a9e00c Binary files /dev/null and b/Source/BWEM/EasyBMP_1.06/sample/EasyBMPtext.bmp differ diff --git a/Source/BWEM/EasyBMP_1.06/sample/makefile b/Source/BWEM/EasyBMP_1.06/sample/makefile new file mode 100644 index 0000000..8f708f9 --- /dev/null +++ b/Source/BWEM/EasyBMP_1.06/sample/makefile @@ -0,0 +1,53 @@ +# +# EasyBMP Cross-Platform Windows Bitmap Library +# +# Author: Paul Macklin +# email: macklin01@users.sourceforge.net +# support: http://easybmp.sourceforge.net +# file: makefile +# date added: 04-22-2006 +# date modified: 12-01-2006 +# version: 1.06 +# +# License: BSD (revised/modified) +# Copyright: 2005-6 by the EasyBMP Project +# +# description: Sample makefile for compiling with +# the EasyBMP library. This compiles +# the EasyBMPsample.cpp program. +# + +CC = g++ + +# this line gives compiler optimizations that are geared towards g++ and Pentium4 +# computers. Comment it out if you don't have a Pentium 4 (or Athlon XP) or up + +# CFLAGS = -O3 -Wno-deprecated -mcpu=pentium4 -march=pentium4 \ +# -mfpmath=sse -msse -mmmx -msse2 -pipe -fomit-frame-pointer -s + +# Uncomment these two lines to use with any Pentium with MMX or up. + +# CFLAGS = -Wno-deprecated -mcpu=pentium -march=pentium -pipe \ +# -fomit-frame-pointer -mmmx -funroll-all-loops -s + +# Uncomment these lines for some "safe" optimization flags + +CFLAGS = -O3 -pipe -fomit-frame-pointer -funroll-all-loops -s + +EasyBMPTest: EasyBMP.o EasyBMPsample.o + g++ $(CFLAGS) EasyBMP.o EasyBMPsample.o -o EasyBMPtest + +EasyBMP.o: ../EasyBMP.cpp ../EasyBMP*.h + cp ../EasyBMP*.h . + cp ../EasyBMP.cpp . + g++ $(CFLAGS) -c EasyBMP.cpp + +EasyBMPsample.o: EasyBMPsample.cpp + g++ -c EasyBMPsample.cpp + +clean: + rm EasyBMP*.h + rm EasyBMP.cpp + rm EasyBMPtest* + rm EasyBMPoutput*.bmp + rm -f *.o diff --git a/Source/BWEM/LICENSE.txt b/Source/BWEM/LICENSE.txt new file mode 100644 index 0000000..53ebddd --- /dev/null +++ b/Source/BWEM/LICENSE.txt @@ -0,0 +1,21 @@ +// (MIT/X11 License) +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// - The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// - The Software is provided "as is", without warranty of any kind, express +// or implied, including but not limited to the warranties of merchantability, +// fitness for a particular purpose and noninfringement. In no event shall the +// authors or copyright holders be liable for any claim, damages or +// other liability, whether in an action of contract, tort or otherwise, +// arising from, out of or in connection with the software or the use or other +// dealings in the Software. +// - Except as contained in this notice, the name of the copyright holders +// shall not be used in advertising or otherwise to promote the sale, use or +// other dealings in this Software without prior written authorization from +// the copyright holders. diff --git a/Source/BWEM/README.txt b/Source/BWEM/README.txt new file mode 100644 index 0000000..4e6c85a --- /dev/null +++ b/Source/BWEM/README.txt @@ -0,0 +1,31 @@ +README.txt +=============================================================================== + the BWEM library +=============================================================================== + + +Brood War Easy Map is a C++ library that analyses Brood War's maps and +provides relevant information such as areas, choke points and bases. +It is built on top of the BWAPI library (http://bwapi.github.io/index.html). +It first aims at simplifying the development of bots for Brood War, but can be used +for any task requiring high level map information. +It can be used as a replacement for the BWTA2 add-on (https://bitbucket.org/auriarte/bwta2), +as it performs faster and shows better robustness while providing similar information. + +------------------------------------------------------------------------------- +License: MIT/X11 + +------------------------------------------------------------------------------- +Version: 1.4.1 + +------------------------------------------------------------------------------- +Sourceforge project: http://sourceforge.net/projects/BWEM + +------------------------------------------------------------------------------- +Online site: http://BWEM.sourceforge.net + +------------------------------------------------------------------------------- +Contact: igor_dimitrijevic@yahoo.fr + +------------------------------------------------------------------------------- + diff --git a/Source/BWEM/doc/html/Andromeda.png b/Source/BWEM/doc/html/Andromeda.png new file mode 100644 index 0000000..5fab4c1 Binary files /dev/null and b/Source/BWEM/doc/html/Andromeda.png differ diff --git a/Source/BWEM/doc/html/Baby Steps.png b/Source/BWEM/doc/html/Baby Steps.png new file mode 100644 index 0000000..2c88d65 Binary files /dev/null and b/Source/BWEM/doc/html/Baby Steps.png differ diff --git a/Source/BWEM/doc/html/Great Barrier Reef.png b/Source/BWEM/doc/html/Great Barrier Reef.png new file mode 100644 index 0000000..6f764f7 Binary files /dev/null and b/Source/BWEM/doc/html/Great Barrier Reef.png differ diff --git a/Source/BWEM/doc/html/Heartbreak Ridge.png b/Source/BWEM/doc/html/Heartbreak Ridge.png new file mode 100644 index 0000000..a31f422 Binary files /dev/null and b/Source/BWEM/doc/html/Heartbreak Ridge.png differ diff --git a/Source/BWEM/doc/html/Iron.html b/Source/BWEM/doc/html/Iron.html new file mode 100644 index 0000000..d99869d --- /dev/null +++ b/Source/BWEM/doc/html/Iron.html @@ -0,0 +1,134 @@ + + + + + +BWEM library : Iron + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Iron

+ +
+

+Iron is a StarCraft AI based on the BWEM library (same author). +
+Iron can be seen as an extension of Stone. +

+Like Stone: +

    +
  • Iron is Terran bot. +
  • Iron is Agent Oriented. +
  • Focus has been on individual behaviors: Agents are highly autonomous and versatile. +
  • These behaviors have been carefully designed to avoid indecisiveness, one of the most common and frustrating issues of AIs. +
  • Iron aims at robustness and doesn't contain any code specific to a particular bot. +
  • SCVs play an important role. +
+

+Unlike Stone: +

    +
  • Iron is a complete bot that can deal with most types of threats. +
  • Iron can expand. +
  • Iron is a hybrid system: alongside the Multi-Agent paradigm, it uses a bunch of global strategies and algorithms. +
+

+Iron competed in the following tournaments: +

+

+

+Iron is still under developement. +
To see its progress, go to SSCAIT Tournament. +

+

+


+

+

+

Iron's FAQ

+
+ +

Why this name?

+Iron is Stone's successor... + +

How strong is it?

+Iron placed 2nd in the IEEE CIG 2016 StarCraft AI Competition, +and 1st in the AIIDE 2016 StarCraft AI Competition, +making it one of the strongest StarCraft bots. + +

What is Iron's style of play?

+Like its father Stone, Iron used to be a pure conservative bot. This comes from how each unit normally behave. +
+Iron now uses a fight simulator which can make its units look like very agressive. +
+Autonomy of Iron's units gives its play a general guerilla aspect. +
+In general, Iron favours the army race, rather than the tech race or the economy race. + +

I'd like to watch some of its games...

+Iron is currently playing games against other bots in SSCAIT Tournament's ladder. + +

How to play against Iron?

+To play against Iron you need: +
- a proper Brood War installation. +
- the last release of the BWAPI library. +
- Iron's binaries: available here. +
A tutorial is available here: Starcraft Broodwar: Play vs custom AI . +

+Check also: Interactive Iron + +

Which version of BWEM does Iron use?

+Iron uses the latest version of the BWEM Library. + +

Which version of the BWAPI does Iron use?

+Iron uses BWAPI 4.1.2. + +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + diff --git a/Source/BWEM/doc/html/Iron.jpg b/Source/BWEM/doc/html/Iron.jpg new file mode 100644 index 0000000..4930a3d Binary files /dev/null and b/Source/BWEM/doc/html/Iron.jpg differ diff --git a/Source/BWEM/doc/html/Ride of Valkyries.png b/Source/BWEM/doc/html/Ride of Valkyries.png new file mode 100644 index 0000000..e3d6033 Binary files /dev/null and b/Source/BWEM/doc/html/Ride of Valkyries.png differ diff --git a/Source/BWEM/doc/html/Stone.html b/Source/BWEM/doc/html/Stone.html new file mode 100644 index 0000000..a128949 --- /dev/null +++ b/Source/BWEM/doc/html/Stone.html @@ -0,0 +1,129 @@ + + + + + +BWEM library : Stone + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Stone

+ +
+

+Stone is the very first StarCraft AI based on the BWEM library (same author). +
+Although it is a simple bot with a simple SCV rush strategy, it uses many of BWEM's features. +

+Stone is Agent Oriented and was designed with this idea in mind: Agents should constantly be doing something valuable. At least they should never be doing something completely idiot. +
+With this in mind, focus has been on individual behaviors: in Stone, Agents are highly autonomous and versatile. +
+These behaviors are simple and combine well together. This, coupled to Agents's ability to cooperate at any time, makes the overall behavior especially robust. +
+Robustness also comes of generality: Stone doesn't contain any code specific to a particular bot. It doesn't implement any clever or complex behavior either. +If ever such a thing appears, it is emergent behavior. This happens sometimes :) +

+Stone competed in the following tournaments: +

+

+

+


+

+

+

Stone's FAQ

+
+ +

Why this name?

+Stone can only handle the first Terran units available. Although Stone's SCVs are quite capable, they can't really fight against advanced units like Vultures or Dragoons. +For this reason, Stone has to live during the early game: the Stone Age... + +

How strong is it?

+Stone placed 2nd in the round-robin group phase of the SSCAIT 2015 Tournament, making it one of the strongest StarCraft bots. +
That said, Stone only produces SCVs. So if you or your bot can figure out a way to fend off their harassment, it is likely you will win. +
To see some effective counter as a Terran, download and watch Stone-LetaBot.rep below. + +

I'd like to watch some of its games...

+Actually you can watch a couple of commented games which were held during the SSCAIT 2015 Tournament: + +

There are also a couple of replays available here. +

    +
  • Stone-LetaBot.rep shows Stone usual harassment... and some impressive defense by LetaBot!
  • +
  • Stone-ZZZkBot.rep shows the clash of styles between two rushing bots. +
    Stone's strategy usually hard counters zerling rushes, but in this game its scouting has been delayed. +
    This leads to a crasy, quite balanced and dynamic game, with suspense until the very end.
  • +
+ +

How to play against Stone?

+To play against Stone you need: +
- a proper Brood War installation. +
- the last release of the BWAPI library. +
- Stone's binaries: download. +
A tutorial is available here: Starcraft Broodwar: Play vs custom AI . + +

Which version of the BWEM does Stone use?

+Stone (SSCAIT 2015 version) uses BWEM 1.2 + +

How big is the code?

+Although Stone's strategy sounds simple, its implementation isn't. +There are 8000 lines of code for the bot itself, plus 5000 lines for the map analyser (BWEM). + +

Will you continue to work on Stone?

+Probably not. I may test a couple of ideas with Stone, nothing more. +

I'am currently developping a new bot, called Iron. +
If you liked Stone, you may like Iron too... + + +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + diff --git a/Source/BWEM/doc/html/Stone.jpg b/Source/BWEM/doc/html/Stone.jpg new file mode 100644 index 0000000..27b0189 Binary files /dev/null and b/Source/BWEM/doc/html/Stone.jpg differ diff --git a/Source/BWEM/doc/html/about.html b/Source/BWEM/doc/html/about.html new file mode 100644 index 0000000..67436b9 --- /dev/null +++ b/Source/BWEM/doc/html/about.html @@ -0,0 +1,63 @@ + + + + + +BWEM library : about + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

about

+
+

+My name is Igor Dimitrijevic. I am the author of the BWEM library. +

+You can reach me by email at igor_dimitrijevic@yahoo.fr +

+See also the project site : http://sourceforge.net/projects/BWEM +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + diff --git a/Source/BWEM/doc/html/download.html b/Source/BWEM/doc/html/download.html new file mode 100644 index 0000000..b857a63 --- /dev/null +++ b/Source/BWEM/doc/html/download.html @@ -0,0 +1,67 @@ + + + + + +BWEM library : about + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

download

+
+

+The library can be downloaded here : http://sourceforge.net/projects/bwem/files +
+The archive only contains sources files and documentation files. +
+No installer is needed. +

+Stone's binaries(SSCAIT2015) are available too, plus a couple of replays. +

+New: Interactive Iron's binaries available. +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + diff --git a/Source/BWEM/doc/html/faq.html b/Source/BWEM/doc/html/faq.html new file mode 100644 index 0000000..17fe47c --- /dev/null +++ b/Source/BWEM/doc/html/faq.html @@ -0,0 +1,289 @@ + + + + + +BWEM library : FAQ + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

FAQ

+
+

+


+ +

+


+

+ +

Where is the API documentation?

+The whole documentation of the BWEM library is located in its headers source files. +
+A good place to start with is src/bwem.h +
+Every single function and type is documented in details. +

+You don't need to read the whole API documentation though. +The library provides concret and expected abstractions such as areas and choke points. +You may want to read first the files src/examples.h and src/examples.cpp, which provide runable examples of how you can use the library and access the features. + +

How mature is the library?

+I consider it quite mature, in terms of both functionality and robustness. +
    +
  • The API is stable now. +
  • The functionality should not evolve much, since the library provides all you need to access the Areas, ChokePoints, Bases, Tiles and MiniTiles in an easy and efficient way. +
  • The library has been tested on most of the official maps of StarCraft and BroodWar, with satisfying results. +
  • Stone, the first bot based on the library, demonstrated both its functionality and its robustness. +
  • Iron, currently under development, is based on the library too. +
+ + +

How fast is it?

+The analysis: +

+On my computer, which runs at 2.5 GHz, the whole analysis takes 100 ms for typical maps used in StarCraft AI competitions. +
+It takes 500 ms to analyse one of the biggest map: Frozen Sea.scx. +

+As a consequence, you should be able to run in debug mode in a decent way. +

+The services: +

+Most of the functionality is provided with a time complexity of O(1). This includes path and ground distance requests. + + +

How robust is it?

+The library has been tested on many maps, including special ones like Crystallis.scm. +
+If you do encounter some maps yielding unacceptable results, or even a crash, please let me know! + + +

Is it easy to use?

+I think so, but that's obviously subjective. +Here is a snippet, taken from the file examples.cpp that comes with the library: +

+

+{
+    // ...
+  
+    for (const Area & area : theMap.Areas())
+        for (const Base & base : area.Bases())
+        {
+            if (MapDrawer::showBases)
+                Broodwar->drawBoxMap(Position(base.Location()),
+                                     Position(base.Location() + UnitType(Terran_Command_Center).tileSize()),
+                                     MapDrawer::Color::bases);
+
+            if (MapDrawer::showAssignedRessources)
+            {
+                vector<Ressource *> AssignedRessources(base.Minerals().begin(), base.Minerals().end());
+                AssignedRessources.insert(AssignedRessources.end(), base.Geysers().begin(), base.Geysers().end());
+
+                for (const Ressource * r : AssignedRessources)
+                    Broodwar->drawLineMap(base.Center(), r->Pos(), MapDrawer::Color::assignedRessources);
+            }
+        }
+
+    // ...
+}
+
+

+The extract shows how to iterate through the Areas, the Bases, and their assigned Ressources. +
+Do not hesitate to read more of examples.cpp! +

+MapDrawer is a helper class that is provided together with the library. It can be used to print information about the map onto the game screen. +
+Another helper class, MapPrinter, can be used to print information about the map into a file. +
+Both are highly customizable and proved to be useful for debug purposes. +
+By the way, they have been used to produce the screenshots in this site. +

+Safety is another aspect of the ease of use. +BWEM provides safety through its use of modern C++ style. +
+An exemple is type safety. Programming an AI for Starcraft requires dealing with three kinds of positions. +In my experience, this is the source of a huge number of bugs. +In order to avoid the possibility of errors, BWEM's API uses the template TPosition and its specializations + (Position, WalkPosition, TilePosition) +provided by the BWAPI library, whenever possible. +

+Also, make sure to read the getting started page. + + +

How does it work?

+The analysis follows the steps: +
    +
  1. Altitudes are computed for each MiniTile. +

    +

  2. The Areas are computed by considering the miniTiles successively in descending order of their altitude. +
    + Each of them either: +
    - involves the creation of a new Area. +
    - is added to some existing neighbouring Area. +
    - makes two neighbouring Areas merge together. +
    +

    +

  3. The ChokePoints are deduced. +

    +

  4. Some statistics and measures like paths and ground distances are calculated. +

    +

  5. The Bases are created at relevant locations. +
+The library also provides automatic recalculation of paths and ground distances (in the case blocking Minerals being destroyed for example). +
When this occurs, only a small part of the analysis is processed again. + + +

What are altitudes?

+The altitude is the distance from the nearest non walkable MiniTile, except those which are part of small enough zones (lakes). +

+Altitudes play an important role in the analysis process. +

+Besides that, altitudes can be very useful in implementing various algorithms. +

+Suppose, for example, you have your air unit somewhere in some area, being attacked by ground units. +You want it to reach the nearest non walkable zone, to escape from the pursuers. +
Knowing the altitude for each MiniTile, you can use this very simple greedy algorithm: among the 8 directions, +always choose the one for which the altitude of the neighbouring MiniTile is the lowest. This way you get the shortest path in O(1). +

+Conversely it is just as easy to reach highest altitudes, though there may be local optima. + + +

Is the code simple?

+Clearly, it is not trivial! +
The algorithms used are, however, quite simples. Most of them are derived from dijkstra and breadth-first search algorithms. +
Moreover the whole analysis is divided into sequential and well separated processes so that it is easy to reason about. + + +

What if I'm not satisfied with the results of the analysis?

+Areas, ChokePoints, Base placement are automatically computed by the BWEM library. +
You may not agree with all the results, for all the maps. +

+Fortunatly, most of the algorithms used by the analysis are parametrised and it is fairly easy to modify these parameters. +
More information about that in bwem.h. +

+As discussed in Is the code simple?, the algorithms themselves are not that complex. +You might want to modify them so that they fit your needs. + + +

So may I modify the code to fit my needs?

+Usually, when you use a library, you don't want to modify it, as it may be hard work and error prone. +Moreover, this removes the possibility of using next versions of the library easily. +

+The case of the BWEM library is particular, however: +
- it doesn't aim at evolving much, as it is quite mature and stable now. +
- some users are not involved in a long-term project, especially those willing to compete in StarCraft AI competitions for one year only. +
So if you really need to change some of the code to fit your particular needs, feel free! + + +

Is the library extensible?

+Appart from willing to modify some part of the code, you may want to add your own, related features. +
For simple cases, you will simply write functions that work on Areas, ChokePoints or Bases. +
But what if you want, saying, Areas to contain more information? +

+As you don't want to modify the library, you will have to define some AreaExtension class. +
Then, you usually will want to be able to: +

    +
  • access the Area from its corresponding AreaExtension → you can use either Area::Id() or Areas addresses. +
  • access the AreaExtension from its corresponding Area → you can use Area::Ext() and Area::SetExt(). +
+More generally, BWEM's main classes inherit from UserData, which provides the Ext() / SetExt() methods. +You can use them to extend theses classes. +It is neither an easy nor a safe way, but it is fearly feasible. +

+By the way, Stone demonstrates this technique. It uses 4 extension types: VArea, VChokePoint, VBase and VMap. +
You can look at the source code for more details. +

+ + +

Are the paths optimal?

+The BWEM library provides paths made of ChokePoints. They are intented to be shortest, ground, paths. +

+The suggested paths, if not optimal, are still very good paths. +

+Looking at the first screenshot in the home page you may notice that the printed path connects the middle of the ChokePoints. +
Of course, in the general case the shortest path do NOT go through the exact middle of the ChokePoints. +
That the printed path connects middles is just a visual choice. It doesn't mean you should do the same. In a real case where you +have to move a unit frame by frame, you would order it to move to the middle of next ChokePoint that is far enough (saying 10 tiles). +This way, the unit will go through the ChokePoints of the path in a smart way. +
Stone demonstrates this technique. If you are interested, download its source code and look at the file walking.cpp. + + +

Can I use the library together with other libraries?

+Of course you can. +
The BWEM library is a light-weight library with namespaces and minimal dependancies. It is not likely to interfere with other libraries. +

+For example, you can include the BWEM library even if you already uses the BWTA2 library. +This way wou can access to specific features from each library. +

+ + + + +


+

+ +

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + + + + diff --git a/Source/BWEM/doc/html/features.html b/Source/BWEM/doc/html/features.html new file mode 100644 index 0000000..fd2571d --- /dev/null +++ b/Source/BWEM/doc/html/features.html @@ -0,0 +1,79 @@ + + + + + +BWEM library : features + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Features

+
+

+

    +
  • Modern C++11 code
  • +
  • Standard, portable C++ code.
  • +
  • No dependancies, except for the BWAPI library.
  • +
  • Provides useful debugging tools (print the map into a file, onto the screen)
  • +
  • Provides common and expected high level abstractions: areas, choke points, bases.
  • +
  • Provides relevant, low level representations: tiles and mini-tiles.
  • +
  • Provides distance to nearest edge, for each mini-tile.
  • +
  • Provides paths and ground distance.
  • +
  • Handles blocking mineral patches and blocking static buildings
  • +
  • Handles stacked mineral patches and stacked static buildings
  • +
  • Handles minerals that need to be gathered before a base can be built.
  • +
  • Dynamic: Paths and ground distance are automatically updated when blocking items get destroyed
  • +
  • Robust: performs well for all the maps tested (including Crystallis.scm)
  • +
  • Fast analysis: performs in less then 1 second
  • +
  • Scales: Frozen Sea.scx analysis still performs in less then 1 second
  • +
  • Fast access: almost all the functionality is O(1), including paths and ground distance.
  • +
  • Memory used < 128 x number of tiles
  • +
  • Extensible : provides free-to-use data for main types.
  • + +
+

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + diff --git a/Source/BWEM/doc/html/help.html b/Source/BWEM/doc/html/help.html new file mode 100644 index 0000000..8097407 --- /dev/null +++ b/Source/BWEM/doc/html/help.html @@ -0,0 +1,70 @@ + + + + + +BWEM library : help + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

help

+
+

+ + +Have you read the FAQ? +

+Looking for the API documentation? +

+Also make sure to read the getting started section. +

+If you still encounter some problems, you can contact me by email at igor_dimitrijevic@yahoo.fr +or post a question here : http://sourceforge.net/p/bwem/discussion/general +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + + diff --git a/Source/BWEM/doc/html/index.html b/Source/BWEM/doc/html/index.html new file mode 100644 index 0000000..96c6b1d --- /dev/null +++ b/Source/BWEM/doc/html/index.html @@ -0,0 +1,152 @@ + + + + + +BWEM library Homepage + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Welcome to the BWEM library homepage

+
+
[online version of this page]
+

+Brood War Easy Map is a C++ library that analyses Brood War's maps and +provides relevant information such as areas, choke points and base locations. +
+It is built on top of the BWAPI library. +
+It first aims at simplifying the development of bots for Brood War, but can be used +for any task requiring high level map information. +It can be used as a replacement for the BWTA2 add-on, +as it performs faster and shows better robustness while providing similar information. +

+The current version is 1.4.1 +

+It has been tested with Microsoft Visual Studio Express 2013 for Windows Desktop, 32-bit +

+If you encounter any bug, error or warning message, do not hesitate to report it on +http://sourceforge.net/p/bwem/discussion/general +or contact me. +


+

Screenshots:

+

+ +
+Andromeda.scx +

+ +
+Ride of Valkyries.scx +

+ +
+Heartbreak Ridge.scx +

+ +
+Great Barrier Reef.scx +

+ +
+Baby Steps.scm +

+


+

Download link

+
+

License: MIT/X11

+
+

Requirements:

+
    +
  • BWAPI 4.1.2
  • +
  • C++11 compiler (e.g. Visual Studio 2013)
  • +
+
+

Limitations:

+
    +
  • BWEM doesn't provide any geometric description (polygon) of the computed areas. If you need this, check the BWTA2 library
  • +
  • BWEM is not compatible with older versions of BWAPI (prior to 4.0). If you need such compatibility, check the BWTA library
  • +
+

+


+

History:

+

July, 2017 : 1.4

+
    +
  • fix: A bug in Area::BoundingBoxSize
  • +
  • fix: A bug with BWAPIClient and Map reinitialization (thanks to Florian Klemme for report and good hint)
  • +
  • fix: A bug with Electric Circuit (thanks to Florian Klemme @Kruecke for report)
  • +
  • fix: UserData::(Set)Ext was wrongly equivalent to UserData::(Set)Ptr (thanks to Florian Klemme for report and fix)
  • +
  • fix: Compiling under vs 2017 (thanks to Joseph Huang for report and fix)
  • +
  • new feature: ExampleWall
  • +
  • new feature: BWEM_ext::disjoint
  • +
+

September, 2016 : 1.3

+
    +
  • fix: A bug in Map::Crop
  • +
  • fix: The length calculation in Map::GetPath has been improved in the case of a path through one ChokePoint exactly.
  • +
  • new feature: Area::LowGroundPercentage, Area::HighGroundPercentage, Area::VeryHighGroundPercentage
  • +
  • new feature: Tile::Doodad
  • +
  • new feature: Ressource::Amount
  • +
  • new feature: inBoundingBox
  • +
  • new feature: Map::Initialized()
  • +
  • new feature: GridMap
  • +
  • new feature: Added the macros BWEM_USE_WINUTILS and BWEM_USE_MAP_PRINTER
  • +
  • break: Tile::HigherGround was somewhat useless and has been replaced with Tile::GroundHeight (see the screenshot Ride of Valkyries.scx. See also this related issue in BWAPI).
  • +
  • break: The type utils::check_t is now a class enum. If you used the 'check' and 'no_check' values explicitly, you will have to replace them with 'check_t::check' and 'check_t::no_check' respectively.
  • +
+

November, 2015 : 1.2

+
    +
  • fix: MapPrinter could crash when writing the file, due to some invalid path. Now errors are handled in the constructor in a safer way.
  • +
  • fix: The semantics of MapPrinter::showAreas and MapPrinter::showContinents have been made clearer.
  • +
  • fix: An empty Log.txt was created and could crash, due to some invalid path. This file creation has been removed.
  • +
  • fix: Tile::Data and Tile::SetData are no longer used by the internals. This removes the possibility that the automatic path recalculation overwrites user data.
  • +
  • fix: BWAPI_ext::makeBoundingBoxIncludePoint is now more robust.
  • +
  • feature: Area, ChokePoint, Base and Tile classes now inherit from the UserData class, thus providing them with the same free-to-use data.
  • +
+

September, 2015 : first release 1.0 beta

+

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + diff --git a/Source/BWEM/doc/html/interactiveIron.html b/Source/BWEM/doc/html/interactiveIron.html new file mode 100644 index 0000000..280a99d --- /dev/null +++ b/Source/BWEM/doc/html/interactiveIron.html @@ -0,0 +1,139 @@ + + + + + +BWEM library : Interactive Iron + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Control the Iron!

+
+

+You can now download a special version of Iron called Interactive Iron. +

+This version allow you to control many aspects of Iron play, in real time. +

+As a bot developper, you can use it to make your bot much more robust against any potential threat from Iron. +With Interactive Iron, you can make Iron stronger, and test you bot against it. +You can also make Iron weaker of course. +

+As a human player, using Interactive Iron will allow you to play more different games. +

+


+

+ +

+

game play commands

+
+For most of these commands, type the same command twice to undo. +

integer

+Specify the delay between each frame in ms. +

scvs

+Make more SCVs. When the bases are saturated, excess SCVs are send to fight. +

marines

+Make more marines. +

vultures

+Make more vultures. +

tanks

+Make more tanks. +

goliaths

+Make more goliaths. +

wraiths

+Make more wraiths. +

expand

+Favor fast expansion. +

berserker

+Enter berserker mode. When in berserker mode, conservative behavior is less important. +

target integer integer

+Specify a tile position as the default target for the combat units. +
This doesn't mean all the combat units will always go and stay their, though: Iron's units have their own decisions... +

+Make sure the tile specified is a walkable tile that all the ground units can reach. +

+Also, you may find the command /grid useful to see the each tile position. +

target

+Back to Iron's default target. +
+

+


+

+

BWEM display commands

+
+Even if you don't use the BWEM library, Interactive Iron allows you to test some of its features on any map. +

seas

+

lakes

+

unbuildable

+

gh

+

minerals

+

geysers

+

static buildings

+

bases

+

assigned ressources

+

frontier

+

cp

+

all

+All these commands can be combined together. +
Type the same command twice to undo. +
You can also type show minerals or show minerals. +

+See BWEM::utils::MapDrawer for more information. +

+

+


+

+

BWEM print tool

+
+When the game is finished, Interactive Iron automatically prints the map into the file <StarCraftFolder>bwapi-data/map.bmp. +
See BWEM::utils::MapPrinter for more information. +

+


+

+ +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + diff --git a/Source/BWEM/doc/html/start.html b/Source/BWEM/doc/html/start.html new file mode 100644 index 0000000..4f00081 --- /dev/null +++ b/Source/BWEM/doc/html/start.html @@ -0,0 +1,315 @@ + + + + + +BWEM library : getting started + + + +
+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+
+

Getting started

+
+

+ +


+ +

+ +


+

How to install the library

+The library should be structured as follows: +
    +
  • BWEM
  • +
      +
    • src
    • + Contains the source files of the BWEM library. +
    • EasyBMP_1.06
    • + Contains additionnal source files required by the BWEM::utils::printMap() facility. +
    • doc
    • + Contains an offline copy of this documentation. +
    +
+First note: the BWEM library includes the EasyBMP library (BSD-revised), +whose source files are all contained in the EasyBMP_1.06 folder. +If you don't use the BWEM::utils::printMap() utility though, you don't need this third party library either and +you can freely remove the whole folder. In this case, just make sure you also remove src/mapPrinter.h and src/mapPrinter.cpp plus +any reference to these files. Before you do that though, note this simple and flexible utility may prove to be very useful +while debugging for example. Alternatively, you can just set BWEM_USE_MAP_PRINTER to 0 in src/defs.s: this will disable the compilation of the related files. +
+Note that the EasyBMP library includes some Windows headers, so if you are on Linux, you have to set BWEM_USE_MAP_PRINTER to 0. +

+Second note: if you are in Linux, you also have to set BWEM_USE_WINUTILS to 0 in src/defs.s. +This will disable the compilation of the optional utils provided in winutils.h. +

+The BWEM library consists in C++ source files only. +You could create a library project to get a library file (.lib or .dll) from them. +In the following though, only the direct approach will be discussed, i.e. adding these files into your C++ project. +

+First, you should have a starting or an existing project that makes use of the BWAPI library. +In the following, I will assume you are using the ExampleAIModule that comes with the BWAPI release. Also, make sure to use the latest version (at least 4.1.2). +

+Copy the BWEM folder into your project (wherever appropriate). For example, in ExampleAIModule\Source. +
+Now you have to add the files into your C++ project, just as you would for any other C++ source file. +
+If you are using Visual Studio, this can be achieved using the solution explorer. +
+Right-click on your project, then add a new filter "BWEM". +
+Right-click on that filter, then add an existing item : actually you will browse and select all the files in ExampleAIModule\Source\BWEM\src. +
+Right-click on the filter once again, then browse and add the files EasyBMP.h and EasyBMP.cpp located in ExampleAIModule\Source\BWEM\src\EasyBMP_1.06. +
+That's all! + +


+

How to use the library

+

+Again, I will assume you are working on the ExampleAIModule. +To use the library, we will only modify ExampleAIModule.cpp. +

+First, modify the first lines so that it shows as: +

+

+#include "ExampleAIModule.h"
+#include "BWEM/src/bwem.h"      // update the path if necessary
+#include <iostream>
+
+using namespace BWAPI;
+using namespace Filter;
+//using namespace BWEM;
+//using namespace BWEM::BWAPI_ext;
+//using namespace BWEM::utils;
+
+namespace { auto & theMap = BWEM::Map::Instance(); }
+
+The class BWEM::Map is the entry point for almost every thing in BWEM. +
+The unique instance can be accessed using BWEM::Map::Instance(). +For convenience, we define an alias for it : theMap, local to this file. +We could as well use a reference or a pointer member of the class ExampleAIModule but, because the instance of Map is a global variable, this matters little. +

+You may want to uncomment some or all of the 3 using directives. +The namespace BWEM introduces very few names so bringing them all is probably not a big deal. +
+The two other namespaces are not direcly related to BWEM functionnality, but you might still want to use some of their stuff. +
+BWEM::BWAPI_ext contains several helper functions, built on top of the BWAPI library, that prove to be useful while implementing the BWEM library. +
+BWEM::utils also contains helper functions, but they are more general and do not depend on the BWAPI library. + +

+Before we can use theMap, we need to initialize it. +The right place to do this is in ExampleAIModule::onStart(). +But first, let's inclose the whole code inside a standard and basic: try-catch handler: + +

+void ExampleAIModule::onStart()
+{
+  try
+  {
+    // Hello World!
+
+    // ...
+  }
+  catch (const std::exception & e)
+  {
+    Broodwar << "EXCEPTION: " << e.what() << std::endl;
+  }
+}
+
+ +This will catch any standard exception, as well as any exception from BWEM. +We will do that in the other methods too. +

+Now, we add the initialization code: + +

+    // ...
+      
+    else // if this is not a replay
+    {
+      // Retrieve you and your enemy's races. enemy() will just return the first enemy.
+      // If you wish to deal with multiple enemies then you must use enemies().
+      if ( Broodwar->enemy() ) // First make sure there is an enemy
+        Broodwar << "The matchup is " << Broodwar->self()->getRace() << " vs " << Broodwar->enemy()->getRace() << std::endl;
+      
+      Broodwar << "Map initialization..." << std::endl;
+
+      theMap.Initialize();
+      theMap.EnableAutomaticPathAnalysis();
+      bool startingLocationsOK = theMap.FindBasesForStartingLocations();
+      assert(startingLocationsOK);
+
+      BWEM::utils::MapPrinter::Initialize(&theMap);
+      BWEM::utils::printMap(theMap);      // will print the map into the file <StarCraftFolder>bwapi-data/map.bmp
+      BWEM::utils::pathExample(theMap);   // add to the printed map a path between two starting locations
+       
+      Broodwar << "gg" << std::endl;
+    }
+  }
+  catch (const std::exception & e)
+
+ +We have correctly initialized BWEM, and we have started using it (see the code in printMap and pathExample, which are good examples). +
+But to get the best out of it we need to inform it of two events, so we add the following: + +
+void ExampleAIModule::onUnitDestroy(BWAPI::Unit unit)
+{
+  try
+  {
+    if      (unit->getType().isMineralField())    theMap.OnMineralDestroyed(unit);
+    else if (unit->getType().isSpecialBuilding()) theMap.OnStaticBuildingDestroyed(unit);
+  }
+  catch (const std::exception & e)
+  {
+    Broodwar << "EXCEPTION: " << e.what() << std::endl;
+  }
+}
+
+ +As an example, let's use BWEM::utils::drawMap, which provides similar functionality to BWEM::utils::printMap. +
+Instead of printing the map into a file, it draws it directly on the game screen, using BWAPI primitives. +
+Don't forget to look at the code of drawMap. To use it, we just insert the following in ExampleAIModule::onFrame(): + +
+void ExampleAIModule::onFrame()
+{
+  // Called once every game frame
+  try
+  {
+    BWEM::utils::gridMapExample(theMap);
+    BWEM::utils::drawMap(theMap);
+
+    // Display the game frame rate as text in the upper left area of the screen
+    Broodwar->drawTextScreen(200, 0,  "FPS: %d", Broodwar->getFPS() );
+    Broodwar->drawTextScreen(200, 20, "Average FPS: %f", Broodwar->getAverageFPS() );
+
+    // ...
+  }
+  catch (const std::exception & e)
+  {
+    Broodwar << "EXCEPTION: " << e.what() << std::endl;
+  }
+}
+
+ +(We also called BWEM::utils::gridMapExample, which demonstrates the use of the new class utils::GridMap.) +
+Finally, the class BWEM::utils::MapDrawer comes in handy for controling the information displayed on the screen. +To see that, let's modify ExampleAIModule::onSendText: + +
+void ExampleAIModule::onSendText(std::string text)
+{
+  BWEM::utils::MapDrawer::ProcessCommand(text);
+
+  // Send the text to the game if it is not being processed.
+  Broodwar->sendText("%s", text.c_str());
+
+  // Make sure to use %s and pass the text as a parameter,
+  // otherwise you may run into problems when you use the %(percent) character!
+}
+
+ +Run your bot. You should now be able select which information you want to show. +
+Press <enter>, then type: minerals +
+Do it again. +
+In MapDrawer::ProcessCommand you will find all the available items you can control. + +

+

+home +| +features +| +getting started +| +FAQ +| +Stone +| +Iron +| +Control the Iron! +| +download +| +help +| +about +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/BWEM/doc/html/style.css b/Source/BWEM/doc/html/style.css new file mode 100644 index 0000000..4517a15 --- /dev/null +++ b/Source/BWEM/doc/html/style.css @@ -0,0 +1,2 @@ +.comment{ color: #007000; } +.existingCode{ color: #808080; } diff --git a/Source/BWEM/src/area.cpp b/Source/BWEM/src/area.cpp new file mode 100644 index 0000000..f00c69f --- /dev/null +++ b/Source/BWEM/src/area.cpp @@ -0,0 +1,427 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "area.h" +#include "mapImpl.h" +#include "graph.h" +#include "neutral.h" +#include "winutils.h" +#include + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Area +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +Area::Area(Graph * pGraph, id areaId, WalkPosition top, int miniTiles) +: m_pGraph(pGraph), m_id(areaId), m_top(top), m_miniTiles(miniTiles) +{ + bwem_assert(areaId > 0); + + auto & topMiniTile = GetMap()->GetMiniTile(top); + bwem_assert(topMiniTile.AreaId() == areaId); + + m_maxAltitude = topMiniTile.Altitude(); +} + + +Area::Area(const Area & Other) + : m_pGraph(Other.m_pGraph) +{ + bwem_assert(false); +} + + +Map * Area::GetMap() const +{ + return m_pGraph->GetMap(); +} + + +TilePosition Area::BoundingBoxSize() const +{ + return m_bottomRight - m_topLeft + 1; +} + + +const std::vector & Area::ChokePoints(const Area * pArea) const +{ + auto it = m_ChokePointsByArea.find(pArea); + bwem_assert(it != m_ChokePointsByArea.end()); + return *it->second; +} + + +void Area::AddGeyser(Geyser * pGeyser) +{ + bwem_assert(pGeyser && !contains(m_Geysers, pGeyser)); + m_Geysers.push_back(pGeyser); +} + + +void Area::AddMineral(Mineral * pMineral) +{ + bwem_assert(pMineral && !contains(m_Minerals, pMineral)); + m_Minerals.push_back(pMineral); +} + + +void Area::OnMineralDestroyed(const Mineral * pMineral) +{ + bwem_assert(pMineral); + + auto iMineral = find(m_Minerals.begin(), m_Minerals.end(), pMineral); + if (iMineral != m_Minerals.end()) + fast_erase(m_Minerals, distance(m_Minerals.begin(), iMineral)); + + // let's examine the bases even if pMineral was not found in this Area, + // which could arise if Minerals were allowed to be assigned to neighbouring Areas. + for (Base & base : Bases()) + base.OnMineralDestroyed(pMineral); +} + + +void Area::AddChokePoints(Area * pArea, vector * pChokePoints) +{ + bwem_assert(!m_ChokePointsByArea[pArea] && pChokePoints); + + m_ChokePointsByArea[pArea] = pChokePoints; + + for (const auto & cp : *pChokePoints) + m_ChokePoints.push_back(&cp); +} + + + +vector Area::ComputeDistances(const ChokePoint * pStartCP, const vector & TargetCPs) const +{ + bwem_assert(!contains(TargetCPs, pStartCP)); + + TilePosition start = GetMap()->BreadthFirstSearch(TilePosition(pStartCP->PosInArea(ChokePoint::middle, this)), + [this](const Tile & tile, TilePosition) { return tile.AreaId() == Id(); }, // findCond + [](const Tile &, TilePosition) { return true; }); // visitCond + + vector Targets; + for (const ChokePoint * cp : TargetCPs) + Targets.push_back(GetMap()->BreadthFirstSearch(TilePosition(cp->PosInArea(ChokePoint::middle, this)), + [this](const Tile & tile, TilePosition) { return tile.AreaId() == Id(); }, // findCond + [](const Tile &, TilePosition) { return true; })); // visitCond + + return ComputeDistances(start, Targets); +} + + +// Returns Distances such that Distances[i] == ground_distance(start, Targets[i]) in pixels +// Note: same algorithm than Graph::ComputeDistances (derived from Dijkstra) +vector Area::ComputeDistances(TilePosition start, const vector & Targets) const +{ + const Map * pMap = GetMap(); + vector Distances(Targets.size()); + + Tile::UnmarkAll(); + + multimap ToVisit; // a priority queue holding the tiles to visit ordered by their distance to start. + ToVisit.emplace(0, start); + + int remainingTargets = Targets.size(); + while (!ToVisit.empty()) + { + int currentDist = ToVisit.begin()->first; + TilePosition current = ToVisit.begin()->second; + const Tile & currentTile = pMap->GetTile(current, check_t::no_check); + bwem_assert(currentTile.InternalData() == currentDist); + ToVisit.erase(ToVisit.begin()); + currentTile.SetInternalData(0); // resets Tile::m_internalData for future usage + currentTile.SetMarked(); + + for (int i = 0 ; i < (int)Targets.size() ; ++i) + if (current == Targets[i]) + { + Distances[i] = int(0.5 + currentDist * 32 / 10000.0); + --remainingTargets; + } + if (!remainingTargets) break; + + for (TilePosition delta : { TilePosition(-1, -1), TilePosition(0, -1), TilePosition(+1, -1), + TilePosition(-1, 0), TilePosition(+1, 0), + TilePosition(-1, +1), TilePosition(0, +1), TilePosition(+1, +1)}) + { + const bool diagonalMove = (delta.x != 0) && (delta.y != 0); + const int newNextDist = currentDist + (diagonalMove ? 14142 : 10000); + + TilePosition next = current + delta; + if (pMap->Valid(next)) + { + const Tile & nextTile = pMap->GetTile(next, check_t::no_check); + if (!nextTile.Marked()) + { + if (nextTile.InternalData()) // next already in ToVisit + { + if (newNextDist < nextTile.InternalData()) // nextNewDist < nextOldDist + { // To update next's distance, we need to remove-insert it from ToVisit: + auto range = ToVisit.equal_range(nextTile.InternalData()); + auto iNext = find_if(range.first, range.second, [next] + (const pair & e) { return e.second == next; }); + bwem_assert(iNext != range.second); + + ToVisit.erase(iNext); + nextTile.SetInternalData(newNextDist); + // nextTile.SetPtr(const_cast(¤tTile)); // note: we won't use this backward trace + ToVisit.emplace(newNextDist, next); + } + } + else if ((nextTile.AreaId() == Id()) || (nextTile.AreaId() == -1)) + { + nextTile.SetInternalData(newNextDist); + // nextTile.SetPtr(const_cast(¤tTile)); // note: we won't use this backward trace + ToVisit.emplace(newNextDist, next); + } + } + } + } + } + + bwem_assert(!remainingTargets); + + // Reset Tile::m_internalData for future usage + for (auto e : ToVisit) + pMap->GetTile(e.second, check_t::no_check).SetInternalData(0); + + return Distances; +} + + +void Area::UpdateAccessibleNeighbours() +{ + m_AccessibleNeighbours.clear(); + + for (auto it : ChokePointsByArea()) + if (any_of(it.second->begin(), it.second->end(), [](const ChokePoint & cp){ return !cp.Blocked(); })) + m_AccessibleNeighbours.push_back(it.first); +} + + +// Called for each tile t of this Area +void Area::AddTileInformation(const BWAPI::TilePosition t, const Tile & tile) +{ + ++m_tiles; + if (tile.Buildable()) ++m_buildableTiles; + if (tile.GroundHeight() == 1) ++m_highGroundTiles; + if (tile.GroundHeight() == 2) ++m_veryHighGroundTiles; + + if (t.x < m_topLeft.x) m_topLeft.x = t.x; + if (t.y < m_topLeft.y) m_topLeft.y = t.y; + if (t.x > m_bottomRight.x) m_bottomRight.x = t.x; + if (t.y > m_bottomRight.y) m_bottomRight.y = t.y; +} + + +// Called after AddTileInformation(t) has been called for each tile t of this Area +void Area::PostCollectInformation() +{ +} + + + +// Calculates the score >= 0 corresponding to the placement of a Base Command Center at 'location'. +// The more there are ressources nearby, the higher the score is. +// The function assumes the distance to the nearby ressources has already been computed (in InternalData()) for each tile around. +// The job is therefore made easier : just need to sum the InternalData() values. +// Returns -1 if the location is impossible. + +int Area::ComputeBaseLocationScore(TilePosition location) const +{ + const Map * pMap = GetMap(); + const TilePosition dimCC = UnitType(Terran_Command_Center).tileSize(); + + int sumScore = 0; + for (int dy = 0 ; dy < dimCC.y ; ++dy) + for (int dx = 0 ; dx < dimCC.x ; ++dx) + { + const Tile & tile = pMap->GetTile(location + TilePosition(dx, dy), check_t::no_check); + if (!tile.Buildable()) return -1; + if (tile.InternalData() == -1) return -1; // The special value InternalData() == -1 means there is some ressource at maximum 3 tiles, which Starcraft rules forbid. + // Unfortunately, this is guaranteed only for the ressources in this Area, which is the very reason of ValidateBaseLocation + if (tile.AreaId() != Id()) return -1; + if (tile.GetNeutral() && tile.GetNeutral()->IsStaticBuilding()) return -1; + + sumScore += tile.InternalData(); + } + + return sumScore; +} + + +// Checks if 'location' is a valid location for the placement of a Base Command Center. +// If the location is valid except for the presence of Mineral patches of less than 9 (see Andromeda.scx), +// the function returns true, and these Minerals are reported in BlockingMinerals +// The function is intended to be called after ComputeBaseLocationScore, as it is more expensive. +// See also the comments inside ComputeBaseLocationScore. +bool Area::ValidateBaseLocation(TilePosition location, vector & BlockingMinerals) const +{ + const Map * pMap = GetMap(); + const TilePosition dimCC = UnitType(Terran_Command_Center).tileSize(); + + BlockingMinerals.clear(); + + for (int dy = -3 ; dy < dimCC.y + 3 ; ++dy) + for (int dx = -3 ; dx < dimCC.x + 3 ; ++dx) + { + TilePosition t = location + TilePosition(dx, dy); + if (pMap->Valid(t)) + { + const Tile & tile = pMap->GetTile(t, check_t::no_check); + if (Neutral * n = tile.GetNeutral()) + { + if (n->IsGeyser()) return false; + if (Mineral * m = n->IsMineral()) + if (m->InitialAmount() <= 8) BlockingMinerals.push_back(m); + else return false; + } + } + } + + // checks the distance to the Bases already created: + for (const Base & base : Bases()) + if (roundedDist(base.Location(), location) < min_tiles_between_Bases) return false; + + return true; +} + + +// Fills in m_Bases with good locations in this Area. +// The algorithm repeatedly searches the best possible location L (near ressources) +// When it finds one, the nearby ressources are assigned to L, which makes the remaining ressources decrease. +// This causes the algorithm to always terminate due to the lack of remaining ressources. +// To efficiently compute the distances to the ressources, with use Potiential Fields in the InternalData() value of the Tiles. +void Area::CreateBases() +{ + const TilePosition dimCC = UnitType(Terran_Command_Center).tileSize(); + const Map * pMap = GetMap(); + + + // Initialize the RemainingRessources with all the Minerals and Geysers in this Area satisfying some conditions: + vector RemainingRessources; + for (Mineral * m : Minerals()) if ((m->InitialAmount() >= 40) && !m->Blocking()) RemainingRessources.push_back(m); + for (Geyser * g : Geysers()) if ((g->InitialAmount() >= 300) && !g->Blocking()) RemainingRessources.push_back(g); + + m_Bases.reserve(min(100, (int)RemainingRessources.size())); + + while (!RemainingRessources.empty()) + { + // 1) Calculate the SearchBoundingBox (needless to search too far from the RemainingRessources): + + TilePosition topLeftRessources = {numeric_limits::max(), numeric_limits::max()}; + TilePosition bottomRightRessources = {numeric_limits::min(), numeric_limits::min()}; + for (const Ressource * r : RemainingRessources) + { + makeBoundingBoxIncludePoint(topLeftRessources, bottomRightRessources, r->TopLeft()); + makeBoundingBoxIncludePoint(topLeftRessources, bottomRightRessources, r->BottomRight()); + } + + TilePosition topLeftSearchBoundingBox = topLeftRessources - dimCC - max_tiles_between_CommandCenter_and_ressources; + TilePosition bottomRightSearchBoundingBox = bottomRightRessources + 1 + max_tiles_between_CommandCenter_and_ressources; + makePointFitToBoundingBox(topLeftSearchBoundingBox, TopLeft(), BottomRight() - dimCC + 1); + makePointFitToBoundingBox(bottomRightSearchBoundingBox, TopLeft(), BottomRight() - dimCC + 1); + + // 2) Mark the Tiles with their distances from each remaining Ressource (Potential Fields >= 0) + for (const Ressource * r : RemainingRessources) + for (int dy = -dimCC.y-max_tiles_between_CommandCenter_and_ressources ; dy < r->Size().y + dimCC.y+max_tiles_between_CommandCenter_and_ressources ; ++dy) + for (int dx = -dimCC.x-max_tiles_between_CommandCenter_and_ressources ; dx < r->Size().x + dimCC.x+max_tiles_between_CommandCenter_and_ressources ; ++dx) + { + TilePosition t = r->TopLeft() + TilePosition(dx, dy); + if (pMap->Valid(t)) + { + const Tile & tile = pMap->GetTile(t, check_t::no_check); + int dist = (distToRectangle(center(t), r->TopLeft(), r->Size())+16)/32; + int score = max(max_tiles_between_CommandCenter_and_ressources + 3 - dist, 0); + if (r->IsGeyser()) score *= 3; // somewhat compensates for Geyser alone vs the several Minerals + if (tile.AreaId() == Id()) tile.SetInternalData(tile.InternalData() + score); // note the additive effect (assume tile.InternalData() is 0 at the begining) + } + } + + // 3) Invalidate the 7 x 7 Tiles around each remaining Ressource (Starcraft rule) + for (const Ressource * r : RemainingRessources) + for (int dy = -3 ; dy < r->Size().y + 3 ; ++dy) + for (int dx = -3 ; dx < r->Size().x + 3 ; ++dx) + { + TilePosition t = r->TopLeft() + TilePosition(dx, dy); + if (pMap->Valid(t)) + pMap->GetTile(t, check_t::no_check).SetInternalData(-1); + } + + + // 4) Search the best location inside the SearchBoundingBox: + TilePosition bestLocation; + int bestScore = 0; + vector BlockingMinerals; + + for (int y = topLeftSearchBoundingBox.y ; y <= bottomRightSearchBoundingBox.y ; ++y) + for (int x = topLeftSearchBoundingBox.x ; x <= bottomRightSearchBoundingBox.x ; ++x) + { + int score = ComputeBaseLocationScore(TilePosition(x, y)); + if (score > bestScore) + if (ValidateBaseLocation(TilePosition(x, y), BlockingMinerals)) + { + bestScore = score; + bestLocation = TilePosition(x, y); + } + } + + // 5) Clear Tile::m_internalData (required due to our use of Potential Fields: see comments in 2)) + for (const Ressource * r : RemainingRessources) + for (int dy = -dimCC.y-max_tiles_between_CommandCenter_and_ressources ; dy < r->Size().y + dimCC.y+max_tiles_between_CommandCenter_and_ressources ; ++dy) + for (int dx = -dimCC.x-max_tiles_between_CommandCenter_and_ressources ; dx < r->Size().x + dimCC.x+max_tiles_between_CommandCenter_and_ressources ; ++dx) + { + TilePosition t = r->TopLeft() + TilePosition(dx, dy); + if (pMap->Valid(t)) pMap->GetTile(t, check_t::no_check).SetInternalData(0); + } + + if (!bestScore) break; + + // 6) Create a new Base at bestLocation, assign to it the relevant ressources and remove them from RemainingRessources: + vector AssignedRessources; + for (Ressource * r : RemainingRessources) + if (distToRectangle(r->Pos(), bestLocation, dimCC) + 2 <= max_tiles_between_CommandCenter_and_ressources*32) + AssignedRessources.push_back(r); + really_remove_if(RemainingRessources, [&AssignedRessources](const Ressource * r){ return contains(AssignedRessources, r); }); + + if (AssignedRessources.empty()) + { + //bwem_assert(false); + break; + } + + m_Bases.emplace_back(this, bestLocation, AssignedRessources, BlockingMinerals); + } +} + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/area.h b/Source/BWEM/src/area.h new file mode 100644 index 0000000..eaed6aa --- /dev/null +++ b/Source/BWEM/src/area.h @@ -0,0 +1,190 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_AREA_H +#define BWEM_AREA_H + +#include +#include "bwapiExt.h" +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Mineral; +class Geyser; +class Tile; +class Base; +class ChokePoint; +class Map; + +namespace detail { class Graph; } + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Area +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Areas are regions that BWEM automatically computes from Brood War's maps +// Areas aim at capturing relevant regions that can be walked, though they may contain small inner non walkable regions called lakes. +// More formally: +// - An area consists in a set of 4-connected MiniTiles, which are either Terrain-MiniTiles or Lake-MiniTiles. +// - An Area is delimited by the side of the Map, by Water-MiniTiles, or by other Areas. In the latter case +// the adjoining Areas are called neighbouring Areas, and each pair of such Areas defines at least one ChokePoint. +// Like ChokePoints and Bases, the number and the addresses of Area instances remain unchanged. +// To access Areas one can use their ids or their addresses with equivalent efficiency. +// +// Areas inherit utils::Markable, which provides marking ability +// Areas inherit utils::UserData, which provides free-to-use data. + +class Area : public utils::Markable, public utils::UserData +{ +public: + typedef int16_t id; + + typedef int16_t groupId; + + // Unique id > 0 of this Area. Range = 1 .. Map::Areas().size() + // this == Map::GetArea(Id()) + // Id() == Map::GetMiniTile(w).AreaId() for each walkable MiniTile w in this Area. + // Area::ids are guaranteed to remain unchanged. + id Id() const { return m_id; } + + // Unique id > 0 of the group of Areas which are accessible from this Area. + // For each pair (a, b) of Areas: a->GroupId() == b->GroupId() <==> a->AccessibleFrom(b) + // A groupId uniquely identifies a maximum set of mutually accessible Areas, that is, in the absence of blocking ChokePoints, a continent. + groupId GroupId() const { return m_groupId; } + + // Bounding box of this Area. + const BWAPI::TilePosition & TopLeft() const { return m_topLeft ; } + const BWAPI::TilePosition & BottomRight() const { return m_bottomRight ; } + BWAPI::TilePosition BoundingBoxSize() const; + + // Position of the MiniTile with the highest Altitude() value. + const BWAPI::WalkPosition & Top() const { return m_top; } + + // Returns Map::GetMiniTile(Top()).Altitude(). + altitude_t MaxAltitude() const { return m_maxAltitude; } + + // Returns the number of MiniTiles in this Area. + // This most accurately defines the size of this Area. + int MiniTiles() const { return m_miniTiles; } + + // Returns the percentage of low ground Tiles in this Area. + int LowGroundPercentage() const { return (m_tiles - m_highGroundTiles - m_veryHighGroundTiles) * 100 / m_tiles; } + + // Returns the percentage of high ground Tiles in this Area. + int HighGroundPercentage() const { return m_highGroundTiles * 100 / m_tiles; } + + // Returns the percentage of very high ground Tiles in this Area. + int VeryHighGroundPercentage() const { return m_veryHighGroundTiles * 100 / m_tiles; } + + // Returns the ChokePoints between this Area and the neighbouring ones. + // Note: if there are no neighbouring Areas, then an empty set is returned. + // Note there may be more ChokePoints returned than the number of neighbouring Areas, as there may be several ChokePoints between two Areas (Cf. ChokePoints(const Area * pArea)). + const std::vector & ChokePoints() const { return m_ChokePoints; } + + // Returns the ChokePoints between this Area and pArea. + // Assumes pArea is a neighbour of this Area, i.e. ChokePointsByArea().find(pArea) != ChokePointsByArea().end() + // Note: there is always at least one ChokePoint between two neighbouring Areas. + const std::vector & ChokePoints(const Area * pArea) const; + + // Returns the ChokePoints of this Area grouped by neighbouring Areas + // Note: if there are no neighbouring Areas, than an empty set is returned. + const std::map *> & ChokePointsByArea() const { return m_ChokePointsByArea; } + + // Returns the accessible neighbouring Areas. + // The accessible neighbouring Areas are a subset of the neighbouring Areas (the neighbouring Areas can be iterated using ChokePointsByArea()). + // Two neighbouring Areas are accessible from each over if at least one the ChokePoints they share is not Blocked (Cf. ChokePoint::Blocked). + const std::vector&AccessibleNeighbours() const { return m_AccessibleNeighbours; } + + // Returns whether this Area is accessible from pArea, that is, if they share the same GroupId(). + // Note: accessibility is always symmetrical. + // Note: even if a and b are neighbouring Areas, + // we can have: a->AccessibleFrom(b) + // and not: contains(a->AccessibleNeighbours(), b) + // See also GroupId() + bool AccessibleFrom(const Area * pArea) const { return GroupId() == pArea->GroupId(); } + + // Returns the Minerals contained in this Area. + // Note: only a call to Map::OnMineralDestroyed(BWAPI::Unit u) may change the result (by removing eventually one element). + const std::vector & Minerals() const { return m_Minerals; } + + // Returns the Geysers contained in this Area. + // Note: the result will remain unchanged. + const std::vector & Geysers() const { return m_Geysers; } + + // Returns the Bases contained in this Area. + // Note: the result will remain unchanged. + const std::vector & Bases() const { return m_Bases; } + + Map * GetMap() const; + + Area & operator=(const Area &) = delete; + +//////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + Area(detail::Graph * pGraph, id areaId, BWAPI::WalkPosition top, int miniTiles); + Area(const Area & Other); + void AddChokePoints(Area * pArea, std::vector * pChokePoints); + void AddMineral(Mineral * pMineral); + void AddGeyser(Geyser * pGeyser); + void AddTileInformation(const BWAPI::TilePosition t, const Tile & tile); + void OnMineralDestroyed(const Mineral * pMineral); + void PostCollectInformation(); + std::vector ComputeDistances(const ChokePoint * pStartCP, const std::vector & TargetCPs) const; + void UpdateAccessibleNeighbours(); + void SetGroupId(groupId gid) { bwem_assert(gid >= 1); m_groupId = gid; } + void CreateBases(); + std::vector & Bases() { return m_Bases; } + +private: + const detail::Graph * GetGraph() const { return m_pGraph; } + detail::Graph * GetGraph() { return m_pGraph; } + + int ComputeBaseLocationScore(BWAPI::TilePosition location) const; + bool ValidateBaseLocation(BWAPI::TilePosition location, std::vector & BlockingMinerals) const; + std::vector ComputeDistances(BWAPI::TilePosition start, const std::vector & Targets) const; + + detail::Graph * const m_pGraph; + id m_id; + groupId m_groupId = 0; + BWAPI::WalkPosition m_top; + BWAPI::TilePosition m_topLeft = {std::numeric_limits::max(), std::numeric_limits::max()}; + BWAPI::TilePosition m_bottomRight = {std::numeric_limits::min(), std::numeric_limits::min()}; + altitude_t m_maxAltitude; + int m_miniTiles; + int m_tiles = 0; + int m_buildableTiles = 0; + int m_highGroundTiles = 0; + int m_veryHighGroundTiles = 0; + + std::map *> m_ChokePointsByArea; + std::vector m_AccessibleNeighbours; + std::vector m_ChokePoints; + std::vector m_Minerals; + std::vector m_Geysers; + std::vector m_Bases; +}; + + + + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/base.cpp b/Source/BWEM/src/base.cpp new file mode 100644 index 0000000..aa11a5f --- /dev/null +++ b/Source/BWEM/src/base.cpp @@ -0,0 +1,85 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "base.h" +#include "graph.h" +#include "mapImpl.h" +#include "neutral.h" +#include "bwapiExt.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Base +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +Base::Base(Area * pArea, const TilePosition & location, const vector & AssignedRessources, const vector & BlockingMinerals) + : m_pArea(pArea), + m_pMap(pArea->GetMap()), + m_location(location), + m_center(Position(location) + Position(UnitType(Terran_Command_Center).tileSize()) / 2), + m_BlockingMinerals(BlockingMinerals) +{ + bwem_assert(!AssignedRessources.empty()); + + for (Ressource * r : AssignedRessources) + if (Mineral * m = r->IsMineral()) m_Minerals.push_back(m); + else if (Geyser * g = r->IsGeyser()) m_Geysers.push_back(g); +} + + +Base::Base(const Base & Other) + : m_pMap(Other.m_pMap), m_pArea(Other.m_pArea) +{ + bwem_assert(false); +} + + +void Base::SetStartingLocation(const TilePosition & actualLocation) +{ + m_starting = true; + m_location = actualLocation; + m_center = Position(actualLocation) + Position(UnitType(Terran_Command_Center).tileSize()) / 2; +} + + +void Base::OnMineralDestroyed(const Mineral * pMineral) +{ + bwem_assert(pMineral); + + auto iMineral = find(m_Minerals.begin(), m_Minerals.end(), pMineral); + if (iMineral != m_Minerals.end()) + fast_erase(m_Minerals, distance(m_Minerals.begin(), iMineral)); + + iMineral = find(m_BlockingMinerals.begin(), m_BlockingMinerals.end(), pMineral); + if (iMineral != m_BlockingMinerals.end()) + fast_erase(m_BlockingMinerals, distance(m_BlockingMinerals.begin(), iMineral)); +} + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/base.h b/Source/BWEM/src/base.h new file mode 100644 index 0000000..898c397 --- /dev/null +++ b/Source/BWEM/src/base.h @@ -0,0 +1,117 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_BASE_H +#define BWEM_BASE_H + +#include +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Ressource; +class Mineral; +class Geyser; +class Area; +class Map; + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Base +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// After Areas and ChokePoints, Bases are the third kind of object BWEM automatically computes from Brood War's maps. +// A Base is essentially a suggested location (intended to be optimal) to put a Command Center, Nexus, or Hatchery. +// It also provides information on the ressources available, and some statistics. +// A Base alway belongs to some Area. An Area may contain zero, one or several Bases. +// Like Areas and ChokePoints, the number and the addresses of Base instances remain unchanged. +// +// Bases inherit utils::UserData, which provides free-to-use data. + +class Base : public utils::UserData +{ +public: + + + // Tells whether this Base's location is contained in Map::StartingLocations() + // Note: all players start at locations taken from Map::StartingLocations(), + // which doesn't mean all the locations in Map::StartingLocations() are actually used. + bool Starting() const { return m_starting; } + + // Returns the Area this Base belongs to. + const Area * GetArea() const { return m_pArea; } + + // Returns the location of this Base (top left Tile position). + // If Starting() == true, it is guaranteed that the loction corresponds exactly to one of Map::StartingLocations(). + const BWAPI::TilePosition & Location() const { return m_location; } + + // Returns the location of this Base (center in pixels). + const BWAPI::Position & Center() const { return m_center; } + + // Returns the available Minerals. + // These Minerals are assigned to this Base (it is guaranteed that no other Base provides them). + // Note: The size of the returned list may decrease, as some of the Minerals may get destroyed. + const std::vector & Minerals() const { return m_Minerals; } + + // Returns the available Geysers. + // These Geysers are assigned to this Base (it is guaranteed that no other Base provides them). + // Note: The size of the returned list may NOT decrease, as Geysers never get destroyed. + const std::vector & Geysers() const { return m_Geysers; } + + // Returns the blocking Minerals. + // These Minerals are special ones: they are placed at the exact location of this Base (or very close), + // thus blocking the building of a Command Center, Nexus, or Hatchery. + // So before trying to build this Base, one have to finish gathering these Minerals first. + // Fortunately, these are guaranteed to have their InitialAmount() <= 8. + // As an example of blocking Minerals, see the two islands in Andromeda.scx. + // Note: if Starting() == true, an empty list is returned. + // Note Base::BlockingMinerals() should not be confused with ChokePoint::BlockingNeutral() and Neutral::Blocking(): + // the last two refer to a Neutral blocking a ChokePoint, not a Base. + const std::vector & BlockingMinerals() const { return m_BlockingMinerals; } + + Base & operator=(const Base &) = delete; + +//////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + Base(Area * pArea, const BWAPI::TilePosition & location, const std::vector & AssignedRessources, const std::vector & BlockingMinerals); + Base(const Base & Other); + void SetStartingLocation(const BWAPI::TilePosition & actualLocation); + void OnMineralDestroyed(const Mineral * pMineral); + +private: + Map * GetMap() const { return m_pMap; } + Map * GetMap() { return m_pMap; } + + Map * const m_pMap; + Area * const m_pArea; + BWAPI::TilePosition m_location; + BWAPI::Position m_center; + std::vector m_Minerals; + std::vector m_Geysers; + std::vector m_BlockingMinerals; + bool m_starting = false; +}; + + + + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/bwapiExt.cpp b/Source/BWEM/src/bwapiExt.cpp new file mode 100644 index 0000000..465a70f --- /dev/null +++ b/Source/BWEM/src/bwapiExt.cpp @@ -0,0 +1,39 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "bwapiExt.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace utils; +using namespace detail; + +namespace BWAPI_ext { + + +void drawDiagonalCrossMap(BWAPI::Position topLeft, BWAPI::Position bottomRight, BWAPI::Color col) +{ + bw->drawLineMap(topLeft, bottomRight, col); + bw->drawLineMap(Position(bottomRight.x, topLeft.y), Position(topLeft.x, bottomRight.y), col); +} + + +}} // namespace BWEM::BWAPI_ext + + + diff --git a/Source/BWEM/src/bwapiExt.h b/Source/BWEM/src/bwapiExt.h new file mode 100644 index 0000000..e62e0b5 --- /dev/null +++ b/Source/BWEM/src/bwapiExt.h @@ -0,0 +1,179 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_BWAPI_EXT_H +#define BWEM_BWAPI_EXT_H + +#include +#include "utils.h" +#include "defs.h" +#include + +namespace BWEM { + + +namespace BWAPI_ext { + + +template +inline std::ostream & operator<<(std::ostream & out, BWAPI::Point A) { out << "(" << A.x << ", " << A.y << ")"; return out; } + + +template +inline BWAPI::Position center(BWAPI::Point A) { return BWAPI::Position(A) + BWAPI::Position(Scale/2, Scale/2); } + + +template +inline BWAPI::Point operator+(BWAPI::Point A, int b) { return A + BWAPI::Point(b, b); } + +template +inline BWAPI::Point operator+(int a, BWAPI::Point B) { return B + a; } + +template +inline BWAPI::Point operator-(BWAPI::Point A, int b) { return A + (-b); } + +template +inline BWAPI::Point operator-(int a, BWAPI::Point B) { return a + (B*-1); } + + +// Enlarges the bounding box [TopLeft, BottomRight] so that it includes A. +template +inline void makeBoundingBoxIncludePoint(BWAPI::Point & TopLeft, BWAPI::Point & BottomRight, const BWAPI::Point & A) +{ + if (A.x < TopLeft.x) TopLeft.x = A.x; + if (A.x > BottomRight.x) BottomRight.x = A.x; + + if (A.y < TopLeft.y) TopLeft.y = A.y; + if (A.y > BottomRight.y) BottomRight.y = A.y; +} + + +// Makes the smallest change to A so that it is included in the bounding box [TopLeft, BottomRight]. +template +inline void makePointFitToBoundingBox(BWAPI::Point & A, const BWAPI::Point & TopLeft, const BWAPI::Point & BottomRight) +{ + if (A.x < TopLeft.x) A.x = TopLeft.x; + else if (A.x > BottomRight.x) A.x = BottomRight.x; + + if (A.y < TopLeft.y) A.y = TopLeft.y; + else if (A.y > BottomRight.y) A.y = BottomRight.y; +} + + +template +bool inBoundingBox(const BWAPI::Point & A, const BWAPI::Point & topLeft, const BWAPI::Point & bottomRight) +{ + return (A.x >= topLeft.x) && (A.x <= bottomRight.x) && + (A.y >= topLeft.y) && (A.y <= bottomRight.y); +} + + +template +inline int queenWiseDist(BWAPI::Point A, BWAPI::Point B){ A -= B; return utils::queenWiseNorm(A.x, A.y); } + +template +inline int squaredDist(BWAPI::Point A, BWAPI::Point B) { A -= B; return squaredNorm(A.x, A.y); } + +template +inline double dist(BWAPI::Point A, BWAPI::Point B) { A -= B; return utils::norm(A.x, A.y); } + +template +inline int roundedDist(BWAPI::Point A, BWAPI::Point B) { return int(0.5 + dist(A, B)); } + + +inline int distToRectangle(const BWAPI::Position & a, const BWAPI::TilePosition & TopLeft, const BWAPI::TilePosition & Size) +{ + auto topLeft = BWAPI::Position(TopLeft); + auto bottomRight = BWAPI::Position(TopLeft + Size) - 1; + + if (a.x >= topLeft.x) + if (a.x <= bottomRight.x) + if (a.y > bottomRight.y) return a.y - bottomRight.y; // S + else if (a.y < topLeft.y) return topLeft.y - a.y; // N + else return 0; // inside + else + if (a.y > bottomRight.y) return roundedDist(a, bottomRight); // SE + else if (a.y < topLeft.y) return roundedDist(a, BWAPI::Position(bottomRight.x, topLeft.y)); // NE + else return a.x - bottomRight.x; // E + else + if (a.y > bottomRight.y) return roundedDist(a, BWAPI::Position(topLeft.x, bottomRight.y)); // SW + else if (a.y < topLeft.y) return roundedDist(a, topLeft); // NW + else return topLeft.x - a.x; // W +} + + +template +inline std::vector> innerBorder(BWAPI::Point TopLeft, BWAPI::Point Size, bool noCorner = false) +{ + std::vector> Border; + for (int dy = 0 ; dy < Size.y ; ++dy) + for (int dx = 0 ; dx < Size.x ; ++dx) + if ((dy == 0) || (dy == Size.y-1) || + (dx == 0) || (dx == Size.x-1)) + if (!noCorner || + !(((dx == 0) && (dy == 0)) || ((dx == Size.x-1) && (dy == Size.y-1)) || + ((dx == 0) && (dy == Size.y-1)) || ((dx == Size.x-1) && (dy == 0)))) + Border.push_back(TopLeft + BWAPI::Point(dx, dy)); + + return Border; +} + +template +inline std::vector> outerBorder(BWAPI::Point TopLeft, BWAPI::Point Size, bool noCorner = false) +{ + return innerBorder(TopLeft - 1, Size + 2, noCorner); +} + + +inline std::vector outerMiniTileBorder(BWAPI::TilePosition TopLeft, BWAPI::TilePosition Size, bool noCorner = false) +{ + return outerBorder(BWAPI::WalkPosition(TopLeft), BWAPI::WalkPosition(Size), noCorner); +} + + +inline std::vector innerMiniTileBorder(BWAPI::TilePosition TopLeft, BWAPI::TilePosition Size, bool noCorner = false) +{ + return innerBorder(BWAPI::WalkPosition(TopLeft), BWAPI::WalkPosition(Size), noCorner); +} + + +void drawDiagonalCrossMap(BWAPI::Position topLeft, BWAPI::Position bottomRight, BWAPI::Color col); + + +template +inline bool overlap(const BWAPI::Point & TopLeft1, const BWAPI::Point & Size1, const BWAPI::Point & TopLeft2, const BWAPI::Point & Size2) +{ + if (TopLeft2.x >= TopLeft1.x + Size1.x) return false; + if (TopLeft2.y >= TopLeft1.y + Size1.y) return false; + if (TopLeft1.x >= TopLeft2.x + Size2.x) return false; + if (TopLeft1.y >= TopLeft2.y + Size2.y) return false; + return true; +} + +template +inline bool disjoint(const BWAPI::Point & TopLeft1, const BWAPI::Point & Size1, const BWAPI::Point & TopLeft2, const BWAPI::Point & Size2) +{ + if (TopLeft2.x > TopLeft1.x + Size1.x) return true; + if (TopLeft2.y > TopLeft1.y + Size1.y) return true; + if (TopLeft1.x > TopLeft2.x + Size2.x) return true; + if (TopLeft1.y > TopLeft2.y + Size2.y) return true; + return false; +} + + + + +}} // namespace BWEM::BWAPI_ext + + + + +#endif + diff --git a/Source/BWEM/src/bwem.cpp b/Source/BWEM/src/bwem.cpp new file mode 100644 index 0000000..b9ece00 --- /dev/null +++ b/Source/BWEM/src/bwem.cpp @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "bwem.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace utils; + +namespace detail { + +void onAssertThrowFailed(const std::string & file, int line, const std::string & condition, const std::string & message) +{ + unused(file); + unused(line); + unused(condition); + unused(message); + + assert(false); + throw Exception(file + ", line " + std::to_string(line) + " - " + message); +} + +} // namespace details + + + +} // namespace BWEM + + diff --git a/Source/BWEM/src/bwem.h b/Source/BWEM/src/bwem.h new file mode 100644 index 0000000..25ceb0b --- /dev/null +++ b/Source/BWEM/src/bwem.h @@ -0,0 +1,75 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_H +#define BWEM_H + + +#include "map.h" +#include "tiles.h" +#include "area.h" +#include "cp.h" +#include "base.h" +#include "neutral.h" +#include "gridMap.h" +#include "examples.h" +#include "mapPrinter.h" +#include "mapDrawer.h" +#include "utils.h" +#include "bwapiExt.h" +#include "defs.h" + +/* + + +To see examples of how to use the BWEM library, have a look at the functions declared in examples.h. +All the examples are runable, just follow the steps in the "Getting started" page of the html documentation. + +To get into the documentation of the API, just read the comments in the following files (preferably in this order): + tiles.h + map.h + area.h + cp.h + base.h + neutral.h + + +Many of the algorithms used in the analysis are parametrised and thus can be easily modified: + +- To change the way the Areas or created (make them smaller or greater), look at the "Condition for the neighboring areas to merge" + in MapImpl::ComputeTempAreas. + Also look at the constant area_min_miniTiles defined in defs.h. + +- To change the threshold between Seas and Lakes, look at the constants defined in defs.h. + +- To change the shape of the ChokePoints, just modify the helper function chooseNeighboringArea. + +- To change the way the Bases are located, look at the constants defined in defs.h. + +- To change the way the StartingLocations are assigned to Bases, look at MapImpl::FindBasesForStartingLocations + and the constant max_tiles_between_StartingLocation_and_its_AssignedBase defined in defs.h. + + +If you are interested in some or all the processes of the analysis, you sould start at MapImpl::Initialize(), +in which sub-processes are called in sequentially steps. + + +*/ + + +namespace BWEM +{ + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/cp.cpp b/Source/BWEM/src/cp.cpp new file mode 100644 index 0000000..a232268 --- /dev/null +++ b/Source/BWEM/src/cp.cpp @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "cp.h" +#include "mapImpl.h" +#include "neutral.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class ChokePoint +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +ChokePoint::ChokePoint(detail::Graph * pGraph, index idx, const Area * area1, const Area * area2, const deque & Geometry, Neutral * pBlockingNeutral) +: m_pGraph(pGraph), m_index(idx), m_Areas(area1, area2), m_Geometry(Geometry), + m_pBlockingNeutral(pBlockingNeutral), m_blocked(pBlockingNeutral != nullptr), m_pseudo(pBlockingNeutral != nullptr) +{ + bwem_assert(!Geometry.empty()); + + // Ensures that in the case where several neutrals are stacked, m_pBlockingNeutral points to the bottom one: + if (m_pBlockingNeutral) m_pBlockingNeutral = GetMap()->GetTile(m_pBlockingNeutral->TopLeft()).GetNeutral(); + + m_nodes[end1] = Geometry.front(); + m_nodes[end2] = Geometry.back(); + + int i = Geometry.size() / 2; + while ((i > 0) && (GetMap()->GetMiniTile(Geometry[i-1]).Altitude() > GetMap()->GetMiniTile(Geometry[i]).Altitude())) --i; + while ((i < (int)Geometry.size()-1) && (GetMap()->GetMiniTile(Geometry[i+1]).Altitude() > GetMap()->GetMiniTile(Geometry[i]).Altitude())) ++i; + m_nodes[middle] = Geometry[i]; + + for (int n = 0 ; n < node_count ; ++n) + for (const Area * pArea : {area1, area2}) + { + WalkPosition & nodeInArea = (pArea == m_Areas.first) ? m_nodesInArea[n].first : m_nodesInArea[n].second; + nodeInArea = GetMap()->BreadthFirstSearch(m_nodes[n], + [pArea, this](const MiniTile & miniTile, WalkPosition w) // findCond + { return (miniTile.AreaId() == pArea->Id()) && !GetMap()->GetTile(TilePosition(w), check_t::no_check).GetNeutral(); }, + [pArea, this](const MiniTile & miniTile, WalkPosition w) // visitCond + { return (miniTile.AreaId() == pArea->Id()) || (Blocked() && (miniTile.Blocked() || GetMap()->GetTile(TilePosition(w), check_t::no_check).GetNeutral())); } + ); + } +} + + +ChokePoint::ChokePoint(const ChokePoint & Other) + : m_pGraph(Other.m_pGraph), m_index(0), m_pseudo(false) +{ + bwem_assert(false); +} + + +Map * ChokePoint::GetMap() const +{ + return m_pGraph->GetMap(); +} + + +const BWAPI::WalkPosition & ChokePoint::PosInArea(node n, const Area * pArea) const +{ + bwem_assert((pArea == m_Areas.first) || (pArea == m_Areas.second)); + return (pArea == m_Areas.first) ? m_nodesInArea[n].first : m_nodesInArea[n].second; +} + + +int ChokePoint::DistanceFrom(const ChokePoint * cp) const +{ + return GetGraph()->Distance(this, cp); +} + + +const CPPath & ChokePoint::GetPathTo(const ChokePoint * cp) const +{ + return GetGraph()->GetPath(this, cp); +} + + +// Assumes pBlocking->RemoveFromTiles() has been called +void ChokePoint::OnBlockingNeutralDestroyed(const Neutral * pBlocking) +{ + bwem_assert(pBlocking && pBlocking->Blocking()); + + if (m_pBlockingNeutral == pBlocking) + { + // Ensures that in the case where several neutrals are stacked, m_pBlockingNeutral points to the bottom one: + m_pBlockingNeutral = GetMap()->GetTile(m_pBlockingNeutral->TopLeft()).GetNeutral(); + + if (!m_pBlockingNeutral) + if (GetGraph()->GetMap()->AutomaticPathUpdate()) + m_blocked = false; + } + +} + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/cp.h b/Source/BWEM/src/cp.h new file mode 100644 index 0000000..9afa921 --- /dev/null +++ b/Source/BWEM/src/cp.h @@ -0,0 +1,176 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_CP_H +#define BWEM_CP_H + +#include +#include "bwapiExt.h" +#include +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Neutral; +class Area; +class Map; + +namespace detail { class Graph; } + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class ChokePoint +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// ChokePoints are frontiers that BWEM automatically computes from Brood War's maps +// A ChokePoint represents (part of) the frontier between exactly 2 Areas. It has a form of line. +// A ChokePoint doesn't contain any MiniTile: All the MiniTiles whose positions are returned by its Geometry() +// are just guaranteed to be part of one of the 2 Areas. +// Among the MiniTiles of its Geometry, 3 particular ones called nodes can also be accessed using Pos(middle), Pos(end1) and Pos(end2). +// ChokePoints play an important role in BWEM: +// - they define accessibility between Areas. +// - the Paths provided by Map::GetPath are made of ChokePoints. +// Like Areas and Bases, the number and the addresses of ChokePoint instances remain unchanged. +// +// Pseudo ChokePoints: +// Some Neutrals can be detected as blocking Neutrals (Cf. Neutral::Blocking). +// Because only ChokePoints can serve as frontiers between Areas, BWEM automatically creates a ChokePoint +// for each blocking Neutral (only one in the case of stacked blocking Neutral). +// Such ChokePoints are called pseudo ChokePoints and they behave differently in several ways. +// +// ChokePoints inherit utils::Markable, which provides marking ability +// ChokePoints inherit utils::UserData, which provides free-to-use data. + +class ChokePoint : public utils::Markable, public utils::UserData +{ +public: + // ChokePoint::middle denotes the "middle" MiniTile of Geometry(), while + // ChokePoint::end1 and ChokePoint::end2 denote its "ends". + // It is guaranteed that, among all the MiniTiles of Geometry(), ChokePoint::middle has the highest altitude value (Cf. MiniTile::Altitude()). + enum node {end1, middle, end2, node_count}; + + // Type of all the Paths used in BWEM (Cf. Map::GetPath). + // See also the typedef CPPath. + typedef std::vector Path; + + // Tells whether this ChokePoint is a pseudo ChokePoint, i.e., it was created on top of a blocking Neutral. + bool IsPseudo() const { return m_pseudo; } + + // Returns the two Areas of this ChokePoint. + const std::pair & GetAreas() const { return m_Areas; } + + // Returns the center of this ChokePoint. + const BWAPI::WalkPosition & Center() const { return Pos(middle); } + + // Returns the position of one of the 3 nodes of this ChokePoint (Cf. node definition). + // Note: the returned value is contained in Geometry() + const BWAPI::WalkPosition & Pos(node n) const { bwem_assert(n < node_count); return m_nodes[n]; } + + // Pretty much the same as Pos(n), except that the returned MiniTile position is guaranteed to be part of pArea. + // That is: Map::GetArea(PosInArea(n, pArea)) == pArea. + const BWAPI::WalkPosition & PosInArea(node n, const Area * pArea) const; + + // Returns the set of positions that defines the shape of this ChokePoint. + // Note: none of these MiniTiles actually belongs to this ChokePoint (a ChokePoint doesn't contain any MiniTile). + // They are however guaranteed to be part of one of the 2 Areas. + // Note: the returned set contains Pos(middle), Pos(end1) and Pos(end2). + // If IsPseudo(), returns {p} where p is the position of a walkable MiniTile near from BlockingNeutral()->Pos(). + const std::deque & Geometry() const { return m_Geometry; } + + // If !IsPseudo(), returns false. + // Otherwise, returns whether this ChokePoint is considered blocked. + // Normally, a pseudo ChokePoint either remains blocked, or switches to not blocked when BlockingNeutral() + // is destroyed and there is no remaining Neutral stacked with it. + // However, in the case where Map::AutomaticPathUpdate() == false, Blocked() will always return true + // whatever BlockingNeutral() returns. + // Cf. Area::AccessibleNeighbours(). + bool Blocked() const { return m_blocked; } + + // If !IsPseudo(), returns nullptr. + // Otherwise, returns a pointer to the blocking Neutral on top of which this pseudo ChokePoint was created, + // unless this blocking Neutral has been destroyed. + // In this case, returns a pointer to the next blocking Neutral that was stacked at the same location, + // or nullptr if no such Neutral exists. + Neutral * BlockingNeutral() const { return m_pBlockingNeutral; } + + // If AccessibleFrom(cp) == false, returns -1. + // Otherwise, returns the ground distance in pixels between Center() and cp->Center(). + // Note: if this == cp, returns 0. + // Time complexity: O(1) + // Note: Corresponds to the length in pixels of GetPathTo(cp). So it suffers from the same lack of accuracy. + // In particular, the value returned tends to be slightly higher than expected when GetPathTo(cp).size() is high. + int DistanceFrom(const ChokePoint * cp) const; + + // Returns whether this ChokePoint is accessible from cp (through a walkable path). + // Note: the relation is symmetric: this->AccessibleFrom(cp) == cp->AccessibleFrom(this) + // Note: if this == cp, returns true. + // Time complexity: O(1) + bool AccessibleFrom(const ChokePoint * cp) const { return DistanceFrom(cp) >= 0; } + + // Returns a list of ChokePoints, which is intended to be the shortest walking path from this ChokePoint to cp. + // The path always starts with this ChokePoint and ends with cp, unless AccessibleFrom(cp) == false. + // In this case, an empty list is returned. + // Note: if this == cp, returns [cp]. + // Time complexity: O(1) + // To get the length of the path returned in pixels, use DistanceFrom(cp). + // Note: all the possible Paths are precomputed during Map::Initialize(). + // The best one is then stored for each pair of ChokePoints. + // However, only the center of the ChokePoints is considered. + // As a consequence, the returned path may not be the shortest one. + const ChokePoint::Path & GetPathTo(const ChokePoint * cp) const; + + Map * GetMap() const; + + ChokePoint & operator=(const ChokePoint &) = delete; + +//////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + typedef int index; + ChokePoint(detail::Graph * pGraph, index idx, const Area * area1, const Area * area2, const std::deque & Geometry, Neutral * pBlockingNeutral = nullptr); + ChokePoint(const ChokePoint & Other); + void OnBlockingNeutralDestroyed(const Neutral * pBlocking); + index Index() const { return m_index; } + const ChokePoint * PathBackTrace() const { return m_pPathBackTrace; } + void SetPathBackTrace(const ChokePoint * p) const { m_pPathBackTrace = p; } + +private: + const detail::Graph * GetGraph() const { return m_pGraph; } + detail::Graph * GetGraph() { return m_pGraph; } + + detail::Graph * const m_pGraph; + const bool m_pseudo; + const index m_index; + const std::pair m_Areas; + BWAPI::WalkPosition m_nodes[node_count]; + std::pair m_nodesInArea[node_count]; + const std::deque m_Geometry; + bool m_blocked; + Neutral * m_pBlockingNeutral; + mutable const ChokePoint * m_pPathBackTrace = nullptr; +}; + + +typedef ChokePoint::Path CPPath; + + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/defs.h b/Source/BWEM/src/defs.h new file mode 100644 index 0000000..9ee6252 --- /dev/null +++ b/Source/BWEM/src/defs.h @@ -0,0 +1,90 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_DEFS_H +#define BWEM_DEFS_H + +#include +#include +#include +#include + +namespace BWEM +{ + +namespace detail +{ + void onAssertThrowFailed(const std::string & file, int line, const std::string & condition, const std::string & message); + +} // namespace details + +#define bwem_assert_debug_only(expr) assert(expr) +#define bwem_assert_plus(expr, message) assert(expr) +#define bwem_assert(expr) bwem_assert_plus(expr, "") +#define bwem_assert_throw_plus(expr, message) ((expr)?(void)0:detail::onAssertThrowFailed(__FILE__,__LINE__, #expr, message)) +#define bwem_assert_throw(expr) bwem_assert_throw_plus(expr, "") + + +#define BWEM_USE_WINUTILS 1 // enable(1) or disable(0) the compilation of winutils.cpp + // winutils.h provides optional utils that require the windows headers. + +#define BWEM_USE_MAP_PRINTER 1 // enable(1) or disable(0) the compilation of mapPrinter.cpp + // mapPrinter.h provides optional utils that require the EasyBMP Library (windows). + + +class Exception : public std::runtime_error +{ +public: + explicit Exception(const char * message) : std::runtime_error(message) {} + explicit Exception(const std::string & message) : Exception(message.c_str()) {} +}; + + + + + + + +typedef int16_t altitude_t; // type of the altitudes, in pixels + + + + +namespace utils +{ + +enum class check_t {no_check, check}; + +} // namespace utils + + +namespace detail +{ + +// These constants control how to decide between Seas and Lakes. +const int lake_max_miniTiles = 300; +const int lake_max_width_in_miniTiles = 8*4; + +// At least area_min_miniTiles connected MiniTiles are necessary for an Area to be created. +const int area_min_miniTiles = 64; + +const int max_tiles_between_CommandCenter_and_ressources = 10; +const int min_tiles_between_Bases = 10; + +const int max_tiles_between_StartingLocation_and_its_AssignedBase = 3; + +} // namespace detail + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/exampleWall.cpp b/Source/BWEM/src/exampleWall.cpp new file mode 100644 index 0000000..1d22d43 --- /dev/null +++ b/Source/BWEM/src/exampleWall.cpp @@ -0,0 +1,418 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "exampleWall.h" +#include "map.h" +#include "base.h" +#include "neutral.h" +#include "mapDrawer.h" +#include "bwapiExt.h" +#include + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace utils; +using namespace BWAPI_ext; + +namespace utils +{ + +static bool canBuildWall(const Map & theMap, BWAPI::UnitType type, TilePosition location) +{ + TilePosition dim(type.tileSize()); + + if (!theMap.Valid(location)) return false; + if (!theMap.Valid(location + dim - 1)) return false; + + for (int dy = 0 ; dy < dim.y ; ++dy) + for (int dx = 0 ; dx < dim.x ; ++dx) + { + TilePosition t = location + TilePosition(dx, dy); + + const auto & tile = theMap.GetTile(t); + + if (!tile.Buildable()) return false; + if (tile.GetNeutral()) return false; + } + + return true; +} + + +// Returns a small list of buildable Tiles in the seaside (minimal altitude) of 'area', +// starting at the ChokePoint end 'cpEnd'. +// 'area' should be one of the two Areas of the ChokePoint (the intent is to restrict the Tiles to one of these areas). +// If 'area' == nullptr, any area's seaside can be searched, allowing the Tiles to cross the ChokePoint. + +static vector findBuildableBorderTiles(const Map & theMap, WalkPosition cpEnd, const Area * area) +{ + vector BuildableBorderTiles; + + // Although we want Tiles, we need to use MiniTiles for accuracy. + vector Visited; + queue ToVisit; + + ToVisit.push(cpEnd); + Visited.push_back(cpEnd); + int seasideCount = 0; + + while (!ToVisit.empty()) + { + WalkPosition current = ToVisit.front(); + ToVisit.pop(); + for (WalkPosition delta : { WalkPosition(-1, -1), WalkPosition(0, -1), WalkPosition(+1, -1), + WalkPosition(-1, 0), WalkPosition(+1, 0), + WalkPosition(-1, +1), WalkPosition(0, +1), WalkPosition(+1, +1)}) + { + WalkPosition next = current + delta; + if (theMap.Valid(next)) + if (!contains(Visited, next)) + { + const MiniTile & Next = theMap.GetMiniTile(next, check_t::no_check); + const Tile & NextTile = theMap.GetTile(TilePosition(next), check_t::no_check); + + const bool seaside = (Next.Altitude() <= (seasideCount <= 8 ? 24 : 11)) && + (area ? Next.AreaId() == area->Id() : Next.AreaId() > 0); + if (seaside || NextTile.GetNeutral()) + { + ToVisit.push(next); + Visited.push_back(next); + if (seaside) ++seasideCount; + if (seasideCount > (area ? 130 : 260)) return BuildableBorderTiles; + + // Uncomment this to see the visited MiniTiles + /// bw->drawBoxMap(Position(next), Position(next) + 8, Colors::White); + + if (NextTile.Buildable() && !NextTile.GetNeutral() && (Next.Altitude() <= 11)) + { + if (!contains(BuildableBorderTiles, TilePosition(next))) + BuildableBorderTiles.push_back(TilePosition(next)); + + if ((BuildableBorderTiles.size() >= 3) && (roundedDist(TilePosition(next), TilePosition(cpEnd)) > 12)) + return BuildableBorderTiles; + } + } + } + } + } + + return BuildableBorderTiles; +} + + +class BorderTileInfo +{ +public: + BorderTileInfo(const Map & theMap, const TilePosition & t) + : + m_topBorder(minAltitudeTop(t, theMap) <= 11), + m_bottomBorder(minAltitudeBottom(t, theMap) <= 11), + m_leftBorder(minAltitudeLeft(t, theMap) <= 11), + m_rightBorder(minAltitudeRight(t, theMap) <= 11) + {} + + bool TopBorder() const { return m_topBorder; } + bool BottomBorder() const { return m_bottomBorder; } + bool LeftBorder() const { return m_leftBorder; } + bool RightBorder() const { return m_rightBorder; } + +private: + bool m_topBorder; + bool m_bottomBorder; + bool m_leftBorder; + bool m_rightBorder; +}; + + +static bool tightEnough(UnitType type, const BorderTileInfo & BorderTile) +{ + if (type == Terran_Barracks) + { + if (BorderTile.TopBorder()) return true; + if (BorderTile.RightBorder()) return true; + if (BorderTile.BottomBorder() && BorderTile.LeftBorder()) return true; + return false; + } + + return true; +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class ExampleWall +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +ExampleWall::ExampleWall(const Map & theMap, const ChokePoint * cp, tight_t tight) +: m_theMap(&theMap), m_cp(cp), m_tight(tight) +{ +} + + +void ExampleWall::DrawLocations() const +{ + const BWAPI::Color colorWall = BWAPI::Colors::Purple; + + for (int i = 0 ; i < (int)m_Locations.size() ; ++i) + { + TilePosition pos = m_Locations[i]; + TilePosition dim = m_BuildingTypes[i].tileSize(); + + for (int n = 0 ; n < 2 ; ++n) + bw->drawBoxMap(Position(pos) + n, Position(pos + dim) - n, colorWall); + } +} + + +void ExampleWall::Compute(int wallSize, const vector & BuildableBorderTiles1, + const vector & BuildableBorderTiles2) +{ + map map_BorderTile_Info; + for (const TilePosition & t : BuildableBorderTiles1) map_BorderTile_Info.emplace(t, BorderTileInfo(*m_theMap, t)); + for (const TilePosition & t : BuildableBorderTiles2) map_BorderTile_Info.emplace(t, BorderTileInfo(*m_theMap, t)); + + TilePosition dimDepot(UnitType(Terran_Supply_Depot).tileSize()); + TilePosition dimBarrack(UnitType(Terran_Barracks).tileSize()); + + m_BuildingTypes = {Terran_Barracks, Terran_Supply_Depot, Terran_Supply_Depot}; + m_BuildingTypes.resize(wallSize); + + vector BuildingDims = {dimBarrack, dimDepot, dimDepot}; + BuildingDims.resize(wallSize); + + + TilePosition wallMaxDim = accumulate(BuildingDims.begin(), BuildingDims.end(), TilePosition(0, 0)); + + for (const TilePosition & borderTile1 : BuildableBorderTiles1) + for (const TilePosition & borderTile2 : BuildableBorderTiles2) + if (abs(borderTile1.y - borderTile2.y) < wallMaxDim.y) + if (abs(borderTile1.x - borderTile2.x) < wallMaxDim.x) + for (int permutation = 0 ; permutation < wallSize ; + ++permutation, + // left rotate for next permutation + rotate(m_BuildingTypes.begin(), m_BuildingTypes.begin()+1, m_BuildingTypes.end()), + rotate(BuildingDims.begin(), BuildingDims.begin()+1, BuildingDims.end()) + ) + { + if (!tightEnough(m_BuildingTypes.front(), map_BorderTile_Info.at(borderTile1))) continue; + if (!tightEnough(m_BuildingTypes.back(), map_BorderTile_Info.at(borderTile2))) continue; + + for (int dy1 = 0 ; dy1 < BuildingDims.front().y ; ++dy1) + for (int dx1 = 0 ; dx1 < BuildingDims.front().x ; ++dx1) + for (int dy2 = 0 ; dy2 < BuildingDims.back().y ; ++dy2) + for (int dx2 = 0 ; dx2 < BuildingDims.back().x ; ++dx2) + { + if ((wallSize == 1) && ((dx1 != dx2) || (dy1 != dy2))) continue; + + m_Locations.resize(wallSize); + m_Locations.front() = borderTile1 - TilePosition(dx1, dy1); + m_Locations.back() = borderTile2 - TilePosition(dx2, dy2); + + if (wallSize == 1) + if (!overlap(m_Locations.front(), BuildingDims.front(), borderTile1, TilePosition(1, 1))) + continue; + + if (!canBuildWall(*m_theMap, m_BuildingTypes.front(), m_Locations.front())) continue; + + if (wallSize >= 2) + { + if (!canBuildWall(*m_theMap, m_BuildingTypes.back(), m_Locations.back())) continue; + + if (overlap(m_Locations.front(), BuildingDims.front(), + m_Locations.back(), BuildingDims.back())) continue; + + if (wallSize == 2) + if (disjoint(m_Locations.front(), BuildingDims.front(), + m_Locations.back(), BuildingDims.back())) continue; + } + + int minX = (wallSize < 3) ? -1 : min(m_Locations.front().x, m_Locations.back().x) - BuildingDims[1].x; + int minY = (wallSize < 3) ? -1 : min(m_Locations.front().y, m_Locations.back().y) - BuildingDims[1].y; + int maxX = (wallSize < 3) ? -1 : max(m_Locations.front().x + BuildingDims.front().x, m_Locations.back().x + BuildingDims.back().x); + int maxY = (wallSize < 3) ? -1 : max(m_Locations.front().y + BuildingDims.front().y, m_Locations.back().y + BuildingDims.back().y); + + for (int y = minY ; y <= maxY ; ++y) + for (int x = minX ; x <= maxX ; ++x) + { + if (wallSize == 3) + { + m_Locations[1] = TilePosition(x, y); + + bool middleBuildingHasBorderTiles = false; + for (const auto & BuildableBorderTiles : {BuildableBorderTiles1, BuildableBorderTiles2}) + for (const TilePosition & t : BuildableBorderTiles) + if (overlap(m_Locations[1], BuildingDims[1], t, TilePosition(1, 1))) + middleBuildingHasBorderTiles = true; + if (middleBuildingHasBorderTiles) continue; + + if (!canBuildWall(*m_theMap, m_BuildingTypes[1], m_Locations[1])) continue; + + if (overlap(m_Locations[1], BuildingDims[1], m_Locations[0], BuildingDims[0])) continue; + if (overlap(m_Locations[1], BuildingDims[1], m_Locations[2], BuildingDims[2])) continue; + + if (disjoint(m_Locations[1], BuildingDims[1], m_Locations[0], BuildingDims[0])) continue; + if (disjoint(m_Locations[1], BuildingDims[1], m_Locations[2], BuildingDims[2])) continue; + } + + bool zealotTight = true; + for (int b = 0 ; b < wallSize ; ++b) if (m_BuildingTypes[b] == Terran_Barracks) + for (int d = 0 ; d < wallSize ; ++d) if (m_BuildingTypes[d] == Terran_Supply_Depot) + if (!disjoint(m_Locations[b], BuildingDims[b], m_Locations[d], BuildingDims[d])) + { + if (m_Locations[b].y + BuildingDims[b].y == m_Locations[d].y) + zealotTight = false; + if (m_Locations[d].x + BuildingDims[d].x == m_Locations[b].x) + zealotTight = false; + } + if (!zealotTight) continue; + + if (m_tight == tight_t::zergling) + { + bool zerglingTight = true; + for (int b = 0 ; b < wallSize ; ++b) if (m_BuildingTypes[b] == Terran_Barracks) + for (int d = 0 ; d < wallSize ; ++d) if (m_BuildingTypes[d] == Terran_Supply_Depot) + if (!disjoint(m_Locations[b], BuildingDims[b], m_Locations[d], BuildingDims[d])) + { + if (m_Locations[b].y + BuildingDims[b].y == m_Locations[d].y) + zerglingTight = false; + if (m_Locations[d].x + BuildingDims[d].x == m_Locations[b].x) + zerglingTight = false; + if (m_Locations[b].x + BuildingDims[b].x == m_Locations[d].x) + zerglingTight = false; + } + for (int d1 = 0 ; d1 < wallSize ; ++d1) if (m_BuildingTypes[d1] == Terran_Supply_Depot) + for (int d2 = d1+1 ; d2 < wallSize ; ++d2) if (m_BuildingTypes[d2] == Terran_Supply_Depot) + if (!disjoint(m_Locations[d1], BuildingDims[d1], m_Locations[d2], BuildingDims[d2])) + { + if (m_Locations[d1].x + BuildingDims[d1].x == m_Locations[d2].x) + zerglingTight = false; + if (m_Locations[d2].x + BuildingDims[d2].x == m_Locations[d1].x) + zerglingTight = false; + } + if (!zerglingTight) continue; + } + + const altitude_t maxAltitudeOfCP = m_theMap->GetMiniTile(m_cp->Center()).Altitude(); + const int lengthCP = 2*maxAltitudeOfCP; + + bool blockingDepot = false; + if (lengthCP < 5*32) + for (int d = 0 ; d < wallSize ; ++d) + if (m_BuildingTypes[d] == Terran_Supply_Depot) + { + Position centerDepot = Position(m_Locations[d]) + Position(BuildingDims[d])/2; + if (roundedDist(centerDepot, center(m_cp->Center())) < 2*32) + blockingDepot = true; + } + if (blockingDepot) continue; + + + // left rotate until back to first permutation + while (permutation++ < wallSize) + { + rotate(m_Locations.begin(), m_Locations.begin()+1, m_Locations.end()); + rotate(m_BuildingTypes.begin(), m_BuildingTypes.begin()+1, m_BuildingTypes.end()); + } + + return; + + } + + } + } + + m_Locations.clear(); +} + + +// Try to find a wall inside 'area' that blocks the ChokePoint m_cp. +// If 'area' != nullptr, 'area' must be one of the two Areas of m_cp. +// If 'area' == nullptr, the wall may cross the ChokePoint. +void ExampleWall::Compute(const Area * area) +{ + // Since the wall must block m_cp, it must touch the seasides of 'area' next to both ends of the ChokePoint m_cp. + // So, for each end of m_cp, we first retrieve a list of candidates buildable, border Tiles for the Wall: + + vector BuildableBorderTiles1 = findBuildableBorderTiles(*m_theMap, m_cp->Pos(ChokePoint::end1), area); + vector BuildableBorderTiles2 = findBuildableBorderTiles(*m_theMap, m_cp->Pos(ChokePoint::end2), area); + + // Uncomment this to see the visited retrieved Tiles +/// for (auto t : BuildableBorderTiles1) bw->drawBoxMap(Position(t), Position(t) + 32, Colors::Orange); +/// for (auto t : BuildableBorderTiles2) bw->drawBoxMap(Position(t), Position(t) + 32, Colors::Orange); + + // Prefer a small wall rather than a bigger one: + for (int wallSize = 1 ; wallSize <= 3 ; ++wallSize) + { + Compute(wallSize, BuildableBorderTiles1, BuildableBorderTiles2); + if (Possible()) + break; + + } +} + + +void ExampleWall::Compute() +{ + // First, look for a wall in m_cp->GetAreas().first only. + // If not found, look for a wall in m_cp->GetAreas().second only. + // If not found, look for a wall in both areas. + + // We could easily use a better strategy here. + + if (!Possible()) Compute(m_cp->GetAreas().first); + if (!Possible()) Compute(m_cp->GetAreas().second); + if (!Possible()) Compute(nullptr); + + PostCompute(); +} + + +void ExampleWall::PostCompute() +{ + if (Possible()) + { + m_center = {0, 0}; + for (int i = 0 ; i < (int)m_Locations.size() ; ++i) + m_center += Position(m_Locations[i]) + Position(m_BuildingTypes[i].tileSize())/2; + + m_center /= m_Locations.size(); + } +} + + + +vector findWalls(const Map & theMap) +{ + vector Walls; + + for (const Area & area : theMap.Areas()) + for (const ChokePoint * cp : area.ChokePoints()) + { + Walls.emplace_back(theMap, cp); + Walls.back().Compute(); + } + + return Walls; +} + + +}} // namespace BWEM::utils + + + diff --git a/Source/BWEM/src/exampleWall.h b/Source/BWEM/src/exampleWall.h new file mode 100644 index 0000000..2c6d37a --- /dev/null +++ b/Source/BWEM/src/exampleWall.h @@ -0,0 +1,106 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_EXAMPLE_WALL_H +#define BWEM_EXAMPLE_WALL_H + +#include +#include "defs.h" +#include + +namespace BWEM +{ + +class Map; +class Area; +class ChokePoint; + +namespace utils +{ + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class ExampleWall +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Given a ChokePoint 'cp', ExampleWall can compute a Terran wall that blocks 'cp'. +// After Possible() is called, Possible() will tell whether a wall was found. +// The possible wall compositions are : +// - Terran_Barracks +// - Terran_Barracks, Terran_Supply_Depot +// - Terran_Barracks, Terran_Supply_Depot, Terran_Supply_Depot +// +// Disclaimer: +// - Walls computed with tight_t::zealot should be zealot-thight 90% only. +// - Walls computed with tight_t::zergling should be zergling-thight 60% only. +// Unfortunatly, reaching better accuracy either involves less walls computed, +// either requires pixel-level information that is unavailable via the BWAPI library. +// - The algorithms used in the implementation are very roughly optimized, and quite specific. +// You should be able to run ExampleWall for each ChokePoint at the intialization, +// or to run it on a single ChokePoint per frame. +// For a faster algorithm, or a generic framework, you may want to use some constraint programming approach. +// +// The current implementation demonstrates the use of Tiles, MiniTiles and altitudes with Areas and ChokePoints. + +class ExampleWall +{ +public: + enum class tight_t {zealot, zergling}; + + ExampleWall(const Map & theMap, const ChokePoint * cp, tight_t tight = tight_t::zealot); + + + const ChokePoint * GetCP() const { return m_cp; } + + void Compute(); + void DrawLocations() const; + + bool Possible() const { return !m_Locations.empty(); } + int Size() const { return m_Locations.size(); } + BWAPI::Position Center() const { return m_center; } + + const std::vector & Locations() const { return m_Locations; } + const std::vector & BuildingTypes() const { return m_BuildingTypes; } + +protected: + void Compute(int wallSize, const std::vector & BuildableBorderTiles1, + const std::vector & BuildableBorderTiles2); + void Compute(const Area * area); + void PostCompute(); + + const Map * m_theMap; + const ChokePoint * m_cp; + tight_t m_tight; + BWAPI::Position m_center = BWAPI::Positions::None; + std::vector m_Locations; + std::vector m_BuildingTypes; +}; + + +std::vector findWalls(const Map & theMap); + +// To prints the computed walls each frame, just add these two lines in your onFrame() handler. +// +// for (const ExampleWall & wall : findWalls(Map::Instance())) +// wall.DrawLocations(); +// +// However, this will slow down a lot the game, so you may want to store the returned vector +// in your onStart() handler and then use it in your onFrame() handler. + + + +}} // namespace BWEM::utils + + +#endif + diff --git a/Source/BWEM/src/examples.cpp b/Source/BWEM/src/examples.cpp new file mode 100644 index 0000000..6ee0470 --- /dev/null +++ b/Source/BWEM/src/examples.cpp @@ -0,0 +1,480 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "examples.h" +#include "map.h" +#include "base.h" +#include "neutral.h" +#include "mapPrinter.h" +#include "mapDrawer.h" +#include "gridMap.h" +#include "bwapiExt.h" + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace utils; +using namespace BWAPI_ext; + +namespace utils +{ + + + + +void drawMap(const Map & theMap) +{ + if (MapDrawer::showFrontier) + for (auto f : theMap.RawFrontier()) + bw->drawBoxMap(Position(f.second), Position(f.second + 1), MapDrawer::Color::frontier, bool("isSolid")); + + for (int y = 0 ; y < theMap.Size().y ; ++y) + for (int x = 0 ; x < theMap.Size().x ; ++x) + { + TilePosition t(x, y); + const Tile & tile = theMap.GetTile(t, check_t::no_check); + + if (MapDrawer::showUnbuildable && !tile.Buildable()) + drawDiagonalCrossMap(Position(t), Position(t + 1), MapDrawer::Color::unbuildable); + + if (MapDrawer::showGroundHeight && (tile.GroundHeight() > 0)) + { + auto col = tile.GroundHeight() == 1 ? MapDrawer::Color::highGround : MapDrawer::Color::veryHighGround; + bw->drawBoxMap(Position(t), Position(t)+6, col, bool("isSolid")); + if (tile.Doodad()) bw->drawTriangleMap(center(t)+Position(0, 5), center(t)+Position(-3, 2), center(t)+Position(+3, 2), Colors::White); + } + } + + for (int y = 0 ; y < theMap.WalkSize().y ; ++y) + for (int x = 0 ; x < theMap.WalkSize().x ; ++x) + { + WalkPosition w(x, y); + const MiniTile & miniTile = theMap.GetMiniTile(w, check_t::no_check); + + if (MapDrawer::showSeas && miniTile.Sea()) + drawDiagonalCrossMap(Position(w), Position(w + 1), MapDrawer::Color::sea); + + if (MapDrawer::showLakes && miniTile.Lake()) + drawDiagonalCrossMap(Position(w), Position(w + 1), MapDrawer::Color::lakes); + } + + if (MapDrawer::showCP) + for (const Area & area : theMap.Areas()) + for (const ChokePoint * cp : area.ChokePoints()) + for (ChokePoint::node end : {ChokePoint::end1, ChokePoint::end2}) + bw->drawLineMap(Position(cp->Pos(ChokePoint::middle)), Position(cp->Pos(end)), MapDrawer::Color::cp); + + if (MapDrawer::showMinerals) + for (auto & m : theMap.Minerals()) + { + bw->drawBoxMap(Position(m->TopLeft()), Position(m->TopLeft() + m->Size()) , MapDrawer::Color::minerals); + if (m->Blocking()) + drawDiagonalCrossMap(Position(m->TopLeft()), Position(m->TopLeft() + m->Size()), MapDrawer::Color::minerals); + } + + if (MapDrawer::showGeysers) + for (auto & g : theMap.Geysers()) + bw->drawBoxMap(Position(g->TopLeft()), Position(g->TopLeft() + g->Size()) , MapDrawer::Color::geysers); + + if (MapDrawer::showStaticBuildings) + for (auto & s : theMap.StaticBuildings()) + { + bw->drawBoxMap(Position(s->TopLeft()), Position(s->TopLeft() + s->Size()) , MapDrawer::Color::staticBuildings); + if (s->Blocking()) + drawDiagonalCrossMap(Position(s->TopLeft()), Position(s->TopLeft() + s->Size()), MapDrawer::Color::staticBuildings); + } + + for (const Area & area : theMap.Areas()) + for (const Base & base : area.Bases()) + { + if (MapDrawer::showBases) + bw->drawBoxMap(Position(base.Location()), Position(base.Location() + UnitType(Terran_Command_Center).tileSize()) , MapDrawer::Color::bases); + + if (MapDrawer::showAssignedRessources) + { + vector AssignedRessources(base.Minerals().begin(), base.Minerals().end()); + AssignedRessources.insert(AssignedRessources.end(), base.Geysers().begin(), base.Geysers().end()); + + for (const Ressource * r : AssignedRessources) + bw->drawLineMap(base.Center(), r->Pos(), MapDrawer::Color::assignedRessources); + } + } +} + +#if BWEM_USE_MAP_PRINTER + +static void printNeutral(const Map & theMap, const Neutral * n, MapPrinter::Color col) +{ + const WalkPosition delta(n->Pos().x < theMap.Center().x ? +1 : -1, n->Pos().y < theMap.Center().y ? +1 : -1); + const int stackSize = MapPrinter::showStackedNeutrals ? theMap.GetTile(n->TopLeft()).StackedNeutrals() : 1; + + for (int i = 0 ; i < stackSize ; ++i) + { + auto origin = WalkPosition(n->TopLeft()) + delta * i; + auto size = WalkPosition (n->Size()); + if (!theMap.Valid(origin) || !theMap.Valid(origin + size - 1)) break; + + MapPrinter::Get().Rectangle(origin, origin + size - 1, col, MapPrinter::fill); + + if (MapPrinter::showBlockingBuildings && n->Blocking()) + if (i < stackSize-1) + { + MapPrinter::Get().Point(origin, MapPrinter::Color::blockingNeutrals); + MapPrinter::Get().Point(origin + size - 1, MapPrinter::Color::blockingNeutrals); + MapPrinter::Get().Point(WalkPosition(origin.x, (origin + size - 1).y), MapPrinter::Color::blockingNeutrals); + MapPrinter::Get().Point(WalkPosition((origin + size - 1).x, origin.y), MapPrinter::Color::blockingNeutrals); + } + else + MapPrinter::Get().Rectangle(origin, origin + size - 1, MapPrinter::Color::blockingNeutrals); + } +} + + +static MapPrinter::Color getZoneColor(const Area * area, map & map_Zone_Color) +{ + const int zoneId = MapPrinter::showAreas ? area->Id() : area->GroupId(); + auto it = map_Zone_Color.emplace(zoneId, MapPrinter::Color()); + MapPrinter::Color & color = it.first->second; + if (it.second) // zoneId was not find --> insertion did occur --> we have do define the new color: + { + int tries = 0; + do + { + color.red = rand() % 256; + color.green = rand() % 256; + color.blue = 0; // blue unused for Terrain so that Water can be easily distinguished. + if (++tries > 100) break; + } while ( + // 1) color should not be too dark + (color.red + color.green < 150) || + + // 2) color should differ enough from the colors of the neighbouring Areas + ( MapPrinter::showAreas && + any_of(area->ChokePointsByArea().begin(), area->ChokePointsByArea().end(), + [&map_Zone_Color, &color](const pair *> & neighbour) + { + auto it2 = map_Zone_Color.find(neighbour.first->Id()); + if (it2 == map_Zone_Color.end()) return false; + MapPrinter::Color neighbouringColor = it2->second; + return abs(color.red - (int)neighbouringColor.red) + + abs(color.green - (int)neighbouringColor.green) < 150; + }) + )); + } + + return color; +} + + +void printMap(const Map & theMap) +{ + map map_Zone_Color; // a "Zone" is either an Area or a continent + + for (int y = 0 ; y < theMap.WalkSize().y ; ++y) + for (int x = 0 ; x < theMap.WalkSize().x ; ++x) + { + WalkPosition p(x, y); + const auto & miniTile = theMap.GetMiniTile(p, check_t::no_check); + + MapPrinter::Color col; + if (miniTile.Sea()) + { + if (MapPrinter::showSeaSide && seaSide(p, &theMap)) col = MapPrinter::Color::seaSide; + else col = MapPrinter::Color::sea; + } + else + { + if (MapPrinter::showLakes && miniTile.Lake()) + col = MapPrinter::Color::lake; + else + { + if (MapPrinter::showAltitude) + { + uint8_t c = uint8_t(255 - miniTile.Altitude() * 255 / theMap.MaxAltitude()); + col = MapPrinter::Color{c, c, c}; + } + else col = MapPrinter::Color::terrain; + + if (MapPrinter::showAreas || MapPrinter::showContinents) + if (miniTile.AreaId() > 0) + { + const Area * area = theMap.GetArea(miniTile.AreaId()); + MapPrinter::Color zoneColor = getZoneColor(area, map_Zone_Color); + col.red = zoneColor.red * col.red / 255; + col.green = zoneColor.green * col.green / 255; + col.blue = 0; + } + else col = MapPrinter::Color::tinyArea; + } + } + + MapPrinter::Get().Point(p, col); + } + + if (MapPrinter::showData) + for (int y = 0 ; y < theMap.Size().y ; ++y) + for (int x = 0 ; x < theMap.Size().x ; ++x) + { + int data = theMap.GetTile(TilePosition(x, y)).Data(); + uint8_t c = uint8_t(((data/1)*1) % 256); + MapPrinter::Color col(c, c, c); + WalkPosition origin(TilePosition(x, y)); + MapPrinter::Get().Rectangle(origin, origin + 3, col, MapPrinter::fill); + } + + if (MapPrinter::showUnbuildable) + for (int y = 0 ; y < theMap.Size().y ; ++y) + for (int x = 0 ; x < theMap.Size().x ; ++x) + if (!theMap.GetTile(TilePosition(x, y)).Buildable()) + { + WalkPosition origin(TilePosition(x, y)); + MapPrinter::Get().Rectangle(origin+1, origin + 2, MapPrinter::Color::unbuildable); + } + + if (MapPrinter::showGroundHeight) + for (int y = 0 ; y < theMap.Size().y ; ++y) + for (int x = 0 ; x < theMap.Size().x ; ++x) + { + int groundHeight = theMap.GetTile(TilePosition(x, y)).GroundHeight(); + if (groundHeight >= 1) + for (int dy = 0 ; dy < 4 ; ++dy) + for (int dx = 0 ; dx < 4 ; ++dx) + { + WalkPosition p = WalkPosition(TilePosition(x, y)) + WalkPosition(dx, dy); + if (theMap.GetMiniTile(p, check_t::no_check).Walkable()) // groundHeight is usefull only for walkable miniTiles + if ((dx + dy) & (groundHeight == 1 ? 1 : 3)) + MapPrinter::Get().Point(p, MapPrinter::Color::higherGround); + } + +// if (theMap.GetTile(TilePosition(x, y)).Doodad()) +// MapPrinter::Get().Circle(WalkPosition(TilePosition(x, y)) + 2, 4, MapPrinter::Color(255, 255, 255)); + } + + if (MapPrinter::showAssignedRessources) + for (const Area & area : theMap.Areas()) + for (const Base & base : area.Bases()) + { + for (const Mineral * m : base.Minerals()) + MapPrinter::Get().Line(WalkPosition(base.Center()), WalkPosition(m->Pos()), MapPrinter::Color::bases); + for (const Geyser * g : base.Geysers()) + MapPrinter::Get().Line(WalkPosition(base.Center()), WalkPosition(g->Pos()), MapPrinter::Color::bases); + } + + if (MapPrinter::showGeysers) + for (auto & g : theMap.Geysers()) + printNeutral(theMap, g.get(), MapPrinter::Color::geysers); + + if (MapPrinter::showMinerals) + for (auto & m : theMap.Minerals()) + printNeutral(theMap, m.get(), MapPrinter::Color::minerals); + + if (MapPrinter::showStaticBuildings) + for (auto & s : theMap.StaticBuildings()) + printNeutral(theMap, s.get(), MapPrinter::Color::staticBuildings); + + if (MapPrinter::showStartingLocations) + for (TilePosition t : theMap.StartingLocations()) + { + WalkPosition origin(t); + WalkPosition size(UnitType(Terran_Command_Center).tileSize()); // same size for other races + MapPrinter::Get().Rectangle(origin, origin + size - 1, MapPrinter::Color::startingLocations, MapPrinter::fill); + } + + if (MapPrinter::showBases) + for (const Area & area : theMap.Areas()) + { + for (const Base & base : area.Bases()) + { + WalkPosition origin(base.Location()); + WalkPosition size(UnitType(Terran_Command_Center).tileSize()); // same size for other races + auto dashMode = base.BlockingMinerals().empty() ? MapPrinter::not_dashed : MapPrinter::dashed; + MapPrinter::Get().Rectangle(origin, origin + size - 1, MapPrinter::Color::bases, MapPrinter::do_not_fill, dashMode); + } + +// if (area.LowGroundPercentage() > 66) MapPrinter::Get().Circle(area.Top(), 15, MapPrinter::Color(0, 0, 0), MapPrinter::fill); +// if (area.HighGroundPercentage() > 66) MapPrinter::Get().Circle(area.Top(), 15, MapPrinter::Color(128, 128, 128), MapPrinter::fill); +// if (area.VeryHighGroundPercentage() > 66) MapPrinter::Get().Circle(area.Top(), 15, MapPrinter::Color(255, 255, 255), MapPrinter::fill); + } + + if (MapPrinter::showChokePoints) + { + for (auto f : theMap.RawFrontier()) + MapPrinter::Get().Point(f.second, MapPrinter::Color::chokePoints); + + for (const Area & area : theMap.Areas()) + for (const ChokePoint * cp : area.ChokePoints()) + { + for (ChokePoint::node n : {ChokePoint::end1, ChokePoint::end2}) + MapPrinter::Get().Square(cp->Pos(n), 1, MapPrinter::Color(255, 0, 255), MapPrinter::fill); + MapPrinter::Get().Square(cp->Center(), 1, MapPrinter::Color(0, 0, 255), MapPrinter::fill); + } + } +/* + for (int y = 0 ; y < theMap.Size().y ; ++y) + for (int x = 0 ; x < theMap.Size().x ; ++x) + if (Area::id id = theMap.GetTile(TilePosition(x, y)).AreaId()) + { + WalkPosition origin(TilePosition(x, y)); + MapPrinter::Color col = (id > 0) ? MapPrinter::Color(255, 255, 255) : + (id == -1) ? MapPrinter::Color(128, 128, 128) :MapPrinter::Color(0, 0, 0); + MapPrinter::Get().Rectangle(origin, origin + 3, col); + } +*/ +} + + + +void pathExample(const Map & theMap) +{ + if (theMap.StartingLocations().size() < 2) return; + + const MapPrinter::Color col(255, 255, 255); + + WalkPosition a = WalkPosition(random_element(theMap.StartingLocations())); + + WalkPosition b = a; + while (b == a) b = WalkPosition(random_element(theMap.StartingLocations())); + +// Uncomment this to use random positions for a and b: +// a = WalkPosition(theMap.RandomPosition()); +// b = WalkPosition(theMap.RandomPosition()); + + MapPrinter::Get().Circle(a, 6, col, MapPrinter::fill); + MapPrinter::Get().Circle(b, 6, col, MapPrinter::fill); + + int length; + const CPPath & Path = theMap.GetPath(Position(a), Position(b), &length); + + if (length < 0) return; // cannot reach b from a + + if (Path.empty()) // no ChokePoint between a and b: + { + // let's verify that a and b are in the same Area: + bwem_assert(theMap.GetNearestArea(a) == theMap.GetNearestArea(b)); + + // just draw a single line between them: + MapPrinter::Get().Line(a, b, col, MapPrinter::dashed); + } + else // at least one ChokePoint between a and b: + { + // let's verify that a and b are not in the same Area: + bwem_assert(theMap.GetNearestArea(a) != theMap.GetNearestArea(b)); + + // draw a line between each ChokePoint in Path: + const ChokePoint * cpPrevious = nullptr; + for (const ChokePoint * cp : Path) + { + if (cpPrevious) MapPrinter::Get().Line(cpPrevious->Center(), cp->Center(), col, MapPrinter::dashed); + MapPrinter::Get().Circle(cp->Center(), 6, col); + cpPrevious = cp; + } + + MapPrinter::Get().Line(a, Path.front()->Center(), col, MapPrinter::dashed); + MapPrinter::Get().Line(b, Path.back()->Center(), col, MapPrinter::dashed); + } +} +#endif // BWEM_USE_MAP_PRINTER + + + + + +struct SimpleGridMapCell +{ + vector Units; +}; + + + +class SimpleGridMap : public GridMap +{ +public: + SimpleGridMap(const Map * pMap) : GridMap(pMap) {} + + void Add(Unit unit); + void Remove(Unit unit); + + vector GetUnits(TilePosition topLeft, TilePosition bottomRight, Player player) const; +}; + + +void SimpleGridMap::Add(Unit unit) +{ + auto & List = GetCell(TilePosition(unit->getPosition())).Units; + + if (!contains(List, unit)) List.push_back(unit); +} + + +void SimpleGridMap::Remove(Unit unit) +{ + auto & List = GetCell(TilePosition(unit->getPosition())).Units; + + really_remove(List, unit); +} + + +vector SimpleGridMap::GetUnits(TilePosition topLeft, TilePosition bottomRight, Player player) const +{ + vector Res; + + int i1, j1, i2, j2; + tie(i1, j1) = GetCellCoords(topLeft); + tie(i2, j2) = GetCellCoords(bottomRight); + + for (int j = j1 ; j <= j2 ; ++j) + for (int i = i1 ; i <= i2 ; ++i) + for (Unit unit : GetCell(i, j).Units) + if (unit->getPlayer() == player) + if (BWAPI_ext::inBoundingBox(TilePosition(unit->getPosition()), topLeft, bottomRight)) + Res.push_back(unit); + + return Res; +} + + +void gridMapExample(const Map & theMap) +{ + + // 1) Initialization + SimpleGridMap Grid(&theMap); + + // Note: generally, you will create one instance of GridMap, after calling Map::Instance().Initialize(). + + + // 2) Update (in AIModule::onFrame) + for (int j = 0 ; j < Grid.Height() ; ++j) + for (int i = 0 ; i < Grid.Width() ; ++i) + Grid.GetCell(i, j).Units.clear(); + + for (Unit unit : Broodwar->getAllUnits()) + Grid.Add(unit); + + // Note: alternatively, you could use the Remove and Add methods only, in the relevant BWAPI::AIModule methods. + + + // 3) Use + TilePosition centerTile(theMap.Center()); + for (Unit unit : Grid.GetUnits(centerTile-10, centerTile+10, Broodwar->self())) + Broodwar << "My " << unit->getType().getName() << " #" << unit->getID() << " is near the center of the map." << endl; +} + + + +}} // namespace BWEM::utils + + + diff --git a/Source/BWEM/src/examples.h b/Source/BWEM/src/examples.h new file mode 100644 index 0000000..55a535a --- /dev/null +++ b/Source/BWEM/src/examples.h @@ -0,0 +1,54 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_EXAMPLES_H +#define BWEM_EXAMPLES_H + +#include +#include "exampleWall.h" +#include "defs.h" + +namespace BWEM +{ + +class Map; + +namespace utils +{ + +void drawMap(const Map & theMap); + +#if BWEM_USE_MAP_PRINTER + +// Prints information about theMap into the file specified in MapPrinter::m_fileName +// The printed informations are highly customizable (Cf. mapPrinter.cpp). +void printMap(const Map & theMap); + + +// Prints information about theMap onto the game screen. +// The printed informations are highly customizable (Cf. mapDrawer.cpp). +void pathExample(const Map & theMap); + +#endif + + +// Demonstrates the use of utils::GridMap. +void gridMapExample(const Map & theMap); + + + +/////////////// See alse the examples in exampleWall.h /////////////// + + +}} // namespace BWEM::utils + + +#endif + diff --git a/Source/BWEM/src/graph.cpp b/Source/BWEM/src/graph.cpp new file mode 100644 index 0000000..1ae4bf0 --- /dev/null +++ b/Source/BWEM/src/graph.cpp @@ -0,0 +1,531 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "graph.h" +#include "mapImpl.h" +#include "neutral.h" +#include "winutils.h" +#include +#include + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { +namespace detail { + + +Area * mainArea(MapImpl * pMap, TilePosition topLeft, TilePosition size) +{ + map map_Area_freq; + + for (int dy = 0 ; dy < size.y ; ++dy) + for (int dx = 0 ; dx < size.x ; ++dx) + if (Area * area = pMap->GetArea(topLeft + TilePosition(dx, dy))) + ++map_Area_freq[area]; + + return map_Area_freq.empty() ? nullptr : map_Area_freq.rbegin()->first; +} + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Graph +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +const Area * Graph::GetArea(WalkPosition w) const +{ + Area::id id = GetMap()->GetMiniTile(w).AreaId(); + return id > 0 ? GetArea(id) : nullptr; +} + + + +const Area * Graph::GetArea(TilePosition t) const +{ + Area::id id = GetMap()->GetTile(t).AreaId(); + return id > 0 ? GetArea(id) : nullptr; +} + + +const vector & Graph::GetChokePoints(Area::id a, Area::id b) const +{ + bwem_assert(Valid(a)); + bwem_assert(Valid(b)); + bwem_assert(a != b); + + if (a > b) swap(a, b); + + return m_ChokePointsMatrix[b][a]; +} + + +void Graph::CreateAreas(const vector> & AreasList) +{ + m_Areas.reserve(AreasList.size()); + for (Area::id id = 1 ; id <= (Area::id)AreasList.size() ; ++id) + { + WalkPosition top = AreasList[id-1].first; + int miniTiles = AreasList[id-1].second; + m_Areas.emplace_back(this, id, top, miniTiles); + } +} + + +void Graph::CreateChokePoints() +{ + ChokePoint::index newIndex = 0; + + vector BlockingNeutrals; + for (auto & s : GetMap()->StaticBuildings()) if (s->Blocking()) BlockingNeutrals.push_back(s.get()); + for (auto & m : GetMap()->Minerals()) if (m->Blocking()) BlockingNeutrals.push_back(m.get()); + + const int pseudoChokePointsToCreate = count_if(BlockingNeutrals.begin(), BlockingNeutrals.end(), + [](const Neutral * n){ return !n->NextStacked(); }); + + // 1) Size the matrix + m_ChokePointsMatrix.resize(AreasCount() + 1); + for (Area::id id = 1 ; id <= AreasCount() ; ++id) + m_ChokePointsMatrix[id].resize(id); // triangular matrix + + // 2) Dispatch the global raw frontier between all the relevant pairs of Areas: + map, vector> RawFrontierByAreaPair; + for (const auto & raw : GetMap()->RawFrontier()) + { + Area::id a = raw.first.first; + Area::id b = raw.first.second; + if (a > b) swap(a, b); + bwem_assert(a <= b); + bwem_assert((a >= 1) && (b <= AreasCount())); + + RawFrontierByAreaPair[make_pair(a, b)].push_back(raw.second); + } + + // 3) For each pair of Areas (A, B): + for (auto & raw : RawFrontierByAreaPair) + { + Area::id a = raw.first.first; + Area::id b = raw.first.second; + + const vector & RawFrontierAB = raw.second; + + // Because our dispatching preserved order, + // and because Map::m_RawFrontier was populated in descending order of the altitude (see Map::ComputeAreas), + // we know that RawFrontierAB is also ordered the same way, but let's check it: + { + vector Altitudes; + for (auto w : RawFrontierAB) + Altitudes.push_back(GetMap()->GetMiniTile(w).Altitude()); + + bwem_assert(is_sorted(Altitudes.rbegin(), Altitudes.rend())); + } + + // 3.1) Use that information to efficiently cluster RawFrontierAB in one or several chokepoints. + // Each cluster will be populated starting with the center of a chokepoint (max altitude) + // and finishing with the ends (min altitude). + const int cluster_min_dist = (int)sqrt(lake_max_miniTiles); + vector> Clusters; + for (auto w : RawFrontierAB) + { + bool added = false; + for (auto & Cluster : Clusters) + { + int distToFront = queenWiseDist(Cluster.front(), w); + int distToBack = queenWiseDist(Cluster.back(), w); + if (min(distToFront, distToBack) <= cluster_min_dist) + { + if (distToFront < distToBack) Cluster.push_front(w); + else Cluster.push_back(w); + + added = true; + break; + } + } + + if (!added) Clusters.push_back(deque(1, w)); + } + + // 3.2) Create one Chokepoint for each cluster: + GetChokePoints(a, b).reserve(Clusters.size() + pseudoChokePointsToCreate); + for (const auto & Cluster : Clusters) + GetChokePoints(a, b).emplace_back(this, newIndex++, GetArea(a), GetArea(b), Cluster); + } + + // 4) Create one Chokepoint for each pair of blocked areas, for each blocking Neutral: + + + for (Neutral * pNeutral : BlockingNeutrals) + if (!pNeutral->NextStacked()) // in the case where several neutrals are stacked, we only consider the top + { + vector BlockedAreas = pNeutral->BlockedAreas(); + for (const Area * pA : BlockedAreas) + for (const Area * pB : BlockedAreas) + { + if (pB == pA) break; // breaks symmetry + + auto center = GetMap()->BreadthFirstSearch(WalkPosition(pNeutral->Pos()), + [](const MiniTile & miniTile, WalkPosition) { return miniTile.Walkable(); }, // findCond + [](const MiniTile &, WalkPosition) { return true; }); // visitCond + + GetChokePoints(pA, pB).reserve(pseudoChokePointsToCreate); + GetChokePoints(pA, pB).emplace_back(this, newIndex++, pA, pB, deque(1, center), pNeutral); + } + } + + // 5) Set the references to the freshly created Chokepoints: + for (Area::id a = 1 ; a <= AreasCount() ; ++a) + for (Area::id b = 1 ; b < a ; ++b) + if (!GetChokePoints(a, b).empty()) + { + GetArea(a)->AddChokePoints(GetArea(b), &GetChokePoints(a, b)); + GetArea(b)->AddChokePoints(GetArea(a), &GetChokePoints(a, b)); + + for (auto & cp : GetChokePoints(a, b)) + m_ChokePointList.push_back(&cp); + } +} + + +void Graph::SetDistance(const ChokePoint * cpA, const ChokePoint * cpB, int value) +{ + m_ChokePointDistanceMatrix[cpA->Index()][cpB->Index()] = + m_ChokePointDistanceMatrix[cpB->Index()][cpA->Index()] = value; +} + + +void Graph::SetPath(const ChokePoint * cpA, const ChokePoint * cpB, const CPPath & PathAB) +{ + m_PathsBetweenChokePoints[cpA->Index()][cpB->Index()] = PathAB; + m_PathsBetweenChokePoints[cpB->Index()][cpA->Index()].assign(PathAB.rbegin(), PathAB.rend()); +} + + +// Computes the ground distances between any pair of ChokePoints in pContext +// This is achieved by invoking several times pContext->ComputeDistances, +// which effectively computes the distances from one starting ChokePoint, using Dijkstra's algorithm. +// If Context == Area, Dijkstra's algorithm works on the Tiles inside one Area. +// If Context == Graph, Dijkstra's algorithm works on the GetChokePoints between the AreaS. +template +void Graph::ComputeChokePointDistances(const Context * pContext) +{ +/// multimap> trace; + + for (const ChokePoint * pStart : pContext->ChokePoints()) + { + vector Targets; + for (const ChokePoint * cp : pContext->ChokePoints()) + { + if (cp == pStart) break; // breaks symmetry + Targets.push_back(cp); + } + + auto DistanceToTargets = pContext->ComputeDistances(pStart, Targets); + + for (int i = 0 ; i < (int)Targets.size() ; ++i) + { + int newDist = DistanceToTargets[i]; + int existingDist = Distance(pStart, Targets[i]); + + if (newDist && ((existingDist == -1) || (newDist < existingDist))) + { + SetDistance(pStart, Targets[i], newDist); + + // Build the path from pStart to Targets[i]: + + CPPath Path {pStart, Targets[i]}; + + // if (Context == Graph), there may be intermediate ChokePoints. They have been set by ComputeDistances, + // so we just have to collect them (in the reverse order) and insert them into Path: + if ((void *)(pContext) == (void *)(this)) // tests (Context == Graph) without warning about constant condition + for (const ChokePoint * pPrev = Targets[i]->PathBackTrace() ; pPrev != pStart ; pPrev = pPrev->PathBackTrace()) + Path.insert(Path.begin()+1, pPrev); + + SetPath(pStart, Targets[i], Path); + + /// vector PathTrace; + /// for (auto e : Path) PathTrace.push_back(e->Center()); + /// trace.emplace(int(0.5 + DistanceToTargets[i]/8.0), PathTrace); + } + } + } + +/// for (auto & line : trace) { Log << line.first; for (auto e : line.second) Log << " " << e; Log << endl; } + +} + +template void Graph::ComputeChokePointDistances(const Graph * pContext); +template void Graph::ComputeChokePointDistances(const Area * pContext); + + +void Graph::ComputeChokePointDistanceMatrix() +{ + // 1) Size the matrix + m_ChokePointDistanceMatrix.clear(); + m_ChokePointDistanceMatrix.resize(m_ChokePointList.size()); + for (auto & line : m_ChokePointDistanceMatrix) + line.resize(m_ChokePointList.size(), -1); + + m_PathsBetweenChokePoints.clear(); + m_PathsBetweenChokePoints.resize(m_ChokePointList.size()); + for (auto & line : m_PathsBetweenChokePoints) + line.resize(m_ChokePointList.size()); + + // 2) Compute distances inside each Area + for (const Area & area : Areas()) + ComputeChokePointDistances(&area); + + // 3) Compute distances through connected Areas + ComputeChokePointDistances(this); + + for (const ChokePoint * cp : ChokePoints()) + { + SetDistance(cp, cp, 0); + SetPath(cp, cp, CPPath{cp}); + } + + // 4) Update Area::m_AccessibleNeighbours for each Area + for (Area & area : Areas()) + area.UpdateAccessibleNeighbours(); + + // 5) Update Area::m_groupId for each Area + UpdateGroupIds(); +} + + +// Returns Distances such that Distances[i] == ground_distance(start, Targets[i]) in pixels +// Any Distances[i] may be 0 (meaning Targets[i] is not reachable). +// This may occur in the case where start and Targets[i] leave in different continents or due to Bloqued intermediate ChokePoint(s). +// For each reached target, the shortest path can be derived using +// the backward trace set in cp->PathBackTrace() for each intermediate ChokePoint cp from the target. +// Note: same algo than Area::ComputeDistances (derived from Dijkstra) +vector Graph::ComputeDistances(const ChokePoint * start, const vector & Targets) const +{ + const MapImpl * pMap = GetMap(); + vector Distances(Targets.size()); + + Tile::UnmarkAll(); + + multimap ToVisit; // a priority queue holding the GetChokePoints to visit ordered by their distance to start. + ToVisit.emplace(0, start); + + int remainingTargets = Targets.size(); + while (!ToVisit.empty()) + { + int currentDist = ToVisit.begin()->first; + const ChokePoint * current = ToVisit.begin()->second; + const Tile & currentTile = pMap->GetTile(TilePosition(current->Center()), check_t::no_check); + bwem_assert(currentTile.InternalData() == currentDist); + ToVisit.erase(ToVisit.begin()); + currentTile.SetInternalData(0); // resets Tile::m_internalData for future usage + currentTile.SetMarked(); + + for (int i = 0 ; i < (int)Targets.size() ; ++i) + if (current == Targets[i]) + { + Distances[i] = currentDist; + --remainingTargets; + } + if (!remainingTargets) break; + + if (current->Blocked() && (current != start)) continue; + + for (const Area * pArea : {current->GetAreas().first, current->GetAreas().second}) + for (const ChokePoint * next : pArea->ChokePoints()) + if (next != current) + { + const int newNextDist = currentDist + Distance(current, next); + const Tile & nextTile = pMap->GetTile(TilePosition(next->Center()), check_t::no_check); + if (!nextTile.Marked()) + { + if (nextTile.InternalData()) // next already in ToVisit + { + if (newNextDist < nextTile.InternalData()) // nextNewDist < nextOldDist + { // To update next's distance, we need to remove-insert it from ToVisit: + auto range = ToVisit.equal_range(nextTile.InternalData()); + auto iNext = find_if(range.first, range.second, [next] + (const pair & e) { return e.second == next; }); + bwem_assert(iNext != range.second); + + ToVisit.erase(iNext); + nextTile.SetInternalData(newNextDist); + next->SetPathBackTrace(current); + ToVisit.emplace(newNextDist, next); + } + } + else + { + nextTile.SetInternalData(newNextDist); + next->SetPathBackTrace(current); + ToVisit.emplace(newNextDist, next); + } + } + } + } + +// bwem_assert(!remainingTargets); + + // Reset Tile::m_internalData for future usage + for (auto e : ToVisit) + pMap->GetTile(TilePosition(e.second->Center()), check_t::no_check).SetInternalData(0); + + return Distances; +} + + +const CPPath & Graph::GetPath(const Position & a, const Position & b, int * pLength) const +{ + const Area * pAreaA = GetNearestArea(WalkPosition(a)); + const Area * pAreaB = GetNearestArea(WalkPosition(b)); + + if (pAreaA == pAreaB) + { + if (pLength) *pLength = a.getApproxDistance(b); + return m_EmptyPath; + }; + + if (!pAreaA->AccessibleFrom(pAreaB)) + { + if (pLength) *pLength = -1; + return m_EmptyPath; + }; + + int minDist_A_B = numeric_limits::max(); + + const ChokePoint * pBestCpA = nullptr; + const ChokePoint * pBestCpB = nullptr; + + for (const ChokePoint * cpA : pAreaA->ChokePoints()) if (!cpA->Blocked()) + { + const int dist_A_cpA = a.getApproxDistance(Position(cpA->Center())); + for (const ChokePoint * cpB : pAreaB->ChokePoints()) if (!cpB->Blocked()) + { + const int dist_B_cpB = b.getApproxDistance(Position(cpB->Center())); + const int dist_A_B = dist_A_cpA + dist_B_cpB + Distance(cpA, cpB); + if (dist_A_B < minDist_A_B) + { + minDist_A_B = dist_A_B; + pBestCpA = cpA; + pBestCpB = cpB; + } + } + } + + bwem_assert(minDist_A_B != numeric_limits::max()); + + const CPPath & Path = GetPath(pBestCpA, pBestCpB); + + if (pLength) + { + bwem_assert(Path.size() >= 1); + + *pLength = minDist_A_B; + + if (Path.size() == 1) + { + bwem_assert(pBestCpA == pBestCpB); + const ChokePoint * cp = pBestCpA; + + Position cpEnd1 = center(cp->Pos(ChokePoint::end1)); + Position cpEnd2 = center(cp->Pos(ChokePoint::end2)); + if (intersect(a.x, a.y, b.x, b.y, cpEnd1.x, cpEnd1.y, cpEnd2.x, cpEnd2.y)) + *pLength = a.getApproxDistance(b); + else + for (ChokePoint::node node : {ChokePoint::end1, ChokePoint::end2}) + { + Position c = center(cp->Pos(node)); + int dist_A_B = a.getApproxDistance(c) + b.getApproxDistance(c); + if (dist_A_B < *pLength) *pLength = dist_A_B; + } + } + } + + return GetPath(pBestCpA, pBestCpB); +} + + +void Graph::UpdateGroupIds() +{ + Area::groupId nextGroupId = 1; + + Area::UnmarkAll(); + for (Area & start : Areas()) + if (!start.Marked()) + { + vector ToVisit{&start}; + while (!ToVisit.empty()) + { + Area * current = ToVisit.back(); + ToVisit.pop_back(); + current->SetGroupId(nextGroupId); + + for (const Area * next : current->AccessibleNeighbours()) + if (!next->Marked()) + { + next->SetMarked(); + ToVisit.push_back(const_cast(next)); + } + } + ++nextGroupId; + } +} + + +void Graph::CollectInformation() +{ + // 1) Process the whole Map: + + for (auto & m : GetMap()->Minerals()) + if (Area * pArea = mainArea(GetMap(), m->TopLeft(), m->Size())) + pArea->AddMineral(m.get()); + + for (auto & g : GetMap()->Geysers()) + if (Area * pArea = mainArea(GetMap(), g->TopLeft(), g->Size())) + pArea->AddGeyser(g.get()); + + for (int y = 0 ; y < GetMap()->Size().y ; ++y) + for (int x = 0 ; x < GetMap()->Size().x ; ++x) + { + const Tile & tile = GetMap()->GetTile(TilePosition(x, y)); + if (tile.AreaId() > 0) + GetArea(tile.AreaId())->AddTileInformation(TilePosition(x, y), tile); + } + + // 2) Post-process each Area separately: + + for (Area & area : m_Areas) + area.PostCollectInformation(); +} + + +void Graph::CreateBases() +{ + m_baseCount = 0; + for (Area & area : m_Areas) + { + area.CreateBases(); + m_baseCount += area.Bases().size(); + } +} + + +}} // namespace BWEM::detail + + + diff --git a/Source/BWEM/src/graph.h b/Source/BWEM/src/graph.h new file mode 100644 index 0000000..8377dfd --- /dev/null +++ b/Source/BWEM/src/graph.h @@ -0,0 +1,157 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_GRAPH_H +#define BWEM_GRAPH_H + +#include +#include "base.h" +#include "cp.h" +#include "area.h" +#include "bwapiExt.h" +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Neutral; +class Mineral; +class Geyser; +class StaticBuilding; +class Tile; + +namespace detail { + +class MapImpl; + +using namespace std; +using namespace utils; +using namespace BWAPI_ext; + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Area +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Graph +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// + +class Graph +{ +public: + Graph(MapImpl * pMap) : m_pMap(pMap) {} + Graph & operator=(const Graph &) = delete; + + const MapImpl * GetMap() const { return m_pMap; } + MapImpl * GetMap() { return m_pMap; } + + const vector & Areas() const { return m_Areas; } + vector & Areas() { return m_Areas; } + int AreasCount() const { return (int)m_Areas.size(); } + + const Area * GetArea(Area::id id) const { bwem_assert(Valid(id)); return &m_Areas[id-1]; } + Area * GetArea(Area::id id) { bwem_assert(Valid(id)); return &m_Areas[id-1]; } + + const Area * GetArea(BWAPI::WalkPosition w) const; + Area * GetArea(BWAPI::WalkPosition w) { return const_cast(static_cast(*this).GetArea(w)); } + + const Area * GetArea(BWAPI::TilePosition t) const; + Area * GetArea(BWAPI::TilePosition t) { return const_cast(static_cast(*this).GetArea(t)); } + + templateconst Area*GetNearestArea(TPosition p) const; + template Area * GetNearestArea(TPosition p) { return const_cast(static_cast(*this).GetNearestArea(p)); } + + + // Returns the list of all the ChokePoints in the Map. + const vector & ChokePoints() const { return m_ChokePointList; } + + // Returns the ChokePoints between two Areas. + const vector & GetChokePoints(Area::id a, Area::id b) const; + const vector & GetChokePoints(const Area * a, const Area * b) const { return GetChokePoints(a->Id(), b->Id()); } + + // Returns the ground distance in pixels between cpA->Center() and cpB>Center() + int Distance(const ChokePoint * cpA, const ChokePoint * cpB) const { return m_ChokePointDistanceMatrix[cpA->Index()][cpB->Index()]; } + + // Returns a list of ChokePoints, which is intended to be the shortest walking path from cpA to cpB. + const CPPath & GetPath(const ChokePoint * cpA, const ChokePoint * cpB) const { return m_PathsBetweenChokePoints[cpA->Index()][cpB->Index()]; } + + const CPPath & GetPath(const BWAPI::Position & a, const BWAPI::Position & b, int * pLength = nullptr) const; + + int BaseCount() const { return m_baseCount; } + + + vector & GetChokePoints(Area::id a, Area::id b) { return const_cast &>(static_cast(*this).GetChokePoints(a, b)); } + vector & GetChokePoints(const Area * a, const Area * b) { return GetChokePoints(a->Id(), b->Id()); } + + // Creates a new Area for each pair (top, miniTiles) in AreasList (See Area::Top() and Area::MiniTiles()) + void CreateAreas(const vector> & AreasList); + + // Creates a new Area for each pair (top, miniTiles) in AreasList (See Area::Top() and Area::MiniTiles()) + void CreateChokePoints(); + + void ComputeChokePointDistanceMatrix(); + + void CollectInformation(); + void CreateBases(); + +private: + template + void ComputeChokePointDistances(const Context * pContext); + vector ComputeDistances(const ChokePoint * pStartCP, const vector & TargetCPs) const; + void SetDistance(const ChokePoint * cpA, const ChokePoint * cpB, int value); + void UpdateGroupIds(); + void SetPath(const ChokePoint * cpA, const ChokePoint * cpB, const CPPath & PathAB); + bool Valid(Area::id id) const { return (1 <= id) && (id <= AreasCount()); } + + MapImpl * const m_pMap; + vector m_Areas; + vector m_ChokePointList; + vector>> m_ChokePointsMatrix; // index == Area::id x Area::id + vector> m_ChokePointDistanceMatrix; // index == ChokePoint::index x ChokePoint::index + vector> m_PathsBetweenChokePoints; // index == ChokePoint::index x ChokePoint::index + const CPPath m_EmptyPath; + int m_baseCount; +}; + + +template +const Area * Graph::GetNearestArea(TPosition p) const +{ + typedef typename TileOfPosition::type Tile_t; + if (const Area * area = GetArea(p)) return area; + + p = GetMap()->BreadthFirstSearch(p, + [this](const Tile_t & t, TPosition) { return t.AreaId() > 0; }, // findCond + [](const Tile_t &, TPosition) { return true; }); // visitCond + + return GetArea(p); +} + + +Area * mainArea(MapImpl * pMap, BWAPI::TilePosition topLeft, BWAPI::TilePosition size); + + +}} // namespace BWEM::detail + + +#endif + diff --git a/Source/BWEM/src/gridMap.cpp b/Source/BWEM/src/gridMap.cpp new file mode 100644 index 0000000..443c458 --- /dev/null +++ b/Source/BWEM/src/gridMap.cpp @@ -0,0 +1,20 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "gridMap.h" + +using namespace std; + +namespace BWEM { +namespace utils { + + +}} // namespace BWEM::utils + diff --git a/Source/BWEM/src/gridMap.h b/Source/BWEM/src/gridMap.h new file mode 100644 index 0000000..a1fb504 --- /dev/null +++ b/Source/BWEM/src/gridMap.h @@ -0,0 +1,117 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_GRID_MAP_H +#define BWEM_GRID_MAP_H + +#include +#include +#include "map.h" +#include "utils.h" +#include "defs.h" + + +namespace BWEM +{ + +class Map; + + +namespace utils { + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class GridMap +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// A basic and generic "grid map" class that works well with the BWEM Library. +// The grid is composed of cells whose type T is user defined. +// Each cell matches a square of N*N tiles. +// The idea is that all the data stored in a cell can be accessed in O(1). +// +// Choose N high enough to efficiently divide the space of the Map. +// Choose N low enough to efficiently performs operations inside each Cell. +// +// You can create any number of GridMap instances, with the same or distinct values for N. +// Keep in mind that even if well designed, a GridMap (as any container) cannot fit all your needs generally. +// +// A typical use case would be to store the BWAPI units in a GridMap. +// See SimpleGridMap in example.h +// + +template +class GridMap +{ +public: + typedef T Cell; + enum {cell_width_in_tiles = N}; + enum {tiles_per_cell = cell_width_in_tiles * cell_width_in_tiles}; + + GridMap(const Map * pMap); + + // Returns the size of the GridMap in Cell number. + int Width() const { return m_width; } + int Height() const { return m_height; } + + // Returns a Cell, given its coordinates + const Cell & GetCell(int i, int j, check_t checkMode = check_t::check) const { bwem_assert((checkMode == check_t::no_check) || ValidCoords(i, j)); utils::unused(checkMode); return m_Cells[m_width * j + i]; } + Cell & GetCell(int i, int j, check_t checkMode = check_t::check) { bwem_assert((checkMode == check_t::no_check) || ValidCoords(i, j)); utils::unused(checkMode); return m_Cells[m_width * j + i]; } + + // Returns the Cell thats contains the Tile t + const Cell & GetCell(const BWAPI::TilePosition & t, check_t checkMode = check_t::check) const{ bwem_assert((checkMode == check_t::no_check) || m_pMap->Valid(t)); utils::unused(checkMode); return GetCell(t.x/N, t.y/N, check_t::no_check); } + Cell & GetCell(const BWAPI::TilePosition & t, check_t checkMode = check_t::check) { bwem_assert((checkMode == check_t::no_check) || m_pMap->Valid(t)); utils::unused(checkMode); return GetCell(t.x/N, t.y/N, check_t::no_check); } + + // Returns the coordinates of the Cell thats contains the Tile t + std::pair GetCellCoords(const BWAPI::TilePosition & t, check_t checkMode = check_t::check) const { bwem_assert((checkMode == check_t::no_check) || m_pMap->Valid(t)); utils::unused(checkMode); return std::make_pair(t.x/N, t.y/N); } + + // Returns specific tiles of a Cell, given its coordinates. + BWAPI::TilePosition GetTopLeft(int i, int j, check_t checkMode = check_t::check) const { bwem_assert((checkMode == check_t::no_check) || ValidCoords(i, j)); utils::unused(checkMode); return BWAPI::TilePosition(i*N, j*N); } + BWAPI::TilePosition GetBottomRight(int i, int j, check_t checkMode = check_t::check) const { bwem_assert((checkMode == check_t::no_check) || ValidCoords(i, j)); utils::unused(checkMode); using namespace BWAPI_ext; return BWAPI::TilePosition((i+1)*N, (j+1)*N) - 1; } + BWAPI::TilePosition GetCenter(int i, int j, check_t checkMode = check_t::check) const { bwem_assert((checkMode == check_t::no_check) || ValidCoords(i, j)); utils::unused(checkMode); using namespace BWAPI_ext; return BWAPI::TilePosition(i*N, j*N) + N/2; } + + // Provides access to the internal array of Cells. + const std::vector & Cells() const { return m_Cells; } + + // Returns whether the coordinates (i, j) is valid. + bool ValidCoords(int i, int j) const { return (0 <= i) && (i < Width()) && (0 <= j) && (j < Height()); } + +private: + const Map * m_pMap; + + int m_width; + int m_height; + std::vector m_Cells; +}; + + +template +GridMap::GridMap(const Map * pMap) +: m_pMap(pMap), + m_width(pMap->Size().x / N), + m_height(pMap->Size().y / N), + m_Cells(m_width * m_height) +{ + static_assert(N > 0, "GridMap::cell_width_in_tiles must be > 0"); + bwem_assert_throw(pMap->Initialized()); + bwem_assert_throw(N <= std::min(pMap->Size().x, pMap->Size().y)); + bwem_assert_throw(pMap->Size().x % N == 0); + bwem_assert_throw(pMap->Size().y % N == 0); + + +} + + +}} // namespace BWEM::utils + + +#endif + diff --git a/Source/BWEM/src/map.cpp b/Source/BWEM/src/map.cpp new file mode 100644 index 0000000..7298da8 --- /dev/null +++ b/Source/BWEM/src/map.cpp @@ -0,0 +1,104 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "map.h" +#include "mapImpl.h" +#include "bwapiExt.h" + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + +namespace utils { + +bool seaSide(WalkPosition p, const Map * pMap) +{ + if (!pMap->GetMiniTile(p).Sea()) return false; + + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + if (pMap->Valid(p + delta)) + if (!pMap->GetMiniTile(p + delta, check_t::no_check).Sea()) + return true; + + return false; +} + +} // namespace utils + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Map +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +unique_ptr Map::m_gInstance = nullptr; + + +Map & Map::Instance() +{ + if (!m_gInstance) m_gInstance = make_unique(); + + return *m_gInstance.get(); +} + + +Position Map::RandomPosition() const +{ + const auto PixelSize = Position(Size()); + return Position(rand() % PixelSize.x, rand() % PixelSize.y); +} + + +template +TPosition crop(const TPosition & p, int siseX, int sizeY) +{ + TPosition res = p; + + if (res.x < 0) res.x = 0; + else if (res.x >= siseX) res.x = siseX-1; + + if (res.y < 0) res.y = 0; + else if (res.y >= sizeY) res.y = sizeY-1; + + return res; +} + + +TilePosition Map::Crop(const TilePosition & p) const +{ + return crop(p, Size().x, Size().y); +} + + +WalkPosition Map::Crop(const WalkPosition & p) const +{ + return crop(p, WalkSize().x, WalkSize().y); +} + + +Position Map::Crop(const Position & p) const +{ + return crop(p, 32*Size().x, 32*Size().y); +} + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/map.h b/Source/BWEM/src/map.h new file mode 100644 index 0000000..5f41e21 --- /dev/null +++ b/Source/BWEM/src/map.h @@ -0,0 +1,311 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_MAP_H +#define BWEM_MAP_H + +#include +#include +#include +#include +#include "tiles.h" +#include "area.h" +#include "cp.h" +#include "utils.h" +#include "defs.h" + + +namespace BWEM +{ + +class Tile; +class MiniTile; +class Area; +class Mineral; +class Geyser; +class StaticBuilding; +class ChokePoint; + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Map +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Map is the entry point: +// - to access general information on the Map +// - to access the Tiles and the MiniTiles +// - to access the Areas +// - to access the StartingLocations +// - to access the Minerals, the Geysers and the StaticBuildings +// - to parametrize the analysis process +// - to update the information +// Map also provides some useful tools such as Paths between ChokePoints and generic algorithms like BreadthFirstSearch +// +// Map functionnality is provided through its singleton Map::Instance(). + +class Map +{ +public: + // Returns the unique instance (singleton). + // It is equal to use Map::Instance() each time, or to store the returned reference and use it instead. + static Map & Instance(); + + + // This has to be called before any other function is called. + // A good place to do this is in ExampleAIModule::onStart() + virtual void Initialize() = 0; + + // Will return true once Initialize() has been called. + bool Initialized() const { return m_size != 0; } + + // Returns the status of the automatic path update (off (false) by default). + // When on, each time a blocking Neutral (either Mineral or StaticBuilding) is destroyed, + // any information relative to the paths through the Areas is updated accordingly. + // For this to function, the Map still needs to be informed of such destructions + // (by calling OnMineralDestroyed and OnStaticBuildingDestroyed). + virtual bool AutomaticPathUpdate() const = 0; + + // Enables the automatic path update (Cf. AutomaticPathUpdate()). + // One might NOT want to call this function, in order to make the accessibility between Areas remain the same throughout the game. + // Even in this case, one should keep calling OnMineralDestroyed and OnStaticBuildingDestroyed. + virtual void EnableAutomaticPathAnalysis() const = 0; + + // Tries to assign one Base for each starting Location in StartingLocations(). + // Only nearby Bases can be assigned (Cf. detail::max_tiles_between_StartingLocation_and_its_AssignedBase). + // Each such assigned Base then has Starting() == true, and its Location() is updated. + // Returns whether the function succeeded (a fail may indicate a failure in BWEM's Base placement analysis + // or a suboptimal placement in one of the starting Locations). + // You normally should call this function, unless you want to compare the StartingLocations() with + // BWEM's suggested locations for the Bases. + virtual bool FindBasesForStartingLocations() = 0; + + // Returns the size of the Map in Tiles. + const BWAPI::TilePosition & Size() const { return m_Size; } + + // Returns the size of the Map in MiniTiles. + const BWAPI::WalkPosition & WalkSize() const { return m_WalkSize; } + + // Returns the center of the Map in pixels. + const BWAPI::Position & Center() const { return m_center; } + + // Returns a random position in the Map in pixels. + BWAPI::Position RandomPosition() const; + + // Returns the maximum altitude in the whole Map (Cf. MiniTile::Altitude()). + virtual altitude_t MaxAltitude() const = 0; + + // Returns the number of Bases. + virtual int BaseCount() const = 0; + + // Returns the number of ChokePoints. + virtual int ChokePointCount() const = 0; + + // Returns a Tile, given its position. + const Tile & GetTile(const BWAPI::TilePosition & p, utils::check_t checkMode = utils::check_t::check) const { bwem_assert((checkMode == utils::check_t::no_check) || Valid(p)); utils::unused(checkMode); return m_Tiles[Size().x * p.y + p.x]; } + + // Returns a MiniTile, given its position. + const MiniTile & GetMiniTile(const BWAPI::WalkPosition & p, utils::check_t checkMode = utils::check_t::check) const { bwem_assert((checkMode == utils::check_t::no_check) || Valid(p)); utils::unused(checkMode); return m_MiniTiles[WalkSize().x * p.y + p.x]; } + + // Returns a Tile or a MiniTile, given its position. + // Provided as a support of generic algorithms. + template + const typename utils::TileOfPosition::type & GetTTile(const TPosition & p, utils::check_t checkMode = utils::check_t::check) const; + + // Provides access to the internal array of Tiles. + const std::vector & Tiles() const { return m_Tiles; } + + // Provides access to the internal array of MiniTiles. + const std::vector & MiniTiles() const { return m_MiniTiles; } + + // Returns whether the position p is valid. + bool Valid(const BWAPI::TilePosition & p) const { return (0 <= p.x) && (p.x < Size().x) && (0 <= p.y) && (p.y < Size().y); } + bool Valid(const BWAPI::WalkPosition & p) const { return (0 <= p.x) && (p.x < WalkSize().x) && (0 <= p.y) && (p.y < WalkSize().y); } + bool Valid(const BWAPI::Position & p) const { return Valid(BWAPI::WalkPosition(p)); } + + // Returns the position closest to p that is valid. + BWAPI::WalkPosition Crop(const BWAPI::WalkPosition & p) const; + BWAPI::TilePosition Crop(const BWAPI::TilePosition & p) const; + BWAPI::Position Crop(const BWAPI::Position & p) const; + + // Returns a reference to the starting Locations. + // Note: these correspond to BWAPI::getStartLocations(). + virtual const std::vector & StartingLocations() const = 0; + + // Returns a reference to the Minerals (Cf. Mineral). + virtual const std::vector> & Minerals() const = 0; + + // Returns a reference to the Geysers (Cf. Geyser). + virtual const std::vector> & Geysers() const = 0; + + // Returns a reference to the StaticBuildings (Cf. StaticBuilding). + virtual const std::vector> & StaticBuildings() const = 0; + + // If a Mineral wrappers the given BWAPI unit, returns a pointer to it. + // Otherwise, returns nullptr. + virtual Mineral * GetMineral(BWAPI::Unit u) const = 0; + + // If a Geyser wrappers the given BWAPI unit, returns a pointer to it. + // Otherwise, returns nullptr. + virtual Geyser * GetGeyser(BWAPI::Unit g) const = 0; + + // Should be called for each destroyed BWAPI unit u having u->getType().isMineralField() == true + virtual void OnMineralDestroyed(BWAPI::Unit u) = 0; + + // Should be called for each destroyed BWAPI unit u having u->getType().isSpecialBuilding() == true + virtual void OnStaticBuildingDestroyed(BWAPI::Unit u) = 0; + + // Returns a reference to the Areas. + virtual const std::vector & Areas() const = 0; + + // Returns an Area given its id. + virtual const Area * GetArea(Area::id id) const = 0; + + // If the MiniTile at w is walkable and is part of an Area, returns that Area. + // Otherwise, returns nullptr; + // Note: because of the lakes, GetNearestArea should be prefered over GetArea. + virtual const Area * GetArea(BWAPI::WalkPosition w) const = 0; + + // If the Tile at t contains walkable sub-MiniTiles which are all part of the same Area, returns that Area. + // Otherwise, returns nullptr; + // Note: because of the lakes, GetNearestArea should be prefered over GetArea. + virtual const Area * GetArea(BWAPI::TilePosition t) const = 0; + + // Returns the nearest Area from w. + // Returns nullptr only if Areas().empty() + // Note: Uses a breadth first search. + virtual const Area * GetNearestArea(BWAPI::WalkPosition w) const = 0; + + // Returns the nearest Area from t. + // Returns nullptr only if Areas().empty() + // Note: Uses a breadth first search. + virtual const Area * GetNearestArea(BWAPI::TilePosition t) const = 0; + + + // Returns a list of ChokePoints, which is intended to be the shortest walking path from 'a' to 'b'. + // Furthermore, if pLength != nullptr, the pointed integer is set to the corresponding length in pixels. + // If 'a' is not accessible from 'b', the empty Path is returned, and -1 is put in *pLength (if pLength != nullptr). + // If 'a' and 'b' are in the same Area, the empty Path is returned, and a.getApproxDistance(b) is put in *pLength (if pLength != nullptr). + // Otherwise, the function relies on ChokePoint::GetPathTo. + // Cf. ChokePoint::GetPathTo for more information. + // Note: in order to retrieve the Areas of 'a' and 'b', the function starts by calling + // GetNearestArea(TilePosition(a)) and GetNearestArea(TilePosition(b)). + // While this brings robustness, this could yield surprising results in the case where 'a' and/or 'b' are in the Water. + // To avoid this and the potential performance penalty, just make sure GetArea(a) != nullptr and GetArea(b) != nullptr. + // Then GetPath should perform very quick. + virtual const CPPath & GetPath(const BWAPI::Position & a, const BWAPI::Position & b, int * pLength = nullptr) const = 0; + + // Generic algorithm for breadth first search in the Map. + // See the several use cases in BWEM source files. + template + TPosition BreadthFirstSearch(TPosition start, Pred1 findCond, Pred2 visitCond, bool connect8 = true) const; + + + // Returns the union of the geometry of all the ChokePoints. Cf. ChokePoint::Geometry() + virtual const std::vector, BWAPI::WalkPosition>> & RawFrontier() const = 0; + + virtual ~Map() = default; + +protected: + Map() = default; + + Tile & GetTile_(const BWAPI::TilePosition & p, utils::check_t checkMode = utils::check_t::check) { return const_cast(static_cast(*this).GetTile(p, checkMode)); } + MiniTile & GetMiniTile_(const BWAPI::WalkPosition & p, utils::check_t checkMode = utils::check_t::check) { return const_cast(static_cast(*this).GetMiniTile(p, checkMode)); } + + int m_size = 0; + BWAPI::TilePosition m_Size; + + int m_walkSize; + BWAPI::WalkPosition m_WalkSize; + + BWAPI::Position m_center; + std::vector m_Tiles; + std::vector m_MiniTiles; + +private: + static std::unique_ptr m_gInstance; + +}; + + +template<> +inline const Tile & Map::GetTTile(const BWAPI::TilePosition & t, utils::check_t checkMode) const +{ + return GetTile(t, checkMode); +} + +template<> +inline const MiniTile & Map::GetTTile(const BWAPI::WalkPosition & w, utils::check_t checkMode) const +{ + return GetMiniTile(w, checkMode); +} + + +template +inline TPosition Map::BreadthFirstSearch(TPosition start, Pred1 findCond, Pred2 visitCond, bool connect8) const +{ + typedef typename utils::TileOfPosition::type Tile_t; + if (findCond(GetTTile(start), start)) return start; + + std::vector Visited; + std::queue ToVisit; + + ToVisit.push(start); + Visited.push_back(start); + + auto dir8 = { TPosition(-1, -1), TPosition(0, -1), TPosition(+1, -1), + TPosition(-1, 0), TPosition(+1, 0), + TPosition(-1, +1), TPosition(0, +1), TPosition(+1, +1)}; + + auto dir4 = { TPosition(0, -1), TPosition(-1, 0), TPosition(+1, 0), TPosition(0, +1)}; + + auto directions = connect8 ? dir8 : dir4; + + while (!ToVisit.empty()) + { + TPosition current = ToVisit.front(); + ToVisit.pop(); + for (TPosition delta : directions) + { + TPosition next = current + delta; + if (Valid(next)) + { + const Tile_t & Next = GetTTile(next, utils::check_t::no_check); + if (findCond(Next, next)) return next; + + if (visitCond(Next, next) && !utils::contains(Visited, next)) + { + ToVisit.push(next); + Visited.push_back(next); + } + } + } + } + + bwem_assert(false); + return start; +} + + + +namespace utils +{ + +bool seaSide(BWAPI::WalkPosition p, const Map * pMap); + +} // namespace utils + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/mapDrawer.cpp b/Source/BWEM/src/mapDrawer.cpp new file mode 100644 index 0000000..7aede00 --- /dev/null +++ b/Source/BWEM/src/mapDrawer.cpp @@ -0,0 +1,102 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "mapDrawer.h" +#include "map.h" + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace BWAPI_ext; + +namespace utils { + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapDrawer +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +bool MapDrawer::showSeas = true; +bool MapDrawer::showLakes = true; +bool MapDrawer::showUnbuildable = true; +bool MapDrawer::showGroundHeight = true; +bool MapDrawer::showMinerals = true; +bool MapDrawer::showGeysers = true; +bool MapDrawer::showStaticBuildings = true; +bool MapDrawer::showBases = true; +bool MapDrawer::showAssignedRessources = true; +bool MapDrawer::showFrontier = true; +bool MapDrawer::showCP = true; + +const Color MapDrawer::Color::sea = BWAPI::Colors::Blue; +const Color MapDrawer::Color::lakes = BWAPI::Colors::Blue; +const Color MapDrawer::Color::unbuildable = BWAPI::Colors::Brown; +const Color MapDrawer::Color::highGround = BWAPI::Colors::Brown; +const Color MapDrawer::Color::veryHighGround = BWAPI::Colors::Red; +const Color MapDrawer::Color::minerals = BWAPI::Colors::Cyan; +const Color MapDrawer::Color::geysers = BWAPI::Colors::Green; +const Color MapDrawer::Color::staticBuildings = BWAPI::Colors::Purple; +const Color MapDrawer::Color::bases = BWAPI::Colors::Blue; +const Color MapDrawer::Color::assignedRessources = BWAPI::Colors::Blue; +const Color MapDrawer::Color::frontier = BWAPI::Colors::Grey; +const Color MapDrawer::Color::cp = BWAPI::Colors::White; + + +bool MapDrawer::ProcessCommandVariants(const string & command, const string & attributName, bool & attribut) +{ + if (command == "show " + attributName) { attribut = true; return true; } + if (command == "hide " + attributName) { attribut = false; return true; } + if (command == attributName) { attribut = !attribut; return true; } + return false; +} + + +bool MapDrawer::ProcessCommand(const string & command) +{ + if (ProcessCommandVariants(command, "seas", showSeas)) return true; + if (ProcessCommandVariants(command, "lakes", showLakes)) return true; + if (ProcessCommandVariants(command, "unbuildable", showUnbuildable)) return true; + if (ProcessCommandVariants(command, "gh", showGroundHeight)) return true; + if (ProcessCommandVariants(command, "minerals", showMinerals)) return true; + if (ProcessCommandVariants(command, "geysers", showGeysers)) return true; + if (ProcessCommandVariants(command, "static buildings", showStaticBuildings)) return true; + if (ProcessCommandVariants(command, "bases", showBases)) return true; + if (ProcessCommandVariants(command, "assigned ressources", showAssignedRessources)) return true; + if (ProcessCommandVariants(command, "frontier", showFrontier)) return true; + if (ProcessCommandVariants(command, "cp", showCP)) return true; + + if (ProcessCommandVariants(command, "all", showSeas)) + if (ProcessCommandVariants(command, "all", showLakes)) + if (ProcessCommandVariants(command, "all", showUnbuildable)) + if (ProcessCommandVariants(command, "all", showGroundHeight)) + if (ProcessCommandVariants(command, "all", showMinerals)) + if (ProcessCommandVariants(command, "all", showGeysers)) + if (ProcessCommandVariants(command, "all", showStaticBuildings)) + if (ProcessCommandVariants(command, "all", showBases)) + if (ProcessCommandVariants(command, "all", showAssignedRessources)) + if (ProcessCommandVariants(command, "all", showFrontier)) + if (ProcessCommandVariants(command, "all", showCP)) + return true; + + return false; +} + +}} // namespace BWEM::utils + + + diff --git a/Source/BWEM/src/mapDrawer.h b/Source/BWEM/src/mapDrawer.h new file mode 100644 index 0000000..72437ac --- /dev/null +++ b/Source/BWEM/src/mapDrawer.h @@ -0,0 +1,78 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_MAP_DRAWER_H +#define BWEM_MAP_DRAWER_H + +#include +#include +#include "utils.h" +#include "defs.h" + + +class BMP; +namespace BWEM { +class Map; +namespace utils { + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapDrawer +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// + + +class MapDrawer +{ +public: + + static bool ProcessCommand(const std::string & command); + + static bool showSeas; + static bool showLakes; + static bool showUnbuildable; + static bool showGroundHeight; + static bool showMinerals; + static bool showGeysers; + static bool showStaticBuildings; + static bool showBases; + static bool showAssignedRessources; + static bool showFrontier; + static bool showCP; + + struct Color + { + static const BWAPI::Color sea; + static const BWAPI::Color lakes; + static const BWAPI::Color unbuildable; + static const BWAPI::Color highGround; + static const BWAPI::Color veryHighGround; + static const BWAPI::Color minerals; + static const BWAPI::Color geysers; + static const BWAPI::Color staticBuildings; + static const BWAPI::Color bases; + static const BWAPI::Color assignedRessources; + static const BWAPI::Color frontier; + static const BWAPI::Color cp; + }; + +private: + static bool ProcessCommandVariants(const std::string & command, const std::string & attributName, bool & attribut); +}; + + +}} // namespace BWEM::detail::detail + + +#endif + diff --git a/Source/BWEM/src/mapImpl.cpp b/Source/BWEM/src/mapImpl.cpp new file mode 100644 index 0000000..a40693e --- /dev/null +++ b/Source/BWEM/src/mapImpl.cpp @@ -0,0 +1,775 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "mapImpl.h" +#include "neutral.h" +#include "bwapiExt.h" +#include "winutils.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { +namespace detail { + + +static bool adjoins8SomeLakeOrNeutral(WalkPosition p, const MapImpl * pMap) +{ + for (WalkPosition delta : { WalkPosition(-1, -1), WalkPosition(0, -1), WalkPosition(+1, -1), + WalkPosition(-1, 0), WalkPosition(+1, 0), + WalkPosition(-1, +1), WalkPosition(0, +1), WalkPosition(+1, +1)}) + { + WalkPosition next = p + delta; + if (pMap->Valid(next)) + { + if (pMap->GetTile(TilePosition(next), check_t::no_check).GetNeutral()) return true; + if (pMap->GetMiniTile(next, check_t::no_check).Lake()) return true; + } + } + + return false; +} + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapImpl +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +MapImpl::MapImpl() +: m_Graph(this) +{ + +} + + +MapImpl::~MapImpl() +{ + m_automaticPathUpdate = false; // now there is no need to update the paths +} + + +void MapImpl::Initialize() +{ + this->~MapImpl(); + new (this) MapImpl(); + +/// Timer overallTimer; +/// Timer timer; + + m_Size = TilePosition(bw->mapWidth(), bw->mapHeight()); + m_size = Size().x * Size().y; + m_Tiles.resize(m_size); + + m_WalkSize = WalkPosition(Size()); + m_walkSize = WalkSize().x * WalkSize().y; + m_MiniTiles.resize(m_walkSize); + + m_center = Position(Size())/2; + + for (TilePosition t : bw->getStartLocations()) + m_StartingLocations.push_back(t); + +/// bw << "Map::Initialize-resize: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + LoadData(); +/// bw << "Map::LoadData: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + DecideSeasOrLakes(); +/// bw << "Map::DecideSeasOrLakes: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + InitializeNeutrals(); +/// bw << "Map::InitializeNeutrals: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + ComputeAltitude(); +/// bw << "Map::ComputeAltitude: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + ProcessBlockingNeutrals(); +/// bw << "Map::ProcessBlockingNeutrals: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + ComputeAreas(); +/// bw << "Map::ComputeAreas: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + GetGraph().CreateChokePoints(); +/// bw << "Graph::CreateChokePoints: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + GetGraph().ComputeChokePointDistanceMatrix(); +/// bw << "Graph::ComputeChokePointDistanceMatrix: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + GetGraph().CollectInformation(); +/// bw << "Graph::CollectInformation: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + + GetGraph().CreateBases(); +/// bw << "Graph::CreateBases: " << timer.ElapsedMilliseconds() << " ms" << endl; timer.Reset(); + +/// bw << "Map::Initialize: " << overallTimer.ElapsedMilliseconds() << " ms" << endl; +} + + +// Computes walkability, buildability and groundHeight and doodad information, using BWAPI corresponding functions +void MapImpl::LoadData() +{ + // Mark unwalkable minitiles (minitiles are walkable by default) + for (int y = 0 ; y < WalkSize().y ; ++y) + for (int x = 0 ; x < WalkSize().x ; ++x) + if (!bw->isWalkable(x, y)) // For each unwalkable minitile, we also mark its 8 neighbours as not walkable. + for (int dy = -1 ; dy <= +1 ; ++dy) // According to some tests, this prevents from wrongly pretending one Marine can go by some thin path. + for (int dx = -1 ; dx <= +1 ; ++dx) + { + WalkPosition w(x+dx, y+dy); + if (Valid(w)) + GetMiniTile_(w, check_t::no_check).SetWalkable(false); + } + + // Mark buildable tiles (tiles are unbuildable by default) + for (int y = 0 ; y < Size().y ; ++y) + for (int x = 0 ; x < Size().x ; ++x) + { + TilePosition t(x, y); + if (bw->isBuildable(t)) + { + GetTile_(t).SetBuildable(); + + // Ensures buildable ==> walkable: + for (int dy = 0 ; dy < 4 ; ++dy) + for (int dx = 0 ; dx < 4 ; ++dx) + GetMiniTile_(WalkPosition(t) + WalkPosition(dx, dy), check_t::no_check).SetWalkable(true); + } + + // Add groundHeight and doodad information: + int bwapiGroundHeight = bw->getGroundHeight(t); + GetTile_(t).SetGroundHeight(bwapiGroundHeight / 2); + if (bwapiGroundHeight % 2) + GetTile_(t).SetDoodad(); + } +} + + +void MapImpl::DecideSeasOrLakes() +{ + for (int y = 0 ; y < WalkSize().y ; ++y) + for (int x = 0 ; x < WalkSize().x ; ++x) + { + WalkPosition origin = WalkPosition(x, y); + MiniTile & Origin = GetMiniTile_(origin, check_t::no_check); + if (Origin.SeaOrLake()) + { + vector ToSearch{origin}; + vector SeaExtent{&Origin}; + Origin.SetSea(); + WalkPosition topLeft = origin; + WalkPosition bottomRight = origin; + while (!ToSearch.empty()) + { + WalkPosition current = ToSearch.back(); + if (current.x < topLeft.x) topLeft.x = current.x; + if (current.y < topLeft.y) topLeft.y = current.y; + if (current.x > bottomRight.x) bottomRight.x = current.x; + if (current.y > bottomRight.y) bottomRight.y = current.y; + + ToSearch.pop_back(); + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + { + WalkPosition next = current + delta; + if (Valid(next)) + { + MiniTile & Next = GetMiniTile_(next, check_t::no_check); + if (Next.SeaOrLake()) + { + ToSearch.push_back(next); + if (SeaExtent.size() <= lake_max_miniTiles) SeaExtent.push_back(&Next); + Next.SetSea(); + } + } + } + } + + if ((SeaExtent.size() <= lake_max_miniTiles) && + (bottomRight.x - topLeft.x <= lake_max_width_in_miniTiles) && + (bottomRight.y - topLeft.y <= lake_max_width_in_miniTiles) && + (topLeft.x >= 2) && (topLeft.y >= 2) && (bottomRight.x < WalkSize().x-2) && (bottomRight.y < WalkSize().y-2)) + for (MiniTile * pSea : SeaExtent) + pSea->SetLake(); + } + } +} + + +void MapImpl::InitializeNeutrals() +{ + for (auto n : bw->getStaticNeutralUnits()) + if (n->getType().isBuilding()) + { + if (n->getType().isMineralField()) + { + m_Minerals.push_back(make_unique(n, this)); + } + else if (n->getType() == Resource_Vespene_Geyser) + { + m_Geysers.push_back(make_unique(n, this)); + } + else + { + bwem_assert_throw(n->getType().isSpecialBuilding()); + m_StaticBuildings.push_back(make_unique(n, this)); + } + } + else if (n->getType() != Zerg_Egg) + if (!n->getType().isCritter()) + { + bwem_assert_plus(!n->getType().isSpecialBuilding(), n->getType().getName()); + + // Log << n->getType().getName() << endl; + + bwem_assert_plus( + n->getType() == Special_Pit_Door || + n->getType() == Special_Right_Pit_Door || + false, n->getType().getName()); + + if (n->getType() == Special_Pit_Door) + m_StaticBuildings.push_back(make_unique(n, this)); + if (n->getType() == Special_Right_Pit_Door) + m_StaticBuildings.push_back(make_unique(n, this)); + } +} + + +void MapImpl::ReplaceAreaIds(BWAPI::WalkPosition p, Area::id newAreaId) +{ + MiniTile & Origin = GetMiniTile_(p, check_t::no_check); + Area::id oldAreaId = Origin.AreaId(); + Origin.ReplaceAreaId(newAreaId); + + vector ToSearch{p}; + while (!ToSearch.empty()) + { + WalkPosition current = ToSearch.back(); + + ToSearch.pop_back(); + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + { + WalkPosition next = current + delta; + if (Valid(next)) + { + MiniTile & Next = GetMiniTile_(next, check_t::no_check); + if (Next.AreaId() == oldAreaId) + { + ToSearch.push_back(next); + Next.ReplaceAreaId(newAreaId); + } + } + } + } + + // also replaces references of oldAreaId by newAreaId in m_RawFrontier: + if (newAreaId > 0) + for (auto & f : m_RawFrontier) + { + if (f.first.first == oldAreaId) f.first.first = newAreaId; + if (f.first.second == oldAreaId) f.first.second = newAreaId; + } +} + + +// Assigns MiniTile::m_altitude foar each miniTile having AltitudeMissing() +// Cf. MiniTile::Altitude() for meaning of altitude_t. +// Altitudes are computed using the straightforward Dijkstra's algorithm : the lower ones are computed first, starting from the seaside-miniTiles neighbours. +// The point here is to precompute all possible altitudes for all possible tiles, and sort them. +void MapImpl::ComputeAltitude() +{ + const int altitude_scale = 8; // 8 provides a pixel definition for altitude_t, since altitudes are computed from miniTiles which are 8x8 pixels + + // 1) Fill in and sort DeltasByAscendingAltitude + const int range = max(WalkSize().x, WalkSize().y) / 2 + 3; // should suffice for maps with no Sea. + + vector> DeltasByAscendingAltitude; + + for (int dy = 0 ; dy <= range ; ++dy) + for (int dx = dy ; dx <= range ; ++dx) // Only consider 1/8 of possible deltas. Other ones obtained by symmetry. + if (dx || dy) + DeltasByAscendingAltitude.emplace_back(WalkPosition(dx, dy), altitude_t(0.5 + norm(dx, dy) * altitude_scale)); + + sort(DeltasByAscendingAltitude.begin(), DeltasByAscendingAltitude.end(), + [](const pair & a, const pair & b){ return a.second < b.second; }); + + + // 2) Fill in ActiveSeaSideList, which basically contains all the seaside miniTiles (from which altitudes are to be computed) + // It also includes extra border-miniTiles which are considered as seaside miniTiles too. + struct ActiveSeaSide { WalkPosition origin; altitude_t lastAltitudeGenerated; }; + vector ActiveSeaSideList; + + for (int y = -1 ; y <= WalkSize().y ; ++y) + for (int x = -1 ; x <= WalkSize().x ; ++x) + { + WalkPosition w(x, y); + if (!Valid(w) || seaSide(w, this)) + ActiveSeaSideList.push_back(ActiveSeaSide{w, 0}); + } + + // 3) Dijkstra's algorithm + for (const auto & delta_altitude : DeltasByAscendingAltitude) + { + const WalkPosition d = delta_altitude.first; + const altitude_t altitude = delta_altitude.second; + for (int i = 0 ; i < (int)ActiveSeaSideList.size() ; ++i) + { + ActiveSeaSide & Current = ActiveSeaSideList[i]; + if (altitude - Current.lastAltitudeGenerated >= 2 * altitude_scale) // optimization : once a seaside miniTile verifies this condition, + fast_erase(ActiveSeaSideList, i--); // we can throw it away as it will not generate min altitudes anymore + else + for (auto delta : { WalkPosition(d.x, d.y), WalkPosition(-d.x, d.y), WalkPosition(d.x, -d.y), WalkPosition(-d.x, -d.y), + WalkPosition(d.y, d.x), WalkPosition(-d.y, d.x), WalkPosition(d.y, -d.x), WalkPosition(-d.y, -d.x)}) + { + WalkPosition w = Current.origin + delta; + if (Valid(w)) + { + auto & miniTile = GetMiniTile_(w, check_t::no_check); + if (miniTile.AltitudeMissing()) + miniTile.SetAltitude(m_maxAltitude = Current.lastAltitudeGenerated = altitude); + } + } + } + } +} + + +void MapImpl::ProcessBlockingNeutrals() +{ + vector Candidates; + for (auto & s : StaticBuildings()) Candidates.push_back(s.get()); + for (auto & m : Minerals()) Candidates.push_back(m.get()); + + for (Neutral * pCandidate : Candidates) + if (!pCandidate->NextStacked()) // in the case where several neutrals are stacked, we only consider the top one + { + // 1) Retreave the Border: the outer border of pCandidate + vector Border = outerMiniTileBorder(pCandidate->TopLeft(), pCandidate->Size()); + really_remove_if(Border, [this](WalkPosition w) { + return !Valid(w) || !GetMiniTile(w, check_t::no_check).Walkable() || + GetTile(TilePosition(w), check_t::no_check).GetNeutral(); }); + + // 2) Find the doors in Border: one door for each connected set of walkable, neighbouring miniTiles. + // The searched connected miniTiles all have to be next to some lake or some static building, though they can't be part of one. + vector Doors; + while (!Border.empty()) + { + WalkPosition door = Border.back(); Border.pop_back(); + Doors.push_back(door); + vector ToVisit(1, door); + vector Visited(1, door); + while (!ToVisit.empty()) + { + WalkPosition current = ToVisit.back(); ToVisit.pop_back(); + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + { + WalkPosition next = current + delta; + if (Valid(next) && !contains(Visited, next)) + if (GetMiniTile(next, check_t::no_check).Walkable()) + if (!GetTile(TilePosition(next), check_t::no_check).GetNeutral()) + if (adjoins8SomeLakeOrNeutral(next, this)) + { + ToVisit.push_back(next); + Visited.push_back(next); + } + } + } + really_remove_if(Border, [&Visited](WalkPosition w) { return contains(Visited, w); }); + } + + // 3) If at least 2 doors, find the true doors in Border: a true door is a door that gives onto an area big enough + vector TrueDoors; + if (Doors.size() >= 2) + for (WalkPosition door : Doors) + { + vector ToVisit(1, door); + vector Visited(1, door); + const size_t limit = pCandidate->IsStaticBuilding() ? 10 : 400; + while (!ToVisit.empty() && (Visited.size() < limit)) + { + WalkPosition current = ToVisit.back(); ToVisit.pop_back(); + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + { + WalkPosition next = current + delta; + if (Valid(next) && !contains(Visited, next)) + if (GetMiniTile(next, check_t::no_check).Walkable()) + if (!GetTile(TilePosition(next), check_t::no_check).GetNeutral()) + { + ToVisit.push_back(next); + Visited.push_back(next); + } + } + } + if (Visited.size() >= limit) TrueDoors.push_back(door); + } + + // 4) If at least 2 true doors, pCandidate is a blocking static building + if (TrueDoors.size() >= 2) + { + // Marks pCandidate (and any Neutral stacked with it) as blocking. + for (Neutral * pNeutral = GetTile(pCandidate->TopLeft()).GetNeutral() ; pNeutral ; pNeutral = pNeutral->NextStacked()) + pNeutral->SetBlocking(TrueDoors); + + // Marks all the miniTiles of pCandidate as blocked. + // This way, areas at TrueDoors won't merge together. + for (int dy = 0 ; dy < WalkPosition(pCandidate->Size()).y ; ++dy) + for (int dx = 0 ; dx < WalkPosition(pCandidate->Size()).x ; ++dx) + { + auto & miniTile = GetMiniTile_(WalkPosition(pCandidate->TopLeft()) + WalkPosition(dx, dy)); + if (miniTile.Walkable()) miniTile.SetBlocked(); + } + } + } +} + + +// Helper class for void Map::ComputeAreas() +// Maintains some information about an area being computed +// A TempAreaInfo is not Valid() in two cases: +// - a default-constructed TempAreaInfo instance is never Valid (used as a dummy value to simplify the algorithm). +// - any other instance becomes invalid when absorbed (see Merge) +class TempAreaInfo +{ +public: + TempAreaInfo() : m_valid(false), m_id(0), m_top(0, 0), m_highestAltitude(0) { bwem_assert(!Valid());} + TempAreaInfo(Area::id id, MiniTile * pMiniTile, WalkPosition pos) + : m_valid(true), m_id(id), m_top(pos), m_size(0), m_highestAltitude(pMiniTile->Altitude()) + { Add(pMiniTile); bwem_assert(Valid()); } + + bool Valid() const { return m_valid; } + Area::id Id() const { bwem_assert(Valid()); return m_id; } + WalkPosition Top() const { bwem_assert(Valid()); return m_top; } + int Size() const { bwem_assert(Valid()); return m_size; } + altitude_t HighestAltitude() const { bwem_assert(Valid()); return m_highestAltitude; } + + void Add(MiniTile * pMiniTile) { bwem_assert(Valid()); ++m_size; pMiniTile->SetAreaId(m_id); } + + // Left to caller : m.SetAreaId(this->Id()) for each MiniTile m in Absorbed + void Merge(TempAreaInfo & Absorbed) { + bwem_assert(Valid() && Absorbed.Valid()); + bwem_assert(m_size >= Absorbed.m_size); + m_size += Absorbed.m_size; + Absorbed.m_valid = false; + } + + TempAreaInfo & operator=(const TempAreaInfo &) = delete; + +private: + bool m_valid; + const Area::id m_id; + const WalkPosition m_top; + const altitude_t m_highestAltitude; + int m_size; +}; + + +// Assigns MiniTile::m_areaId for each miniTile having AreaIdMissing() +// Areas are computed using MiniTile::Altitude() information only. +// The miniTiles are considered successively in descending order of their Altitude(). +// Each of them either: +// - involves the creation of a new area. +// - is added to some existing neighbouring area. +// - makes two neighbouring areas merge together. +void MapImpl::ComputeAreas() +{ + vector> MiniTilesByDescendingAltitude = SortMiniTiles(); + + vector TempAreaList = ComputeTempAreas(MiniTilesByDescendingAltitude); + + CreateAreas(TempAreaList); + + SetAreaIdInTiles(); +} + + +vector> MapImpl::SortMiniTiles() +{ + vector> MiniTilesByDescendingAltitude; + for (int y = 0 ; y < WalkSize().y ; ++y) + for (int x = 0 ; x < WalkSize().x ; ++x) + { + WalkPosition w = WalkPosition(x, y); + MiniTile & miniTile = GetMiniTile_(w, check_t::no_check); + if (miniTile.AreaIdMissing()) + MiniTilesByDescendingAltitude.emplace_back(w, &miniTile); + } + + sort(MiniTilesByDescendingAltitude.begin(), MiniTilesByDescendingAltitude.end(), + [](const pair & a, const pair & b){ return a.second->Altitude() > b.second->Altitude(); }); + + return MiniTilesByDescendingAltitude; +} + + +static pair findNeighboringAreas(WalkPosition p, const MapImpl * pMap) +{ + pair result(0, 0); + + for (WalkPosition delta : {WalkPosition(0, -1), WalkPosition(-1, 0), WalkPosition(+1, 0), WalkPosition(0, +1)}) + if (pMap->Valid(p + delta)) + { + Area::id areaId = pMap->GetMiniTile(p + delta, check_t::no_check).AreaId(); + if (areaId > 0) + if (!result.first) result.first = areaId; + else if (result.first != areaId) + if (!result.second || ((areaId < result.second))) + result.second = areaId; + } + + return result; +} + + +static Area::id chooseNeighboringArea(Area::id a, Area::id b) +{ + static map, int> map_AreaPair_counter; + + if (a > b) swap(a, b); + return (map_AreaPair_counter[make_pair(a, b)]++ % 2 == 0) ? a : b; +} + + +vector MapImpl::ComputeTempAreas(const vector> & MiniTilesByDescendingAltitude) +{ + vector TempAreaList(1); // TempAreaList[0] left unused, as AreaIds are > 0 + for (const auto & Current : MiniTilesByDescendingAltitude) + { + const WalkPosition pos = Current.first; + MiniTile * cur = Current.second; + + pair neighboringAreas = findNeighboringAreas(pos, this); + if (!neighboringAreas.first) // no neighboring area : creates of a new area + { + TempAreaList.emplace_back((Area::id)TempAreaList.size(), cur, pos); + } + else if (!neighboringAreas.second) // one neighboring area : adds cur to the existing area + { + TempAreaList[neighboringAreas.first].Add(cur); + } + else // two neighboring areas : adds cur to one of them & possible merging + { + Area::id smaller = neighboringAreas.first; + Area::id bigger = neighboringAreas.second; + if (TempAreaList[smaller].Size() > TempAreaList[bigger].Size()) swap(smaller, bigger); + + // Condition for the neighboring areas to merge: + if ((TempAreaList[smaller].Size() < 80) || + (TempAreaList[smaller].HighestAltitude() < 80) || + (cur->Altitude() / (double)TempAreaList[bigger].HighestAltitude() >= 0.90) || + (cur->Altitude() / (double)TempAreaList[smaller].HighestAltitude() >= 0.90) || + any_of(StartingLocations().begin(), StartingLocations().end(), [&pos](const TilePosition & startingLoc) + { return dist(TilePosition(pos), startingLoc + TilePosition(2, 1)) <= 3;}) || + false + ) + { + // adds cur to the absorbing area: + TempAreaList[bigger].Add(cur); + + // merges the two neighboring areas: + ReplaceAreaIds(TempAreaList[smaller].Top(), bigger); + TempAreaList[bigger].Merge(TempAreaList[smaller]); + } + else // no merge : cur starts or continues the frontier between the two neighboring areas + { + // adds cur to the chosen Area: + TempAreaList[chooseNeighboringArea(smaller, bigger)].Add(cur); + m_RawFrontier.emplace_back(neighboringAreas, pos); + } + } + } + + // Remove from the frontier obsolete positions + really_remove_if(m_RawFrontier, [](const pair, BWAPI::WalkPosition> & f) + { return f.first.first == f.first.second; }); + + return TempAreaList; +} + + +// Initializes m_Graph with the valid and big enough areas in TempAreaList. +void MapImpl::CreateAreas(const vector & TempAreaList) +{ + typedef pair pair_top_size_t; + vector AreasList; + + Area::id newAreaId = 1; + Area::id newTinyAreaId = -2; + + for (auto & TempArea : TempAreaList) + if (TempArea.Valid()) + { + if (TempArea.Size() >= area_min_miniTiles) + { + bwem_assert(newAreaId <= TempArea.Id()); + if (newAreaId != TempArea.Id()) + ReplaceAreaIds(TempArea.Top(), newAreaId); + + AreasList.emplace_back(TempArea.Top(), TempArea.Size()); + newAreaId++; + } + else + { + ReplaceAreaIds(TempArea.Top(), newTinyAreaId); + newTinyAreaId--; + } + } + + GetGraph().CreateAreas(AreasList); +} + + +void MapImpl::SetAreaIdInTile(TilePosition t) +{ + auto & tile = GetTile_(t); + bwem_assert(tile.AreaId() == 0); // initialized to 0 + + for (int dy = 0 ; dy < 4 ; ++dy) + for (int dx = 0 ; dx < 4 ; ++dx) + if (Area::id id = GetMiniTile(WalkPosition(t) + WalkPosition(dx, dy), check_t::no_check).AreaId()) + if (tile.AreaId() == 0) tile.SetAreaId(id); + else if (tile.AreaId() != id) + { + tile.SetAreaId(-1); + return; + } +} + + +void MapImpl::SetAltitudeInTile(TilePosition t) +{ + altitude_t minAltitude = numeric_limits::max(); + + for (int dy = 0 ; dy < 4 ; ++dy) + for (int dx = 0 ; dx < 4 ; ++dx) + { + altitude_t altitude = GetMiniTile(WalkPosition(t) + WalkPosition(dx, dy), check_t::no_check).Altitude(); + if (altitude < minAltitude) minAltitude = altitude; + } + + GetTile_(t).SetMinAltitude(minAltitude); +} + + +void MapImpl::SetAreaIdInTiles() +{ + for (int y = 0 ; y < Size().y ; ++y) + for (int x = 0 ; x < Size().x ; ++x) + { + TilePosition t(x, y); + SetAreaIdInTile(t); + SetAltitudeInTile(t); + } +} + + +Mineral * MapImpl::GetMineral(BWAPI::Unit u) const +{ + auto iMineral = find_if(m_Minerals.begin(), m_Minerals.end(), [u](const unique_ptr & m){ return m->Unit() == u; }); + return (iMineral != m_Minerals.end()) ? iMineral->get() : nullptr; +} + + +Geyser * MapImpl::GetGeyser(BWAPI::Unit u) const +{ + auto iGeyser = find_if(m_Geysers.begin(), m_Geysers.end(), [u](const unique_ptr & g){ return g->Unit() == u; }); + return (iGeyser != m_Geysers.end()) ? iGeyser->get() : nullptr; +} + + +void MapImpl::OnMineralDestroyed(BWAPI::Unit u) +{ + auto iMineral = find_if(m_Minerals.begin(), m_Minerals.end(), [u](const unique_ptr & m){ return m->Unit() == u; }); + bwem_assert(iMineral != m_Minerals.end()); + + fast_erase(m_Minerals, distance(m_Minerals.begin(), iMineral)); +} + + +void MapImpl::OnStaticBuildingDestroyed(BWAPI::Unit u) +{ + auto iStaticBuilding = find_if(m_StaticBuildings.begin(), m_StaticBuildings.end(), [u](const unique_ptr & g){ return g->Unit() == u; }); + bwem_assert(iStaticBuilding != m_StaticBuildings.end()); + + fast_erase(m_StaticBuildings, distance(m_StaticBuildings.begin(), iStaticBuilding)); +} + + +void MapImpl::OnMineralDestroyed(const Mineral * pMineral) +{ + for (Area & area : GetGraph().Areas()) + area.OnMineralDestroyed(pMineral); +} + + +void MapImpl::OnBlockingNeutralDestroyed(const Neutral * pBlocking) +{ + bwem_assert(pBlocking && pBlocking->Blocking()); + + for (const Area * pArea : pBlocking->BlockedAreas()) + for (const ChokePoint * cp : pArea->ChokePoints()) + const_cast(cp)->OnBlockingNeutralDestroyed(pBlocking); + + if (GetTile(pBlocking->TopLeft()).GetNeutral()) return; // there remains some blocking Neutrals at the same location + + // Unblock the miniTiles of pBlocking: + Area::id newId = pBlocking->BlockedAreas().front()->Id(); + for (int dy = 0 ; dy < WalkPosition(pBlocking->Size()).y ; ++dy) + for (int dx = 0 ; dx < WalkPosition(pBlocking->Size()).x ; ++dx) + { + auto & miniTile = GetMiniTile_(WalkPosition(pBlocking->TopLeft()) + WalkPosition(dx, dy)); + if (miniTile.Walkable()) miniTile.ReplaceBlockedAreaId(newId); + } + + // Unblock the Tiles of pBlocking: + for (int dy = 0 ; dy < pBlocking->Size().y ; ++dy) + for (int dx = 0 ; dx < pBlocking->Size().x ; ++dx) + { + GetTile_(pBlocking->TopLeft() + TilePosition(dx, dy)).ResetAreaId(); + SetAreaIdInTile(pBlocking->TopLeft() + TilePosition(dx, dy)); + } + + if (AutomaticPathUpdate()) + GetGraph().ComputeChokePointDistanceMatrix(); +} + + +bool MapImpl::FindBasesForStartingLocations() +{ + bool atLeastOneFailed = false; + for (auto location : StartingLocations()) + { + bool found = false; + for (Area & area : GetGraph().Areas()) if (!found) + for (Base & base : area.Bases()) if (!found) + if (queenWiseDist(base.Location(), location) <= max_tiles_between_StartingLocation_and_its_AssignedBase) + { + base.SetStartingLocation(location); + found = true; + } + + if (!found) atLeastOneFailed = true; + } + + return !atLeastOneFailed; +} + + +}} // namespace BWEM::detail + + + diff --git a/Source/BWEM/src/mapImpl.h b/Source/BWEM/src/mapImpl.h new file mode 100644 index 0000000..3e7178c --- /dev/null +++ b/Source/BWEM/src/mapImpl.h @@ -0,0 +1,156 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_MAP_IMPL_H +#define BWEM_MAP_IMPL_H + +#include +#include "graph.h" +#include "map.h" +#include "tiles.h" +#include +#include +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Neutral; +class Mineral; +class Geyser; +class StaticBuilding; +class ChokePoint; + +namespace detail { + +using namespace std; +using namespace utils; +using namespace BWAPI_ext; + + + +class TempAreaInfo; +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapImpl +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// + +class MapImpl : public Map +{ +public: + // Downcast helpers + static MapImpl * Get(Map * pMap) { return static_cast(pMap); } + static const MapImpl * Get(const Map * pMap) { return static_cast(pMap); } + + using Map::GetTile_; + using Map::GetMiniTile_; + + MapImpl(); + ~MapImpl(); + + void Initialize() override; + + bool AutomaticPathUpdate() const override { return m_automaticPathUpdate; } + void EnableAutomaticPathAnalysis() const override { m_automaticPathUpdate = true; } + + bool FindBasesForStartingLocations() override; + + altitude_t MaxAltitude() const override { return m_maxAltitude; } + + int BaseCount() const override { return GetGraph().BaseCount(); } + int ChokePointCount() const override { return GetGraph().ChokePoints().size(); } + + const vector & StartingLocations() const override { return m_StartingLocations; } + + const vector> & Geysers() const override { return m_Geysers; } + const vector> & Minerals() const override { return m_Minerals; } + const vector> & StaticBuildings() const override { return m_StaticBuildings; } + + Mineral * GetMineral(BWAPI::Unit u) const override; + Geyser * GetGeyser(BWAPI::Unit u) const override; + + void OnMineralDestroyed(BWAPI::Unit u) override; + void OnStaticBuildingDestroyed(BWAPI::Unit u) override; + + const vector & Areas() const override { return GetGraph().Areas(); } + + // Returns an Area given its id. Range = 1..Size() + const Area * GetArea(Area::id id) const override { return m_Graph.GetArea(id); } + Area * GetArea(Area::id id) { return m_Graph.GetArea(id); } + + const Area * GetArea(BWAPI::WalkPosition w) const override { return m_Graph.GetArea(w); } + const Area * GetArea(BWAPI::TilePosition t) const override { return m_Graph.GetArea(t); } + + Area * GetArea(BWAPI::WalkPosition w) { return m_Graph.GetArea(w); } + Area * GetArea(BWAPI::TilePosition t) { return m_Graph.GetArea(t); } + + const Area * GetNearestArea(BWAPI::WalkPosition w) const override { return m_Graph.GetNearestArea(w); } + const Area * GetNearestArea(BWAPI::TilePosition t) const override { return m_Graph.GetNearestArea(t); } + + Area * GetNearestArea(BWAPI::WalkPosition w) { return m_Graph.GetNearestArea(w); } + Area * GetNearestArea(BWAPI::TilePosition t) { return m_Graph.GetNearestArea(t); } + + + const CPPath & GetPath(const BWAPI::Position & a, const BWAPI::Position & b, int * pLength = nullptr) const override { return m_Graph.GetPath(a, b, pLength); } + + const class Graph & GetGraph() const { return m_Graph; } + class Graph & GetGraph() { return m_Graph; } + + + const vector, BWAPI::WalkPosition>> & RawFrontier() const override { return m_RawFrontier; } + + + + void OnMineralDestroyed(const Mineral * pMineral); + void OnBlockingNeutralDestroyed(const Neutral * pBlocking); + +private: + void ReplaceAreaIds(BWAPI::WalkPosition p, Area::id newAreaId); + + void InitializeNeutrals(); + void LoadData(); + void DecideSeasOrLakes(); + void ComputeAltitude(); + void ProcessBlockingNeutrals(); + void ComputeAreas(); + vector> + SortMiniTiles(); + vector ComputeTempAreas(const vector> & MiniTilesByDescendingAltitude); + void CreateAreas(const vector & TempAreaList); + void SetAreaIdInTiles(); + void SetAreaIdInTile(BWAPI::TilePosition t); + void SetAltitudeInTile(BWAPI::TilePosition t); + + + altitude_t m_maxAltitude; + + mutable bool m_automaticPathUpdate = false; + + class Graph m_Graph; + vector> m_Minerals; + vector> m_Geysers; + vector> m_StaticBuildings; + vector m_StartingLocations; + + vector, BWAPI::WalkPosition>> m_RawFrontier; +}; + + + + + +}} // namespace BWEM::detail + + +#endif + diff --git a/Source/BWEM/src/mapPrinter.cpp b/Source/BWEM/src/mapPrinter.cpp new file mode 100644 index 0000000..f0d8e49 --- /dev/null +++ b/Source/BWEM/src/mapPrinter.cpp @@ -0,0 +1,163 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "mapPrinter.h" + +#if BWEM_USE_MAP_PRINTER + +#include "map.h" +#include "../EasyBMP_1.06/EasyBMP.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace BWAPI_ext; + +namespace utils { + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapPrinter +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +const string MapPrinter::m_fileName = "bwapi-data/map.bmp"; + +std::unique_ptr MapPrinter::m_pBMP; +MapPrinter MapPrinter::m_Instance; +const Map * MapPrinter::m_pMap = nullptr; + +const bool MapPrinter::showAltitude = true; +const bool MapPrinter::showAreas = true; +const bool MapPrinter::showContinents = !showAreas && true; +const bool MapPrinter::showLakes = true; +const bool MapPrinter::showSeaSide = true; +const bool MapPrinter::showUnbuildable = false; +const bool MapPrinter::showGroundHeight = true; +const bool MapPrinter::showChokePoints = true; +const bool MapPrinter::showResources = true; +const bool MapPrinter::showMinerals = showResources && true; +const bool MapPrinter::showGeysers = showResources && true; +const bool MapPrinter::showStaticBuildings = true; +const bool MapPrinter::showBlockingBuildings = true; +const bool MapPrinter::showStackedNeutrals = true; +const bool MapPrinter::showStartingLocations = true; +const bool MapPrinter::showBases = true; +const bool MapPrinter::showAssignedRessources = showBases && true; +const bool MapPrinter::showData = false; + +const MapPrinter::Color MapPrinter::Color::higherGround = Color(0, 0, 0); +const MapPrinter::Color MapPrinter::Color::unbuildable = Color(0, 0, 0); +const MapPrinter::Color MapPrinter::Color::terrain = Color(164, 164, 164); +const MapPrinter::Color MapPrinter::Color::sea = Color(0, 0, 192); +const MapPrinter::Color MapPrinter::Color::seaSide = Color(0, 0, 100); +const MapPrinter::Color MapPrinter::Color::lake = Color(0, 100, 200); +const MapPrinter::Color MapPrinter::Color::tinyArea = Color(50, 50, 255); +const MapPrinter::Color MapPrinter::Color::chokePoints = (showAreas||showContinents) ? Color(255, 255, 255) : Color(0, 0, 0); +const MapPrinter::Color MapPrinter::Color::minerals = Color(0, 255, 255); +const MapPrinter::Color MapPrinter::Color::geysers = Color(0, 255, 0); +const MapPrinter::Color MapPrinter::Color::staticBuildings = Color(255, 0, 255); +const MapPrinter::Color MapPrinter::Color::blockingNeutrals = Color(0, 0, 0); +const MapPrinter::Color MapPrinter::Color::startingLocations = Color(255, 255, 0); +const MapPrinter::Color MapPrinter::Color::bases = Color(0, 0, 255); + + +void MapPrinter::Initialize(const Map * pMap) +{ + bwem_assert_throw(pMap->Initialized()); + bwem_assert_throw_plus(canWrite(m_fileName), "MapPrinter could not create the file " + m_fileName); + + m_pMap = pMap; + m_pBMP = make_unique(); + m_pBMP->SetSize(bw->mapWidth()*4, bw->mapHeight()*4); + m_pBMP->SetBitDepth(24); +} + + +MapPrinter::~MapPrinter() +{ + if (canWrite(m_fileName)) m_pBMP->WriteToFile(m_fileName.c_str()); + +// Uncomment the 2 lines below to write one more copy of the bitmap, in the folder of the map used, with the same name. +// string twinFileName = bw->mapPathName().substr(0, bw->mapPathName().size()-3) + "bmp"; +// if (canWrite(twinFileName)) m_pBMP->WriteToFile(twinFileName.c_str()); +} + + +void MapPrinter::Point(int x, int y, Color col) +{ + bwem_assert((0 <= x) && (x < m_pBMP->TellWidth())); + bwem_assert((0 <= y) && (y < m_pBMP->TellHeight())); + + RGBApixel rgba; + rgba.Red = col.red; + rgba.Green = col.green; + rgba.Blue = col.blue; + + m_pBMP->SetPixel(x, y, rgba); +} + + +void MapPrinter::Line(WalkPosition A, WalkPosition B, Color col, dashed_t dashedMode) +{ + int N = max(1, roundedDist(A, B)); + if ((dashedMode == dashed) && (N >= 4)) N /= 2; + + for (int i = 0 ; i <= N ; ++i) + MapPrinter::Get().Point((A*i + B*(N-i))/N, col); +} + + +void MapPrinter::Rectangle(WalkPosition TopLeft, WalkPosition BottomRight, Color col, fill_t fillMode, dashed_t dashedMode) +{ + for (int y = TopLeft.y ; y <= BottomRight.y ; ++y) + for (int x = TopLeft.x ; x <= BottomRight.x ; ++x) + if ((fillMode == fill) || (y == TopLeft.y) || (y == BottomRight.y) || (x == TopLeft.x) || (x == BottomRight.x)) + if ((dashedMode == not_dashed) || ((x + y) & 1)) + MapPrinter::Get().Point(x, y, col); +} + + +void MapPrinter::Square(WalkPosition Center, int radius, Color col, fill_t fillMode) +{ + for (int y = Center.y-radius ; y <= Center.y+radius ; ++y) + for (int x = Center.x-radius ; x <= Center.x+radius ; ++x) + if ((fillMode == fill) || (y == Center.y-radius) || (y == Center.y+radius) || (x == Center.x-radius) || (x == Center.x+radius)) + if (m_pMap->Valid(WalkPosition(x, y))) + MapPrinter::Get().Point(x, y, col); +} + + +void MapPrinter::Circle(WalkPosition Center, int radius, Color col, fill_t fillMode) +{ + for (int y = Center.y-radius ; y <= Center.y+radius ; ++y) + for (int x = Center.x-radius ; x <= Center.x+radius ; ++x) + { + WalkPosition w(x, y); + if (dist(w, Center) <= radius) + if ((fillMode == fill) || (dist(w, Center) >= radius-1)) + if (m_pMap->Valid(w)) + MapPrinter::Get().Point(x, y, col); + } +} + + +}} // namespace BWEM::utils + +#endif // BWEM_USE_MAP_PRINTER + diff --git a/Source/BWEM/src/mapPrinter.h b/Source/BWEM/src/mapPrinter.h new file mode 100644 index 0000000..99fdc41 --- /dev/null +++ b/Source/BWEM/src/mapPrinter.h @@ -0,0 +1,129 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_MAP_PRINTER_H +#define BWEM_MAP_PRINTER_H + +#include +#include +#include "utils.h" +#include "defs.h" + +#if BWEM_USE_MAP_PRINTER + +class BMP; +namespace BWEM { +class Map; +namespace utils { + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MapPrinter +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// + + +class MapPrinter +{ +public: + static const bool showAltitude; // renders the Altitude() value for each MiniTile + static const bool showAreas; // renders Areas with distinct colors + static const bool showContinents; // renders continents with distinct colors, unless showAreas == true + static const bool showLakes; // prints unwalkable MiniTiles in Areas + static const bool showSeaSide; // highlights Sea-MiniTiles next to Terrain-MiniTiles + static const bool showUnbuildable; // renders Tiles that are not Buildable() + static const bool showGroundHeight; // renders Tiles for which GroundHeight() > 0 + static const bool showChokePoints; // renders the ChokePoints suggested by BWEM + static const bool showResources; + static const bool showMinerals; // prints the Minerals, unless showResources == false + static const bool showGeysers; // prints the Geysers, unless showResources == false + static const bool showStaticBuildings; // prints the StaticBuildings + static const bool showBlockingBuildings; // renders the StaticBuildings and the Minerals that are blocking some ChokePoint + static const bool showStackedNeutrals; // renders the StaticBuildings and the Minerals that are stacked + static const bool showStartingLocations; // renders the Bases that are at starting locations + static const bool showBases; // prints the Bases suggested by BWEM + static const bool showAssignedRessources; // renders the Ressources assigned to each Base, unless showBases == false + static const bool showData; // renders the Data() value for each MiniTile + + struct Color + { + static const Color higherGround; + static const Color unbuildable; + static const Color terrain; + static const Color sea; + static const Color seaSide; + static const Color lake; + static const Color tinyArea; + static const Color chokePoints; + static const Color minerals; + static const Color geysers; + static const Color staticBuildings; + static const Color blockingNeutrals; + static const Color startingLocations; + static const Color bases; + + Color() : red(0), green(0), blue(0) {} + Color(int r, int g, int b) : red(uint8_t(r)), green(uint8_t(g)), blue(uint8_t(b)) + { + bwem_assert( + utils::CanTypeFitValue(r) && + utils::CanTypeFitValue(g) && + utils::CanTypeFitValue(b)); + } + + uint8_t red; + uint8_t green; + uint8_t blue; + }; + + + static void Initialize(const Map * pMap); + static MapPrinter & Get() { bwem_assert_throw_plus(m_pMap, "MapPrinter not initialized"); return m_Instance; } + + enum dashed_t {not_dashed, dashed}; + enum fill_t {do_not_fill, fill}; + + void Point(int x, int y, Color col); + void Point(BWAPI::WalkPosition p, Color col) { Point(p.x, p.y, col); } + + void Line(BWAPI::WalkPosition A, BWAPI::WalkPosition B, Color col, dashed_t dashedMode = not_dashed); + + void Rectangle(BWAPI::WalkPosition TopLeft, BWAPI::WalkPosition BottomRight, Color col, fill_t fillMode = do_not_fill, dashed_t dashedMode = not_dashed); + + // This function will automatically crop the square so that it fits in the Map. + void Square(BWAPI::WalkPosition Center, int radius, Color col, fill_t fillMode = do_not_fill); + + // This function will automatically crop the circle so that it fits in the Map. + void Circle(BWAPI::WalkPosition Center, int radius, Color col, fill_t fillMode = do_not_fill); + + MapPrinter(const MapPrinter &) = delete; + MapPrinter & operator=(const MapPrinter &) = delete; + +private: + MapPrinter() = default; + ~MapPrinter(); + + + static std::unique_ptr m_pBMP; + static MapPrinter m_Instance; + static const Map * m_pMap; + static const std::string m_fileName; +}; + + +}} // namespace BWEM::detail::detail + +#endif // BWEM_USE_MAP_PRINTER + +#endif + diff --git a/Source/BWEM/src/neutral.cpp b/Source/BWEM/src/neutral.cpp new file mode 100644 index 0000000..766c93d --- /dev/null +++ b/Source/BWEM/src/neutral.cpp @@ -0,0 +1,221 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "neutral.h" +#include "mapImpl.h" + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Neutral +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +Neutral::Neutral(BWAPI::Unit u, Map * pMap) + : m_bwapiUnit(u), m_bwapiType(u->getType()), m_pMap(pMap), + m_pos(u->getInitialPosition()), + m_topLeft(u->getInitialTilePosition()), + m_size(u->getInitialType().tileSize()) +{ + if (u->getType() == Special_Right_Pit_Door) ++m_topLeft.x; + + PutOnTiles(); +} + + +Neutral::~Neutral() +{ + try + { + RemoveFromTiles(); + + if (Blocking()) + MapImpl::Get(GetMap())->OnBlockingNeutralDestroyed(this); + } + catch(...) + { + bwem_assert(false); + } +} + + +TilePosition Neutral::BottomRight() const +{ + return m_topLeft + m_size - 1; +} + + +void Neutral::PutOnTiles() +{ + bwem_assert(!m_pNextStacked); + + for (int dy = 0 ; dy < Size().y ; ++dy) + for (int dx = 0 ; dx < Size().x ; ++dx) + { + auto & tile = MapImpl::Get(GetMap())->GetTile_(TopLeft() + TilePosition(dx, dy)); + if (!tile.GetNeutral()) tile.AddNeutral(this); + else + { + Neutral * pTop = tile.GetNeutral()->LastStacked(); + bwem_assert(this != tile.GetNeutral()); + bwem_assert(this != pTop); + bwem_assert(!pTop->IsGeyser()); + bwem_assert_plus(pTop->Type() == Type(), "stacked neutrals have different types: " + pTop->Type().getName() + " / " + Type().getName()); + bwem_assert_plus(pTop->TopLeft() == TopLeft(), "stacked neutrals not aligned: " + my_to_string(pTop->TopLeft()) + " / " + my_to_string(TopLeft())); + bwem_assert((dx == 0) && (dy == 0)); + + pTop->m_pNextStacked = this; + return; + } + } +} + + +void Neutral::RemoveFromTiles() +{ + for (int dy = 0 ; dy < Size().y ; ++dy) + for (int dx = 0 ; dx < Size().x ; ++dx) + { + auto & tile = MapImpl::Get(GetMap())->GetTile_(TopLeft() + TilePosition(dx, dy)); + bwem_assert(tile.GetNeutral()); + + if (tile.GetNeutral() == this) + { + tile.RemoveNeutral(this); + if (m_pNextStacked) tile.AddNeutral(m_pNextStacked); + } + else + { + Neutral * pPrevStacked = tile.GetNeutral(); + while (pPrevStacked->NextStacked() != this) pPrevStacked = pPrevStacked->NextStacked(); + bwem_assert(pPrevStacked->Type() == Type()); + bwem_assert(pPrevStacked->TopLeft() == TopLeft()); + bwem_assert((dx == 0) && (dy == 0)); + + pPrevStacked->m_pNextStacked = m_pNextStacked; + m_pNextStacked = nullptr; + return; + } + } + + m_pNextStacked = nullptr; +} + + +vector Neutral::BlockedAreas() const +{ + vector Result; + for (WalkPosition w : m_blockedAreas) + Result.push_back(GetMap()->GetArea(w)); + + return Result; +} + + +void Neutral::SetBlocking(const vector & blockedAreas) +{ + bwem_assert(m_blockedAreas.empty() && !blockedAreas.empty()); + + m_blockedAreas = blockedAreas; +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Ressource +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +Ressource::Ressource(BWAPI::Unit u, Map * pMap) + : Neutral(u, pMap), + m_initialAmount(u->getInitialResources()) +{ + bwem_assert(Type().isMineralField() || (Type() == Resource_Vespene_Geyser)); +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Mineral +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +Mineral::Mineral(BWAPI::Unit u, Map * pMap) + : Ressource(u, pMap) +{ + bwem_assert(Type().isMineralField()); +} + + +Mineral::~Mineral() +{ + MapImpl::Get(GetMap())->OnMineralDestroyed(this); +} + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Geyser +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +Geyser::Geyser(BWAPI::Unit u, Map * pMap) + : Ressource(u, pMap) +{ + bwem_assert(Type() == Resource_Vespene_Geyser); +} + + +Geyser::~Geyser() +{ +} + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class StaticBuilding +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +StaticBuilding::StaticBuilding(BWAPI::Unit u, Map * pMap) : Neutral(u, pMap) +{ + bwem_assert(Type().isSpecialBuilding() || + (Type() == Special_Pit_Door) || + Type() == Special_Right_Pit_Door); +} + + + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/neutral.h b/Source/BWEM/src/neutral.h new file mode 100644 index 0000000..672e0d9 --- /dev/null +++ b/Source/BWEM/src/neutral.h @@ -0,0 +1,229 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_NEUTRAL_H +#define BWEM_NEUTRAL_H + +#include +#include "bwapiExt.h" +#include +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Ressource; +class Mineral; +class Geyser; +class Area; +class StaticBuilding; +class Map; + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Neutral +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Neutral is the abstract base class for a small hierarchy of wrappers around some BWAPI::Units +// The units concerned are the Ressources (Minerals and Geysers) and the static Buildings. +// Stacked Neutrals are supported, provided they share the same type at the same location. +// + +class Neutral : public utils::UserData +{ +public: + + // If this Neutral is a Ressource, returns a typed pointer to this Ressource. + // Otherwise, returns nullptr. + virtual Ressource * IsRessource() { return nullptr; } + virtual const Ressource * IsRessource() const { return nullptr; } + + // If this Neutral is a Mineral, returns a typed pointer to this Mineral. + // Otherwise, returns nullptr. + virtual Mineral * IsMineral() { return nullptr; } + virtual const Mineral * IsMineral() const { return nullptr; } + + // If this Neutral is a Geyser, returns a typed pointer to this Geyser. + // Otherwise, returns nullptr. + virtual Geyser * IsGeyser() { return nullptr; } + virtual const Geyser * IsGeyser() const { return nullptr; } + + // If this Neutral is a StaticBuilding, returns a typed pointer to this StaticBuilding. + // Otherwise, returns nullptr. + virtual StaticBuilding * IsStaticBuilding() { return nullptr; } + virtual const StaticBuilding * IsStaticBuilding() const { return nullptr; } + + // Returns the BWAPI::Unit this Neutral is wrapping around. + BWAPI::Unit Unit() const { return m_bwapiUnit; } + + // Returns the BWAPI::UnitType of the BWAPI::Unit this Neutral is wrapping around. + BWAPI::UnitType Type() const { return m_bwapiType; } + + // Returns the center of this Neutral, in pixels (same as Unit()->getInitialPosition()). + BWAPI::Position Pos() const { return m_pos; } + + // Returns the top left Tile position of this Neutral (same as Unit()->getInitialTilePosition()). + BWAPI::TilePosition TopLeft() const { return m_topLeft; } + + // Returns the bottom right Tile position of this Neutral + BWAPI::TilePosition BottomRight() const; + + // Returns the size of this Neutral, in Tiles (same as Type()->tileSize()) + BWAPI::TilePosition Size() const { return m_size; } + + // Tells whether this Neutral is blocking some ChokePoint. + // This applies to Minerals and StaticBuildings only. + // For each blocking Neutral, a pseudo ChokePoint (which is Blocked()) is created on top of it, + // with the exception of stacked blocking Neutrals for which only one pseudo ChokePoint is created. + // Cf. definition of pseudo ChokePoints in class ChokePoint comment. + // Cf. ChokePoint::BlockingNeutral and ChokePoint::Blocked. + bool Blocking() const { return !m_blockedAreas.empty(); } + + // If Blocking() == true, returns the set of Areas blocked by this Neutral. + std::vector BlockedAreas() const; + + // Returns the next Neutral stacked over this Neutral, if ever. + // To iterate through the whole stack, one can use the following: + // for (const Neutral * n = Map::GetTile(TopLeft()).GetNeutral() ; n ; n = n->NextStacked()) + Neutral * NextStacked() const { return m_pNextStacked; } + + // Returns the last Neutral stacked over this Neutral, if ever. + Neutral * LastStacked() { Neutral * pTop = this; while (pTop->m_pNextStacked) pTop = pTop->m_pNextStacked; return pTop; } + + Neutral & operator=(const Neutral &) = delete; + + //////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + void SetBlocking(const std::vector & blockedAreas); + +protected: + Neutral(BWAPI::Unit u, Map * pMap); + ~Neutral(); + Map * const GetMap() const { return m_pMap; } + +private: + void PutOnTiles(); + void RemoveFromTiles(); + + const BWAPI::Unit m_bwapiUnit; + const BWAPI::UnitType m_bwapiType; + const BWAPI::Position m_pos; + BWAPI::TilePosition m_topLeft; + const BWAPI::TilePosition m_size; + Map * const m_pMap; + Neutral * m_pNextStacked = nullptr; + std::vectorm_blockedAreas; +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Ressource +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// A Ressource is either a Mineral or a Geyser + +class Ressource : public Neutral +{ +public: + Ressource(BWAPI::Unit u, Map * pMap); + + Ressource * IsRessource() override { return this; } + const Ressource * IsRessource() const override{ return this; } + + // Returns the initial amount of ressources for this Ressource (same as Unit()->getInitialResources). + int InitialAmount() const { return m_initialAmount; } + + // Returns the current amount of ressources for this Ressource (same as Unit()->getResources). + int Amount() const { return Unit()->getResources(); } + +private: + const int m_initialAmount; +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Mineral +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Minerals Correspond to the units in BWAPI::getStaticNeutralUnits() for which getType().isMineralField(), + +class Mineral : public Ressource +{ +public: + Mineral(BWAPI::Unit u, Map * pMap); + ~Mineral(); + + Mineral * IsMineral() override { return this; } + const Mineral * IsMineral() const override { return this; } + +private: +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Geyser +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Geysers Correspond to the units in BWAPI::getStaticNeutralUnits() for which getType() == Resource_Vespene_Geyser + +class Geyser : public Ressource +{ +public: + Geyser(BWAPI::Unit u, Map * pMap); + ~Geyser(); + + Geyser * IsGeyser() override { return this; } + const Geyser * IsGeyser() const override { return this; } + +private: +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class StaticBuilding +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// StaticBuildings Correspond to the units in BWAPI::getStaticNeutralUnits() for which getType().isSpecialBuilding +// StaticBuilding also wrappers some special units like Special_Pit_Door. + +class StaticBuilding : public Neutral +{ +public: + StaticBuilding(BWAPI::Unit u, Map * pMap); + + StaticBuilding * IsStaticBuilding() override { return this; } + const StaticBuilding * IsStaticBuilding() const override { return this; } + +private: +}; + + + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/tiles.cpp b/Source/BWEM/src/tiles.cpp new file mode 100644 index 0000000..df03d51 --- /dev/null +++ b/Source/BWEM/src/tiles.cpp @@ -0,0 +1,98 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "tiles.h" +#include "neutral.h" +#include "bwapiExt.h" +#include "winutils.h" +#include "map.h" + + +using namespace BWAPI; +using namespace BWAPI::UnitTypes::Enum; +namespace { auto & bw = Broodwar; } + +using namespace std; + + +namespace BWEM { + +using namespace detail; +using namespace BWAPI_ext; + +namespace { enum {sizeof_Tile = sizeof(Tile), sizeof_MiniTile = sizeof(MiniTile)}; } + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MiniTile +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +const Area::id MiniTile::blockingCP = std::numeric_limits::min(); + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Tile +// // +////////////////////////////////////////////////////////////////////////////////////////////// + + +int Tile::StackedNeutrals() const +{ + int stackSize = 0; + for (Neutral * pStacked = GetNeutral() ; pStacked ; pStacked = pStacked->NextStacked()) + ++stackSize; + + return stackSize; +}; + + + +altitude_t minAltitudeTop(const TilePosition & tile, const Map & theMap) +{ + WalkPosition w(tile); + + return min(theMap.GetMiniTile(w + WalkPosition(1, 0), utils::check_t::no_check).Altitude(), + theMap.GetMiniTile(w + WalkPosition(2, 0), utils::check_t::no_check).Altitude()); +} + + +altitude_t minAltitudeBottom(const TilePosition & tile, const Map & theMap) +{ + WalkPosition w(tile); + + return min(theMap.GetMiniTile(w + WalkPosition(1, 3), utils::check_t::no_check).Altitude(), + theMap.GetMiniTile(w + WalkPosition(2, 3), utils::check_t::no_check).Altitude()); +} + + +altitude_t minAltitudeLeft(const TilePosition & tile, const Map & theMap) +{ + WalkPosition w(tile); + + return min(theMap.GetMiniTile(w + WalkPosition(0, 1), utils::check_t::no_check).Altitude(), + theMap.GetMiniTile(w + WalkPosition(0, 2), utils::check_t::no_check).Altitude()); +} + + +altitude_t minAltitudeRight(const TilePosition & tile, const Map & theMap) +{ + WalkPosition w(tile); + + return min(theMap.GetMiniTile(w + WalkPosition(3, 1), utils::check_t::no_check).Altitude(), + theMap.GetMiniTile(w + WalkPosition(3, 2), utils::check_t::no_check).Altitude()); +} + + +} // namespace BWEM + + + diff --git a/Source/BWEM/src/tiles.h b/Source/BWEM/src/tiles.h new file mode 100644 index 0000000..2f08ec2 --- /dev/null +++ b/Source/BWEM/src/tiles.h @@ -0,0 +1,255 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_TILES_H +#define BWEM_TILES_H + +#include +#include "area.h" +#include "utils.h" +#include "defs.h" + + +namespace BWEM { + +class Neutral; +class Map; + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class MiniTile +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Corresponds to BWAPI/Starcraft's concept of minitile (8x8 pixels). +// MiniTiles are accessed using WalkPositions (Cf. Map::GetMiniTile). +// A Map holds Map::WalkSize().x * Map::WalkSize().y MiniTiles as its "MiniTile map". +// A MiniTile contains essentialy 3 informations: +// - its Walkability +// - its altitude (distance from the nearest non walkable MiniTile, except those which are part of small enough zones (lakes)) +// - the id of the Area it is part of, if ever. +// The whole process of analysis of a Map relies on the walkability information +// from which are derived successively : altitudes, Areas, ChokePoints. + +class MiniTile +{ +public: + // Corresponds approximatively to BWAPI::isWalkable + // The differences are: + // - For each BWAPI's unwalkable MiniTile, we also mark its 8 neighbours as not walkable. + // According to some tests, this prevents from wrongly pretending one small unit can go by some thin path. + // - The relation buildable ==> walkable is enforced, by marking as walkable any MiniTile part of a buildable Tile (Cf. Tile::Buildable) + // Among the MiniTiles having Altitude() > 0, the walkable ones are considered Terrain-MiniTiles, and the other ones Lake-MiniTiles. + bool Walkable() const { return m_areaId != 0; } + + // Distance in pixels between the center of this MiniTile and the center of the nearest Sea-MiniTile + // Sea-MiniTiles all have their Altitude() equal to 0. + // MiniTiles having Altitude() > 0 are not Sea-MiniTiles. They can be either Terrain-MiniTiles or Lake-MiniTiles. + altitude_t Altitude() const { return m_altitude; } + + // Sea-MiniTiles are unwalkable MiniTiles that have their Altitude() equal to 0. + bool Sea() const { return m_altitude == 0; } + + // Lake-MiniTiles are unwalkable MiniTiles that have their Altitude() > 0. + // They form small zones (inside Terrain-zones) that can be eaysily walked around (e.g. Starcraft's doodads) + // The intent is to preserve the continuity of altitudes inside Areas. + bool Lake() const { return (m_altitude != 0) && !Walkable(); } + + // Terrain MiniTiles are just walkable MiniTiles + bool Terrain() const { return Walkable(); } + + // For Sea and Lake MiniTiles, returns 0 + // For Terrain MiniTiles, returns a non zero id: + // - if (id > 0), id uniquely identifies the Area A that contains this MiniTile. + // Moreover we have: A.Id() == id and Map::GetArea(id) == A + // For more information about positive Area::ids, see Area::Id() + // - if (id < 0), then this MiniTile is part of a Terrain-zone that was considered too small to create an Area for it. + // Note: negative Area::ids start from -2 + // Note: because of the lakes, Map::GetNearestArea should be prefered over Map::GetArea. + Area::id AreaId() const { return m_areaId; } + + +//////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + void SetWalkable(bool walkable) { m_areaId = (walkable ? -1 : 0); m_altitude = (walkable ? -1 : 1); } + bool SeaOrLake() const { return m_altitude == 1; } + void SetSea() { bwem_assert(!Walkable() && SeaOrLake()); m_altitude = 0; } + void SetLake() { bwem_assert(!Walkable() && Sea()); m_altitude = -1; } + bool AltitudeMissing() const { return m_altitude == -1; } + void SetAltitude(altitude_t a) { bwem_assert_debug_only(AltitudeMissing() && (a > 0)); m_altitude = a; } + bool AreaIdMissing() const { return m_areaId == -1; } + void SetAreaId(Area::id id) { bwem_assert(AreaIdMissing() && (id >= 1)); m_areaId = id; } + void ReplaceAreaId(Area::id id) { bwem_assert((m_areaId > 0) && ((id >= 1) || (id <= -2)) && (id != m_areaId)); m_areaId = id; } + void SetBlocked() { bwem_assert(AreaIdMissing()); m_areaId = blockingCP; } + bool Blocked() const { return m_areaId == blockingCP; } + void ReplaceBlockedAreaId(Area::id id) { bwem_assert((m_areaId == blockingCP) && (id >= 1)); m_areaId = id; } + +private: + altitude_t m_altitude = -1; // 0 for seas ; != 0 for terrain and lakes (-1 = not computed yet) ; 1 = SeaOrLake intermediate value + Area::id m_areaId = -1; // 0 -> unwalkable ; > 0 -> index of some Area ; < 0 -> some walkable terrain, but too small to be part of an Area + static const Area::id blockingCP; +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Tile +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Corresponds to BWAPI/Starcraft's concept of tile (32x32 pixels). +// Tiles are accessed using TilePositions (Cf. Map::GetTile). +// A Map holds Map::Size().x * Map::Size().y Tiles as its "Tile map". +// +// It should be noted that a Tile exactly overlaps 4 x 4 MiniTiles. +// As there are 16 times as many MiniTiles as Tiles, we allow a Tiles to contain more data than MiniTiles. +// As a consequence, Tiles should be preferred over MiniTiles, for efficiency. +// The use of Tiles is further facilitated by some functions like Tile::AreaId or Tile::MinAltitude +// which somewhat aggregate the MiniTile's corresponding information +// +// Tiles inherit utils::Markable, which provides marking ability +// Tiles inherit utils::UserData, which provides free-to-use data. + +class Tile : public utils::Markable, public utils::UserData +{ +public: + // Corresponds to BWAPI::isBuildable + // Note: BWEM enforces the relation buildable ==> walkable (Cf. MiniTile::Walkable) + bool Buildable() const { return m_bits.buildable; } + + // Tile::AreaId() somewhat aggregates the MiniTile::AreaId() values of the 4 x 4 sub-MiniTiles. + // Let S be the set of MiniTile::AreaId() values for each walkable MiniTile in this Tile. + // If empty(S), returns 0. Note: in this case, no contained MiniTile is walkable, so all of them have their AreaId() == 0. + // If S = {a}, returns a (whether positive or negative). + // If size(S) > 1 returns -1 (note that -1 is never returned by MiniTile::AreaId()). + Area::id AreaId() const { return m_areaId; } + + // Tile::MinAltitude() somewhat aggregates the MiniTile::Altitude() values of the 4 x 4 sub-MiniTiles. + // Returns the minimum value. + altitude_t MinAltitude() const { return m_minAltitude; } + + // Tells if at least one of the sub-MiniTiles is Walkable. + bool Walkable() const { return m_areaId != 0; } + + // Tells if at least one of the sub-MiniTiles is a Terrain-MiniTile. + bool Terrain() const { return Walkable(); } + + // 0: lower ground 1: high ground 2: very high ground + // Corresponds to BWAPI::getGroundHeight / 2 + int GroundHeight() const { return m_bits.groundHeight; } + + // Tells if this Tile is part of a doodad. + // Corresponds to BWAPI::getGroundHeight % 2 + bool Doodad() const { return m_bits.doodad; } + + // If any Neutral occupies this Tile, returns it (note that all the Tiles it occupies will then return it). + // Otherwise, returns nullptr. + // Neutrals are Minerals, Geysers and StaticBuildings (Cf. Neutral). + // In some maps (e.g. Benzene.scx), several Neutrals are stacked at the same location. + // In this case, only the "bottom" one is returned, while the other ones can be accessed using Neutral::NextStacked(). + // Because Neutrals never move on the Map, the returned value is guaranteed to remain the same, unless some Neutral + // is destroyed and BWEM is informed of that by a call of Map::OnMineralDestroyed(BWAPI::Unit u) for exemple. In such a case, + // BWEM automatically updates the data by deleting the Neutral instance and clearing any reference to it such as the one + // returned by Tile::GetNeutral(). In case of stacked Neutrals, the next one is then returned. + Neutral * GetNeutral() const { return m_pNeutral; } + + // Returns the number of Neutrals that occupy this Tile (Cf. GetNeutral). + int StackedNeutrals() const; + +//////////////////////////////////////////////////////////////////////////// +// Details: The functions below are used by the BWEM's internals + + void SetBuildable() { m_bits.buildable = 1; } + void SetGroundHeight(int h) { bwem_assert((0 <= h) && (h <= 2)); m_bits.groundHeight = h; } + void SetDoodad() { m_bits.doodad = 1; } + void AddNeutral(Neutral * pNeutral) { bwem_assert(!m_pNeutral && pNeutral); m_pNeutral = pNeutral; } + void SetAreaId(Area::id id) { bwem_assert((id == -1) || !m_areaId && id); m_areaId = id; } + void ResetAreaId() { m_areaId = 0; } + void SetMinAltitude(altitude_t a) { bwem_assert(a >= 0); m_minAltitude = a; } + void RemoveNeutral(Neutral * pNeutral){ bwem_assert(pNeutral && (m_pNeutral == pNeutral)); utils::unused(pNeutral); m_pNeutral = nullptr; } + int InternalData() const { return m_internalData; } + void SetInternalData(int data) const { m_internalData = data; } + + +private: + struct Bits + { + Bits() : buildable(0), groundHeight(0), doodad(0) {} + uint8_t buildable:1; + uint8_t groundHeight:2; + uint8_t doodad:1; + }; + + Neutral * m_pNeutral = nullptr; + altitude_t m_minAltitude; + Area::id m_areaId = 0; + mutable int m_internalData = 0; + Bits m_bits; +}; + +// Note: the following 4 functions may change in the future... +altitude_t minAltitudeTop(const BWAPI::TilePosition & tile, const Map & theMap); +altitude_t minAltitudeBottom(const BWAPI::TilePosition & tile, const Map & theMap); +altitude_t minAltitudeLeft(const BWAPI::TilePosition & tile, const Map & theMap); +altitude_t minAltitudeRight(const BWAPI::TilePosition & tile, const Map & theMap); + + + +namespace utils { + + +// Helpers for generic use of Tiles and MiniTiles (see Map::BreadthFirstSearch for a use case) + +template +struct TileOfPosition; + +template<> +struct TileOfPosition +{ + typedef Tile type; +}; + +template<> +struct TileOfPosition +{ + typedef MiniTile type; +}; + + + +template +struct PositionOfTile; + +template<> +struct PositionOfTile +{ + typedef BWAPI::TilePosition type; +}; + +template<> +struct PositionOfTile +{ + typedef BWAPI::WalkPosition type; +}; + + + +} // namespace utils + + + + +} // namespace BWEM + + +#endif + diff --git a/Source/BWEM/src/utils.cpp b/Source/BWEM/src/utils.cpp new file mode 100644 index 0000000..70e9588 --- /dev/null +++ b/Source/BWEM/src/utils.cpp @@ -0,0 +1,69 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + + + +#include "utils.h" +#include + +using namespace std; + +namespace BWEM { +namespace utils { + + +//ofstream Log("bwapi-data/write/log.txt"); + + +bool canWrite(const string & fileName) +{ + ofstream out(fileName); + out.close(); + return !out.fail(); +} + + + + +// From http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect + +// Returns true if the lines intersect, otherwise false. In addition, if the lines +// intersect the intersection point may be stored in i_x and i_y. +static bool get_line_intersection(double p0_x, double p0_y, double p1_x, double p1_y, + double p2_x, double p2_y, double p3_x, double p3_y, double *i_x = nullptr, double *i_y = nullptr) +{ + double s1_x, s1_y, s2_x, s2_y; + s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; + s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; + + double s, t; + s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); + t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); + + if (s >= 0 && s <= 1 && t >= 0 && t <= 1) + { + // Collision detected + if (i_x != nullptr) *i_x = p0_x + (t * s1_x); + if (i_y != nullptr) *i_y = p0_y + (t * s1_y); + return true; + } + + return false; // No collision +} + + +bool intersect(int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy) +{ + return get_line_intersection(ax, ay, bx, by, cx, cy, dx, dy); +} + + +}} // namespace BWEM::utils + diff --git a/Source/BWEM/src/utils.h b/Source/BWEM/src/utils.h new file mode 100644 index 0000000..d25a5be --- /dev/null +++ b/Source/BWEM/src/utils.h @@ -0,0 +1,278 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + + + +#ifndef BWEM_UTILS_H +#define BWEM_UTILS_H + + +#include +#include +#include +#include +#include +#include +#include "defs.h" + + +namespace BWEM { + + +namespace utils { + + + +//extern std::ofstream Log; + + + + +template void unused(const T &) {} + + + +inline int queenWiseNorm(int dx, int dy) +{ + return std::max(abs(dx), abs(dy)); +} + + +inline int squaredNorm(int dx, int dy) +{ + return dx*dx + dy*dy; +} + + +inline double norm(int dx, int dy) +{ + return sqrt(squaredNorm(dx, dy)); +} + + +inline int scalar_product(int ax, int ay, int bx, int by) +{ + return ax*bx + ay*by; +} + + +// Returns whether the line segments [a, b] and [c, d] intersect. +bool intersect(int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy); + + +template +std::string my_to_string(const T & value) +{ + std::ostringstream oss; + std::string res; + oss << value; + res = oss.str(); + return res; +} + + +template +T string_to_value(const std::string & s) +{ + std::istringstream iss(s); + T res; + iss >> res; + return res; +} + + +template +bool from_string(const std::string & s, T & dest) +{ + std::istringstream iss(s); + return !(iss >> dest).fail(); +} + + +bool canWrite(const std::string & fileName); + +// http://stackoverflow.com/questions/17224256/function-checking-if-an-integer-type-can-fit-a-value-of-possibly-different-inte +template +bool CanTypeFitValue(const U value) { + const intmax_t botT = intmax_t(std::numeric_limits::min() ); + const intmax_t botU = intmax_t(std::numeric_limits::min() ); + const uintmax_t topT = uintmax_t(std::numeric_limits::max() ); + const uintmax_t topU = uintmax_t(std::numeric_limits::max() ); + return !( (botT > botU && value < static_cast (botT)) || (topT < topU && value > static_cast (topT)) ); +} + + +// http://stackoverflow.com/questions/6942273/get-random-element-from-container-c-stl +template +I random_element(I begin, I end) +{ + const auto n = std::distance(begin, end); + const auto divisor = (RAND_MAX + 1) / n; + + typename std::remove_const::type k; + do { k = std::rand() / divisor; } while (k >= n); + + std::advance(begin, k); + return begin; +} + + +template +inline const typename T::value_type & random_element(const T & Container) +{ + const auto n = Container.size(); + const auto divisor = (RAND_MAX + 1) / n; + + typename std::remove_const::type k; + do { k = std::rand() / divisor; } while (k >= n); + + return Container[k]; +} + + +template +inline typename T::value_type & random_element(T & Container) +{ + return const_cast(random_element(static_cast(Container))); +} + + +template +inline void really_remove(T & Container, const typename T::value_type & Element) +{ + Container.erase(remove(Container.begin(), Container.end(), Element), Container.end()); +} + + +template +inline void really_remove_if(T & Container, Pred pred) +{ + Container.erase(remove_if(Container.begin(), Container.end(), pred), Container.end()); +} + + +template +inline bool contains(const T & Container, const E & Element) +{ + return find(Container.begin(), Container.end(), Element) != Container.end(); +} + + + + +template +inline void fast_erase(std::vector & Vector, int i) +{ + bwem_assert((0 <= i) && (i < (int)Vector.size())); + + std::swap(Vector[i], Vector.back()); + Vector.pop_back(); +} + + +struct compare2nd +{ + template + bool operator()(const T & a, const T & b) + { + return a.second < b.second; + } +}; + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Markable +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Provides efficient marking ability. +// +// Usage: class MyNode : (public) Markable {...}; +// +// Note: This implementation uses a static member. +// + +template +class Markable +{ +public: + typedef Mark mark_t; + + Markable() : m_lastMark(0) {} + + bool Marked() const { return m_lastMark == m_currentMark; } + void SetMarked() const { m_lastMark = m_currentMark; } + void SetUnmarked() const { m_lastMark = m_currentMark-1; } + static void UnmarkAll() { ++m_currentMark; } + +private: + mutable mark_t m_lastMark; + static mark_t m_currentMark; +}; + + +template +Mark Markable::m_currentMark = 0; + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class UserData +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// Provides free-to-use, intrusive data for several types of the BWEM library +// Despite their names and their types, they can be used for any purpose. +// + +class UserData +{ +public: + // Free use. + int Data() const { return m_data; } + void SetData(int data) const { m_data = data; } + + // Free use. + void * Ptr() const { return m_ptr; } + void SetPtr(void * p) const { m_ptr = p; } + + // Free use. + void * Ext() const { return m_ext; } + void SetExt(void * p) const { m_ext = p; } + +protected: + UserData() = default; + UserData(const UserData &) = default; + +private: + mutable void * m_ptr = nullptr; + mutable void * m_ext = nullptr; + mutable int m_data = 0; +}; + + + + + +}} // namespace BWEM::utils + + + + + + + + + +#endif + diff --git a/Source/BWEM/src/winutils.cpp b/Source/BWEM/src/winutils.cpp new file mode 100644 index 0000000..ebbcc7f --- /dev/null +++ b/Source/BWEM/src/winutils.cpp @@ -0,0 +1,60 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#include "winutils.h" +#if BWEM_USE_WINUTILS +#include + + +using namespace std; + +namespace BWEM { +namespace utils { + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Timer +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +double Timer::m_freq = 0.0; + + +Timer::Timer() +{ + if (m_freq == 0.0) // m_freq not initialized yet ? + { + LARGE_INTEGER li; + auto res = QueryPerformanceFrequency(&li); + bwem_assert(res); + unused(res); + m_freq = li.QuadPart / 1000.0; + } + + Reset(); +} + + +int64_t Timer::Now() const +{ + LARGE_INTEGER li; + auto res = QueryPerformanceCounter(&li); + bwem_assert(res); + unused(res); + return li.QuadPart; +} + + + +}} // namespace BWEM::utils + +#endif // BWEM_USE_WINUTILS + diff --git a/Source/BWEM/src/winutils.h b/Source/BWEM/src/winutils.h new file mode 100644 index 0000000..9927773 --- /dev/null +++ b/Source/BWEM/src/winutils.h @@ -0,0 +1,57 @@ +////////////////////////////////////////////////////////////////////////// +// +// This file is part of the BWEM Library. +// BWEM is free software, licensed under the MIT/X11 License. +// A copy of the license is provided with the library in the LICENSE file. +// Copyright (c) 2015, 2017, Igor Dimitrijevic +// +////////////////////////////////////////////////////////////////////////// + + +#ifndef BWEM_WINUTILS_H +#define BWEM_WINUTILS_H + +#include "defs.h" +#if BWEM_USE_WINUTILS + +#include "utils.h" + +namespace BWEM { +namespace utils { + + + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// class Timer +// // +////////////////////////////////////////////////////////////////////////////////////////////// +// +// High resolution timer - uses Windows's QueryPerformanceCounter +// + +class Timer +{ +public: + Timer(); // calls Reset() + + void Reset() { m_start = Now(); } + double ElapsedMilliseconds() const { return (Now() - m_start) / m_freq; } + +private: + int64_t Now() const; + + int64_t m_start; + + static double m_freq; // counts per millisecond +}; + + +}} // namespace BWEM::utils + + +#endif // BWEM_USE_WINUTILS + + +#endif + diff --git a/Source/BuildingPlacement.cpp b/Source/BuildingPlacement.cpp new file mode 100644 index 0000000..9fb45f3 --- /dev/null +++ b/Source/BuildingPlacement.cpp @@ -0,0 +1,97 @@ +#include "BuildingPlacement.h" + +namespace { auto & map = BWEM::Map::Instance(); } + +//@TODO save a target build position with each sunken/spore colony +// so that each base gets the right number of each +//@TODO also make sure sunkens don't block off the natural +TilePosition BuildingPlacement::getPosition(BWAPI::UnitType unit) +{ + TilePosition tp = Macro::players.at(Macro::selfID).bases.front().tilePosition; + if (unit == UnitTypes::Zerg_Sunken_Colony && Macro::players.at(Macro::selfID).bases.size() > 1) + { + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + if (Macro::players.at(Macro::selfID).bases.at(i).sunkenCount + + Macro::players.at(Macro::selfID).bases.at(i).creepColonyCount + < Macro::players.at(Macro::selfID).bases.at(i).desiredSunkenCount()) + tp = Macro::players.at(Macro::selfID).bases.at(i).defenseLocation; + } + } + else if (unit == UnitTypes::Zerg_Spore_Colony && Macro::players.at(Macro::selfID).bases.size() > 1) + { + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + if (Macro::players.at(Macro::selfID).bases.at(i).sporeCount + + Macro::players.at(Macro::selfID).bases.at(i).creepColonyCount + < Macro::players.at(Macro::selfID).bases.at(i).desiredSporeCount()) + tp = Macro::players.at(Macro::selfID).bases.at(i).defenseLocation; + } + } + else if (unit == UnitTypes::Zerg_Extractor) + { + for (auto b : Macro::players.at(Macro::selfID).bases) + { + if (b.gasPatches > b.extractorsBuilt()) + { + for (auto unit : BWAPI::Broodwar->getNeutralUnits()) + { + if ((unit->getType() == BWAPI::UnitTypes::Resource_Vespene_Geyser) + && unit->getDistance(b.base->getPosition()) < 400) + { + tp = unit->getTilePosition(); + } + } + //tp = b.base->getClosestUnit(UnitTypes::Resource_Vespene_Geyser)->getTilePosition(); + } + } + } + else + { + tp = Broodwar->getBuildLocation(unit, Macro::players.at(Macro::selfID).bases.front().tilePosition); + } + + checkBounds(tp); + return tp; +} + +bool BuildingPlacement::build(CUnit unit) +{ + bool built = false; + int buildingRange = 4; + for (int x = -buildingRange; x <= buildingRange; x++) + { + for (int y = -buildingRange; y <= buildingRange; y++) + { + TilePosition tp = TilePosition(unit.target.x + x, unit.target.y + y); + if (!(tp == Macro::lastBuildLocation) + && unit.unit->build(unit.targetUnit, tp)) + { + built = true; + break; + } + } + } + return built; +} + +TilePosition BuildingPlacement::checkBounds(TilePosition tp) +{ + if (tp.x < 1) tp.x = 1; + if (tp.y < 1) tp.y = 1; + if (tp.x > map.Size().x) tp.x = map.Size().x - 2; + if (tp.y > map.Size().y) tp.y = map.Size().y - 2; + return tp; +} + +std::vector BuildingPlacement::getDirection(TilePosition tp1, TilePosition tp2) +{ + double deltaX = tp1.x - tp2.x; + double deltaY = tp1.y - tp2.y; + double slope = sqrt(pow(deltaX, 2.0) + pow(deltaY, 2.0)); + + std::vector vector; + vector.push_back((deltaX / slope)); + vector.push_back((deltaY / slope)); + return vector; +} \ No newline at end of file diff --git a/Source/BuildingPlacement.h b/Source/BuildingPlacement.h new file mode 100644 index 0000000..7c3f676 --- /dev/null +++ b/Source/BuildingPlacement.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "CUnit.h" +#include "Macro.h" + +using namespace BWAPI; + +static const double x_vector = 0; +static const double y_vector = 1; + +class CUnit; + +class BuildingPlacement +{ +public: + static bool build(CUnit unit); + static TilePosition checkBounds(TilePosition tp); + static std::vector getDirection(TilePosition tp1, TilePosition tp2); + static TilePosition getPosition(BWAPI::UnitType unit); +}; \ No newline at end of file diff --git a/Source/CBase.cpp b/Source/CBase.cpp new file mode 100644 index 0000000..4c9ad9c --- /dev/null +++ b/Source/CBase.cpp @@ -0,0 +1,327 @@ +#include "CBase.h" + +namespace { auto & map = BWEM::Map::Instance(); } + +CBase::CBase(BWAPI::TilePosition tp) +{ + this->area = map.GetArea(tp); + this->tilePosition = tp; + this->position = Position(tp.x * 32, tp.y * 32); + this->initialize(); +} + +CBase::CBase(BWAPI::Unit unit) +{ + this->base = unit; + this->tilePosition = unit->getTilePosition(); + this->position = unit->getPosition(); + this->initialize(); + this->area = map.GetArea(this->tilePosition); +} + +CBase::CBase() +{ +} + +void CBase::addWorker(std::string resource) +{ + if (resource == "gas") + { + this->gasWorkers++; + } + if (resource == "minerals") + { + this->mineralWorkers++; + } +} + +int CBase::desiredSporeCount() +{ + int count = 1; + if (!Macro::enemyIDs.empty()) + { + if (Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Spire) > 0) count++; + count += ceil(Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Starport) / 2.0); + count += ceil(Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Stargate) / 2.0); + count += Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Mutalisk) / 5; + } + return 1; +} + +// things to consider - +// how many resources are left at this base +// if it's a main (has tech buildings) +// if it's a natural (leads to a main) +int CBase::desiredSunkenCount() +{ + if (this->exposedToCenter && Broodwar->getFrameCount() < 600 * 24) return 5; + else if (this->exposedToCenter) return 2; + else return 0; +} + +int CBase::extractorsBuilt() +{ + int count = 0; + for (auto u : Macro::players.at(Macro::selfID).units) + { + if ((u.unit->getType().isRefinery()) && + u.unit->getDistance(this->position) < 400) + { + count++; + } + } + return count; +} + +void CBase::getDefense() +{ + this->creepColonyCount = 0; + this->sunkenCount = 0; + this->sporeCount = 0; + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (u.unit->getType() == UnitTypes::Zerg_Sunken_Colony) + { + if (u.unit->getDistance(this->position) < 400) this->sunkenCount++; + } + else if (u.unit->getType() == UnitTypes::Zerg_Spore_Colony) + { + if (u.unit->getDistance(this->position) < 400) this->sporeCount++; + } + else if (u.unit->getType() == UnitTypes::Zerg_Creep_Colony) + { + if (u.unit->getDistance(this->position) < 400) this->creepColonyCount++; + } + } + + /*Broodwar->registerEvent( + [this](Game*) { + Broodwar->drawTextMap(Position(this->position.x, this->position.y + 20), "creep count = %d", this->creepColonyCount); + }, + nullptr, + 23);*/ +} + +void CBase::getResources() +{ + if (this->base->isCompleted()) + { + this->mineralPatches = 0; + this->gasPatches = 0; + this->gasAmount = 0; + this->minsAmount = 0; + for (auto unit : BWAPI::Broodwar->getNeutralUnits()) + { + if ((unit->getType() == BWAPI::UnitTypes::Resource_Vespene_Geyser || unit->getType().isRefinery()) && + unit->getPosition().isValid() && + unit->getDistance(this->position) < 400) + { + this->gasPatches++; + this->gasAmount += unit->getInitialResources(); + } + + if (unit->getType() == BWAPI::UnitTypes::Resource_Mineral_Field && + unit->getPosition().isValid() && + unit->getDistance(this->position) < 400) + { + this->mineralPatches++; + this->minsAmount += unit->getInitialResources(); + } + } + } +} + +void CBase::initialize() +{ + this->complete = false; + this->creepColonyCount = 0; + this->exposedToCenter = this->isExposedToCenter(); + this->gasPatches = 0; + this->gasWorkers = 0; + this->gasAmount = 0; + this->lastAttack = 0; + this->mineralPatches = 0; + this->mineralWorkers = 0; + this->minsAmount = 0; + this->setDefenseLocation(); + this->sporeCount = 0; + this->startLocation = isStartLocation(); + this->sunkenCount = 0; + this->initializeResources(); +} + +// initialize data for this CBase +void CBase::initializeResources() +{ + this->mineralPatches = 0; + this->gasPatches = 0; + this->gasAmount = 0; + this->minsAmount = 0; + for (const BWEM::Area& a : map.Areas()) + { + for (const BWEM::Base& b : a.Bases()) + { + if (b.Location() == this->tilePosition) + { + this->gasPatches = b.Geysers().size(); + this->mineralPatches = b.Minerals().size(); + for (auto g : b.Geysers()) + { + this->gasAmount += g->Amount(); + } + for (auto m : b.Minerals()) + { + this->minsAmount += m->Amount(); + } + } + } + } +} + +// if a base doesn't have any other bases between it +// and the center of the map, then it's easier to attack +bool CBase::isExposedToCenter() +{ + const BWEM::Area* area = map.GetArea(this->tilePosition); + + std::pair target = Util::getTargetArea(area, this->startLocation); + const BWEM::Area* targetArea = target.first; + bool foundTarget = target.second; + + if (foundTarget) + { + // get a path from the target Area to the player's starting base. + // if an Area has more than two neighbors, it's not part of a + // straight-line path and probably exposed to the center. + // if it contains a base, then the target Area is not + // exposed to the center + int count = 0; + for (auto a : Util::getPath(area, targetArea)) + { + if (a->Id() == area->Id()) continue; + if (a->AccessibleNeighbours().size() > 2) return true; + if (a->Bases().size() > 0) + { + return false; + } + count++; + } + } + else + { + Broodwar << "Couldn't find target area in CBase::isExposedToCenter" << std::endl; + } + + return true; +} + +// determines if base is owned by someone +bool CBase::isOwned() +{ + for (size_t i = 0; i < Macro::players.size(); i++) + { + for (size_t j = 0; j < Macro::players.at(i).bases.size(); j++) + { + int x = Macro::players.at(i).bases.at(j).tilePosition.x; + int y = Macro::players.at(i).bases.at(j).tilePosition.y; + if (this->tilePosition.x == x && this->tilePosition.y == y) return true; + } + } + return false; +} + +bool CBase::isSaturated(std::string resource) +{ + if (resource == "gas") + { + return this->gasWorkers >= this->extractorsBuilt() * 3; + } + else + { + return this->mineralWorkers >= this->mineralPatches * 2; + } +} + +bool CBase::isStartLocation() +{ + for (auto sl : Broodwar->getStartLocations()) + { + if (TilePosition(sl) == this->tilePosition) return true; + } + return false; +} + +//@TODO clean this up +// find a spot to put static defense by. +// if this is a main base, build around the hatchery. +// otherwise, build pointed toward another start location +void CBase::setDefenseLocation() +{ + const BWEM::Area* area = map.GetArea(this->tilePosition); + TilePosition tp = this->tilePosition; + + std::pair target = Util::getTargetArea(area, this->startLocation); + const BWEM::Area* targetArea = target.first; + bool foundTarget = target.second; + + if (foundTarget) + { + std::pair neighbor = Util::getNeighborArea(area, targetArea); + const BWEM::Area* neighborArea = neighbor.first; + bool foundNeighbor = neighbor.second; + if (foundNeighbor) + { + TilePosition average = Military::getChokepointCenter(area, neighborArea); + + // this caused an out-of-range exception in scouting.cpp: + // std::vector direction = BuildingPlacement::getDirection(Macro::players.at(Macro::selfID).bases.at(1).tilePosition); + std::vector direction = BuildingPlacement::getDirection(this->tilePosition, average); + + tp.x += direction.at(x_vector) * -5; + tp.y += direction.at(y_vector) * -5; + } + else + { + Broodwar << "Couldn't find neighbor area in CBase::setDefenseLocation()" << std::endl; + Broodwar << targetArea->TopLeft() << std::endl; + } + } + else + { + //Broodwar << area->TopLeft() << std::endl; + Broodwar << "Couldn't find target area in CBase::setDefenseLocation()" << std::endl; + } + this->defenseLocation = tp; +} + +void CBase::subtractWorker(std::string resource) +{ + if (resource == "gas") + { + this->gasWorkers--; + } + if (resource == "minerals") + { + this->mineralWorkers--; + } +} + +// get number of tech buildings at this base +int CBase::techCount() +{ + std::vector techBuildingTypes = { + UnitTypes::Zerg_Spawning_Pool, UnitTypes::Zerg_Hydralisk_Den, + UnitTypes::Zerg_Evolution_Chamber, UnitTypes::Zerg_Spire, + UnitTypes::Zerg_Queens_Nest, UnitTypes::Zerg_Greater_Spire, + UnitTypes::Zerg_Ultralisk_Cavern, UnitTypes::Zerg_Defiler_Mound + }; + int nearbyTech = 0; + + for (auto u : Macro::players.at(Macro::selfID).units) + { + if ((std::find(techBuildingTypes.begin(), techBuildingTypes.end(), u.unit->getType()) != techBuildingTypes.end()) + && u.unit->getDistance(this->position) < 32 * 15) + nearbyTech++; + } + return nearbyTech; +} \ No newline at end of file diff --git a/Source/CBase.h b/Source/CBase.h new file mode 100644 index 0000000..bbf6bbe --- /dev/null +++ b/Source/CBase.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include "KoraBot.h" +#include "Macro.h" + +using namespace BWAPI; +using namespace Filter; + +class CBase +{ +public: + CBase(BWAPI::TilePosition tp); + CBase(BWAPI::Unit unit); + CBase(); + const BWEM::Area* area; + bool complete; + int completedTime; + int creepColonyCount; + BWAPI::TilePosition defenseLocation; + bool exposedToCenter; + int lastAttack; + int lastSeen; + int mineralWorkers; + int mineralPatches; + int minsAmount; + int gasWorkers; + int gasPatches; + int gasAmount; + int originalMinerals; + int originalGas; + BWAPI::Position position; + bool startLocation; + int sporeCount; + int sunkenCount; + BWAPI::TilePosition tilePosition; + BWAPI::Unit base; + + // functions + void addWorker(std::string resource); + int desiredSporeCount(); + int desiredSunkenCount(); + int extractorsBuilt(); + void getDefense(); + void getResources(); + void initialize(); + void initializeResources(); + bool isExposedToCenter(); + bool isOwned(); + bool isSaturated(std::string resource); + bool isStartLocation(); + void setDefenseLocation(); + void subtractWorker(std::string resource); + int techCount(); +}; diff --git a/Source/CMap.cpp b/Source/CMap.cpp new file mode 100644 index 0000000..c292532 --- /dev/null +++ b/Source/CMap.cpp @@ -0,0 +1,18 @@ +#include "CMap.h" + +namespace { auto & map = BWEM::Map::Instance(); } + +// gets the TilePosition of the center of an area +TilePosition CMap::getAreaAverage(const BWEM::Area* area) +{ + int xAverage = (area->TopLeft().x + area->BottomRight().x) / 2; + int yAverage = (area->TopLeft().y + area->BottomRight().y) / 2; + return TilePosition(xAverage, yAverage); +} + +TilePosition CMap::getAreaAverage(const BWEM::Area area) +{ + int xAverage = (area.TopLeft().x + area.BottomRight().x) / 2; + int yAverage = (area.TopLeft().y + area.BottomRight().y) / 2; + return TilePosition(xAverage, yAverage); +} \ No newline at end of file diff --git a/Source/CMap.h b/Source/CMap.h new file mode 100644 index 0000000..e8b149e --- /dev/null +++ b/Source/CMap.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Macro.h" + +using namespace BWAPI; + +class CBase; +class CUnit; + +class CMap +{ + public: + static TilePosition getAreaAverage(const BWEM::Area* area); + static TilePosition getAreaAverage(const BWEM::Area area); +}; \ No newline at end of file diff --git a/Source/CPlayer.cpp b/Source/CPlayer.cpp new file mode 100644 index 0000000..59e5b20 --- /dev/null +++ b/Source/CPlayer.cpp @@ -0,0 +1,14 @@ +#include "CPlayer.h" + +CPlayer::CPlayer() +{ +} + +CPlayer::CPlayer(BWAPI::Player player) +{ + this->gasLost = 0; + this->gasSpent = 0; + this->mineralsLost = 0; + this->mineralsSpent = 0; + this->player = player; +} \ No newline at end of file diff --git a/Source/CPlayer.h b/Source/CPlayer.h new file mode 100644 index 0000000..55dec59 --- /dev/null +++ b/Source/CPlayer.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" + +using namespace BWAPI; + +class CBase; +class CUnit; + +class CPlayer +{ +public: + CPlayer(BWAPI::Player player); + CPlayer(); + + std::vector bases; + int gasLost; + int gasSpent; + int id; + int mineralsGathered; + int mineralsLost; + int mineralsSpent; + BWAPI::Player player; + std::vector units; +}; \ No newline at end of file diff --git a/Source/CUnit.cpp b/Source/CUnit.cpp new file mode 100644 index 0000000..4e3b8ae --- /dev/null +++ b/Source/CUnit.cpp @@ -0,0 +1,50 @@ +#include "CUnit.h" + +CUnit::CUnit() +{ + this->initialize(); + this->id = -1; +} + +CUnit::CUnit(BWAPI::Unit unit) +{ + this->initialize(); + this->id = unit->getID(); + this->tilePosition = unit->getTilePosition(); + this->unit = unit; + this->unitType = unit->getType(); +} + +void CUnit::initialize() +{ + this->action = ""; + this->lastSeen = TilePosition(0, 0); + this->miningBase = -1; + this->target = TilePosition(-1, -1); +} + +bool CUnit::isIdle() +{ + bool status = false; + if (this->unit->getOrder() == Orders::PlayerGuard + && this->action != "expand" + && this->action != "build" + && this->action != "minerals") + status = true; + return status; +} + +bool CUnit::isInSquad() +{ + for (auto s : Macro::squads) + { + if (s.contains(this->unit)) return true; + } + return false; +} + +void CUnit::mine(std::string action, int baseIndex) +{ + this->action = action; + this->miningBase = baseIndex; +} \ No newline at end of file diff --git a/Source/CUnit.h b/Source/CUnit.h new file mode 100644 index 0000000..bb4f493 --- /dev/null +++ b/Source/CUnit.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Macro.h" + +using namespace BWAPI; +class CBase; +class Player; + +class CUnit +{ +public: + + CUnit(); + CUnit(BWAPI::Unit unit); + + std::string action; + void initialize(); + bool isIdle(); + bool isInSquad(); + int id; + BWAPI::TilePosition lastSeen; + void mine(std::string action, int baseIndex); + int miningBase; + int resourceType; + int resourceVal; + BWAPI::TilePosition target; + BWAPI::UnitType targetUnit; + TilePosition tilePosition; + BWAPI::Unit unit; + BWAPI::UnitType unitType; +}; diff --git a/Source/CheckItem.cpp b/Source/CheckItem.cpp new file mode 100644 index 0000000..b00ad55 --- /dev/null +++ b/Source/CheckItem.cpp @@ -0,0 +1,556 @@ +#include "CheckItem.h" + +void CheckItem::checkBases() +{ + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + int freed = 0; + Macro::players.at(Macro::selfID).bases.at(i).getResources(); + if (Macro::players.at(Macro::selfID).bases.at(i).mineralPatches * 2 + < Macro::players.at(Macro::selfID).bases.at(i).mineralWorkers) + { + for (size_t j = 0; j < Macro::players.at(Macro::selfID).units.size(); j++) + { + if (Macro::players.at(Macro::selfID).units.at(j).unit->getType() == UnitTypes::Zerg_Drone && freed < 2) + { + if (Macro::players.at(Macro::selfID).units.at(j).miningBase == i) + { + Macro::freeUnit(j); + freed++; + } + } + } + } + } +} + +void CheckItem::checkEvoChamber() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) == 0 + && Broodwar->self()->supplyUsed() >= 60 && !Macro::queueHas(UnitTypes::Zerg_Evolution_Chamber)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Evolution_Chamber, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkGas() +{ + if (Broodwar->self()->supplyUsed() >= 30) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Extractor) < 1 + && !Macro::queueHas(UnitTypes::Zerg_Extractor)) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Extractor, false, 0.0)); + } + } + if (Broodwar->self()->supplyUsed() >= 80) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Extractor) < 2 + && !Macro::queueHas(UnitTypes::Zerg_Extractor)) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Extractor, false, 0.0)); + } + } + if (Broodwar->self()->supplyUsed() >= 120) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Extractor) < 3 + && !Macro::queueHas(UnitTypes::Zerg_Extractor)) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Extractor, false, 0.0)); + } + } +} + +void CheckItem::checkGroundArmor() +{ + bool chk = false; + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Carapace) == 0) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Carapace) + && Broodwar->getFrameCount() > 480 * 24) + chk = true; + } + else if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Carapace) == 1) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Carapace)) + chk = true; + } + else if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Carapace) == 2) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Carapace)) + chk = true; + } + + if (chk) + { + QueueEntry qe = QueueEntry(UpgradeTypes::Zerg_Carapace, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkHive() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) == 0 + && Util::completedBuilding(UnitTypes::Zerg_Queens_Nest) + && !Macro::queueHas(UnitTypes::Zerg_Hive)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Hive, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkHydraDen() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hydralisk_Den) == 0 + && Broodwar->self()->supplyUsed() >= 40 && !Macro::queueHas(UnitTypes::Zerg_Hydralisk_Den)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Hydralisk_Den, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkHydralisk() +{ + if (Util::completedBuilding(UnitTypes::Zerg_Hydralisk_Den) + && !Macro::queueHas(UnitTypes::Zerg_Hydralisk)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Hydralisk, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkHydraRange() +{ + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Grooved_Spines) == 0 + && !Macro::queueHas(UpgradeTypes::Grooved_Spines) + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hydralisk_Den) >= 1 + && Broodwar->self()->supplyUsed() >= 80) + + { + Macro::queue.push_back(QueueEntry(UpgradeTypes::Grooved_Spines, false, 0.0)); + } +} + +void CheckItem::checkHydraSpeed() +{ + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Muscular_Augments) == 0 + && !Macro::queueHas(UpgradeTypes::Muscular_Augments) + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hydralisk_Den) >= 1 + && Broodwar->self()->supplyUsed() >= 80) + + { + Macro::queue.push_back(QueueEntry(UpgradeTypes::Muscular_Augments, false, 0.0)); + } +} + +void CheckItem::checkLair() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) == 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) == 0 + && Util::completedBuilding(UnitTypes::Zerg_Spawning_Pool) + && Broodwar->self()->supplyUsed() >= 80 + && !Macro::queueHas(UnitTypes::Zerg_Lair)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Lair, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkLingSpeed() +{ + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Metabolic_Boost) == 0 + && !Macro::queueHas(UpgradeTypes::Metabolic_Boost) + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Spawning_Pool) >= 1 + && Broodwar->self()->supplyUsed() >= 80) + + { + Macro::queue.push_back(QueueEntry(UpgradeTypes::Metabolic_Boost, false, 0.0)); + } +} + +// things to consider: +// - how many hatcheries there currently are +// - how many resources are currently being brought in +void CheckItem::checkMacroHatch() +{ + if (!Macro::macroHatch) + { + /*if (Broodwar->elapsedTime() >= 300) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hatchery) <= 2) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Hatchery, false, 0.0)); + Macro::setMacroHatch(true); + } + }*/ + + int minWorkers = 0; + for (auto b : Macro::players.at(Macro::selfID).bases) + { + minWorkers += b.mineralWorkers; + } + + int minIncome = minWorkers * 55; + int larvaPerMin = Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hatchery) * 4; + int minSpent = larvaPerMin * 100; + + if (Broodwar->elapsedTime() >= 300 + && minIncome > minSpent + && !Macro::queueHas(UnitTypes::Zerg_Hatchery)) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Hatchery, false, 0.0)); + Macro::setMacroHatch(true); + } + } +} + +void CheckItem::checkMissile() +{ + bool chk = false; + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Missile_Attacks) == 0) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Missile_Attacks) + && Broodwar->getFrameCount() > 480 * 24) + chk = true; + } + else if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Missile_Attacks) == 1) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Missile_Attacks)) + chk = true; + } + else if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Zerg_Missile_Attacks) == 2) + { + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) > 0 + && !Macro::queueHas(UpgradeTypes::Zerg_Missile_Attacks)) + chk = true; + } + + if(chk) + { + QueueEntry qe = QueueEntry(UpgradeTypes::Zerg_Missile_Attacks, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkMutalisk() +{ + if (Util::completedBuilding(UnitTypes::Zerg_Spire) + && !Macro::queueHas(UnitTypes::Zerg_Mutalisk) + && Broodwar->elapsedTime() >= 300) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Mutalisk, false, 0.0); + Macro::queue.push_back(qe); + } +} + +// determine if a new base is needed +// things to consider: +// - how many resources are currently being brought in +void CheckItem::checkNewBase() +{ + if (!Macro::newBase) + { + if (Broodwar->elapsedTime() >= 180) + { + if (Macro::players.at(Macro::selfID).bases.size() == 1 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hatchery) < 2) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Hatchery, false, 0.0)); + Macro::setNewBase(true); + } + } + if (Broodwar->elapsedTime() >= 600) + { + if (Macro::players.at(Macro::selfID).bases.size() == 2 + && !Macro::newBase) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Hatchery, false, 0.0)); + Macro::setNewBase(true); + } + } + + int minPatches = 0; + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + minPatches += Macro::players.at(Macro::selfID).bases.at(i).mineralPatches; + } + + if (Macro::players.at(Macro::selfID).bases.size() >= 3 + && !Macro::newBase + && minPatches <= 22) + { + Macro::queue.push_back(QueueEntry(UnitTypes::Zerg_Hatchery, false, 0.0)); + Macro::setNewBase(true); + } + } +} + +void CheckItem::checkOverlordSpeed() +{ + if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Pneumatized_Carapace) == 0 + && !Macro::queueHas(UpgradeTypes::Pneumatized_Carapace) + && (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) >= 1 + || Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) >= 1)) + + { + Macro::queue.push_back(QueueEntry(UpgradeTypes::Pneumatized_Carapace, false, 0.0)); + } +} + +void CheckItem::checkQueensNest() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Queens_Nest) == 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) + Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) >= 1 + && Broodwar->self()->supplyUsed() >= 140 + && !Macro::queueHas(UnitTypes::Zerg_Queens_Nest)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Queens_Nest, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkSpawningPool() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Spawning_Pool) == 0 + && Broodwar->self()->supplyUsed() >= 26 + && !Macro::queueHas(UnitTypes::Zerg_Spawning_Pool)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Spawning_Pool, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkSpire() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Spire) == 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Lair) + Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) >= 1 + && Broodwar->self()->supplyUsed() >= 140 + && !Macro::queueHas(UnitTypes::Zerg_Spire)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Spire, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkSpore() +{ + int sporesNeeded = 0; + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + if (Macro::players.at(Macro::selfID).bases.at(i).sporeCount + + Macro::players.at(Macro::selfID).bases.at(i).creepColonyCount + < Macro::players.at(Macro::selfID).bases.at(i).desiredSporeCount()) + { + sporesNeeded += (Macro::players.at(Macro::selfID).bases.at(i).desiredSporeCount() - Macro::players.at(Macro::selfID).bases.at(i).sporeCount); + } + } + + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Evolution_Chamber) > 0 + && Macro::queueCount(UnitTypes::Zerg_Spore_Colony) < 2 + && Macro::queueCount(UnitTypes::Zerg_Spore_Colony) < sporesNeeded) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Spore_Colony, false, 0.0); + Macro::queue.push_back(qe); + } +} + +// things to consider: +// how many sunkens are needed +// how many have been built +void CheckItem::checkSunken() +{ + int sunkensNeeded = 0; + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + if (Macro::players.at(Macro::selfID).bases.at(i).sunkenCount + + Macro::players.at(Macro::selfID).bases.at(i).creepColonyCount + < Macro::players.at(Macro::selfID).bases.at(i).desiredSunkenCount()) + { + sunkensNeeded += (Macro::players.at(Macro::selfID).bases.at(i).desiredSunkenCount() + - Macro::players.at(Macro::selfID).bases.at(i).sunkenCount + - Macro::players.at(Macro::selfID).bases.at(i).creepColonyCount); + } + } + + sunkensNeeded -= Macro::queueCount(UnitTypes::Zerg_Sunken_Colony); + + if (Broodwar->self()->supplyUsed() >= 36 + && Macro::queueCount(UnitTypes::Zerg_Sunken_Colony) < 3 + && Macro::queueCount(UnitTypes::Zerg_Sunken_Colony) < sunkensNeeded) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Sunken_Colony, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkSupply() +{ + int currentSupply = Broodwar->self()->supplyUsed(); + int totalSupply = Broodwar->self()->supplyTotal() + (Util::eggCount(UnitTypes::Zerg_Overlord) * 16); + int availableSupply = totalSupply - currentSupply; + int hatchCount = Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hatchery); + + if (availableSupply <= (hatchCount * 4) + && !Macro::queueHas(UnitTypes::Zerg_Overlord) + && totalSupply < 400) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Overlord, false, 0.0); + Macro::queue.push_back(qe); + } +} + +//@TODO clean this up +void CheckItem::checkWorkers() +{ + // check if any bases are oversaturated, unassign extra workers + // check unassigned workers + // if a base doesn't have (mins*2) mineral miners, go there, same with gas + //@TODO use the built-in getUnits with a filter to reduce the size of this for loop + for (size_t i = 0; i < Macro::players.at(Macro::selfID).units.size(); i++) + { + //auto start = std::chrono::high_resolution_clock::now(); + if (Macro::players.at(Macro::selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Drone) + { + if (Macro::players.at(Macro::selfID).units.at(i).isIdle() + && Macro::players.at(Macro::selfID).units.at(i).action != "scout") + { + // if(resourceToMine()) == "minerals" + int base = Macro::findMiningBase(Macro::players.at(Macro::selfID).units.at(i).unit, "gas"); + if (base > -1) + { + if (Macro::players.at(Macro::selfID).units.at(i).unit->gather(Macro::players.at(Macro::selfID).bases.at(base).base->getClosestUnit(IsRefinery))) + { + Macro::players.at(Macro::selfID).units.at(i).mine("gas", base); + Macro::players.at(Macro::selfID).bases.at(base).gasWorkers++; + break; + } + } + + base = Macro::findMiningBase(Macro::players.at(Macro::selfID).units.at(i).unit, "minerals"); + if (base > -1 && Macro::players.at(Macro::selfID).bases.at(base).mineralPatches > 0) + { + if (Macro::players.at(Macro::selfID).units.at(i).unit->gather(Macro::players.at(Macro::selfID).bases.at(base).base->getClosestUnit(IsMineralField))) + { + Macro::players.at(Macro::selfID).units.at(i).mine("minerals", base); + Macro::players.at(Macro::selfID).bases.at(base).mineralWorkers++; + } + } + } + else if (Macro::players.at(Macro::selfID).units.at(i).action == "expand") + { + CBase target = CBase(Macro::players.at(Macro::selfID).units.at(i).target); + if (target.isOwned()) + { + CBase base = Util::getNextExpand(Macro::selfID); + Macro::players.at(Macro::selfID).units.at(i).target = base.tilePosition; + } + //@TODO make this more flexible in case something is blocking the build location + Macro::players.at(Macro::selfID).units.at(i).unit->build(UnitTypes::Zerg_Hatchery, Macro::players.at(Macro::selfID).units.at(i).target); + } + else if (Macro::players.at(Macro::selfID).units.at(i).action == "build") + { + //if (Military::likelihoodToAttack() < 1) + //if (Macro::players.at(Macro::selfID).units.at(i).targetUnit == UnitTypes::Zerg_Creep_Colony) + { + BuildingPlacement::build(Macro::players.at(Macro::selfID).units.at(i)); + } + /*else if (!Macro::players.at(Macro::selfID).units.at(i).unit->build(Macro::players.at(Macro::selfID).units.at(i).targetUnit, Macro::players.at(Macro::selfID).units.at(i).target)) + { + TilePosition target = Broodwar->getBuildLocation(Macro::players.at(Macro::selfID).units.at(i).targetUnit, Macro::players.at(Macro::selfID).bases.front().tilePosition, 20, true); + Macro::players.at(Macro::selfID).units.at(i).target = target; + + if (Broodwar->self()->minerals() > Macro::players.at(Macro::selfID).units.at(i).targetUnit.mineralPrice() - 10 + && Macro::players.at(Macro::selfID).units.at(i).unit->getOrder() != Orders::PlaceBuilding) + Macro::players.at(Macro::selfID).units.at(i).unit->move(Position(Macro::players.at(Macro::selfID).units.at(i).target)); + }*/ + } + else if(Macro::players.at(Macro::selfID).units.at(i).action == "scout") + { + if (Macro::players.at(Macro::selfID).units.at(i).unit->isIdle() && Macro::workerScout) + { + Macro::workerScout = false; + Macro::players.at(Macro::selfID).units.at(i).action = ""; + } + if (Macro::players.at(Macro::enemyIDs.front()).bases.size() > 0) + { + for (auto b : Macro::players.at(Macro::enemyIDs.front()).bases) + { + if(b.startLocation) Macro::players.at(Macro::selfID).units.at(i).unit->move(Macro::players.at(Macro::selfID).bases.front().position); + } + } + } + else if (Macro::players.at(Macro::selfID).units.at(i).action == "minerals") + { + int miningBase = Macro::players.at(Macro::selfID).units.at(i).miningBase; + if (miningBase > -1) + { + if (Macro::players.at(Macro::selfID).bases.at(miningBase).mineralWorkers > Macro::players.at(Macro::selfID).bases.at(miningBase).mineralPatches * 2) + { + Broodwar << " freeing unit" << std::endl; + Macro::freeUnit(i); + } + } + } + else if (Macro::players.at(Macro::selfID).units.at(i).action == "gas") + { + /*(int miningBase = Macro::players.at(Macro::selfID).units.at(i).miningBase; + if (miningBase > -1) + if (Macro::players.at(Macro::selfID).bases.at(miningBase).gasWorkers > Macro::players.at(Macro::selfID).bases.at(miningBase).gasPatches * 3) + { + Macro::freeUnit(i); + }*/ + } + } + //std::chrono::duration elapsed = std::chrono::high_resolution_clock::now() - start; + //if(elapsed.count() > 1) + //Broodwar << " time = " << elapsed.count() << " " << Macro::players.at(Macro::selfID).units.at(i).action << std::endl; + } + + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Drone) < 60 && !Macro::queueHas(UnitTypes::Zerg_Drone)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Drone, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkUltraCavern() +{ + if (Util::countUnits(Macro::selfID, UnitTypes::Zerg_Ultralisk_Cavern) == 0 + && Util::countUnits(Macro::selfID, UnitTypes::Zerg_Hive) == 1 + && !Macro::queueHas(UnitTypes::Zerg_Ultralisk_Cavern)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Ultralisk_Cavern, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkUltralisk() +{ + if (Util::completedBuilding(UnitTypes::Zerg_Ultralisk_Cavern) + && !Macro::queueHas(UnitTypes::Zerg_Ultralisk)) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Ultralisk, false, 0.0); + Macro::queue.push_back(qe); + } +} + +void CheckItem::checkZergling() +{ + if (Util::completedBuilding(UnitTypes::Zerg_Spawning_Pool) + && !Macro::queueHas(UnitTypes::Zerg_Zergling) + && Broodwar->elapsedTime() >= 180) + { + QueueEntry qe = QueueEntry(UnitTypes::Zerg_Zergling, false, 0.0); + Macro::queue.push_back(qe); + } +} \ No newline at end of file diff --git a/Source/CheckItem.h b/Source/CheckItem.h new file mode 100644 index 0000000..260d851 --- /dev/null +++ b/Source/CheckItem.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Macro.h" +#include "Util.h" + +using namespace BWAPI; + +class CheckItem +{ +public: + static void checkBases(); + static void checkEvoChamber(); + static void checkGas(); + static void checkHive(); + static void checkHydraDen(); + static void checkHydralisk(); + static void checkMacroHatch(); + static void checkMutalisk(); + static void checkNewBase(); + static void checkQueensNest(); + static void checkSpawningPool(); + static void checkSpire(); + static void checkSpore(); + static void checkSunken(); + static void checkSupply(); + static void checkWorkers(); + static void checkUltraCavern(); + static void checkUltralisk(); + static void checkZergling(); + + static void checkHydraRange(); + static void checkHydraSpeed(); + static void checkLair(); + static void checkLingSpeed(); + static void checkOverlordSpeed(); + + static void checkGroundArmor(); + static void checkMissile(); +}; \ No newline at end of file diff --git a/Source/Dll.cpp b/Source/Dll.cpp new file mode 100644 index 0000000..a5c8aba --- /dev/null +++ b/Source/Dll.cpp @@ -0,0 +1,22 @@ +#include +#include + +#include "KoraBot.h" + +extern "C" __declspec(dllexport) void gameInit(BWAPI::Game* game) { BWAPI::BroodwarPtr = game; } +BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +extern "C" __declspec(dllexport) BWAPI::AIModule* newAIModule() +{ + return new KoraBot(); +} diff --git a/Source/EconLogic.cpp b/Source/EconLogic.cpp new file mode 100644 index 0000000..292f07d --- /dev/null +++ b/Source/EconLogic.cpp @@ -0,0 +1,20 @@ +#include "EconLogic.h" +#include + +using namespace BWAPI; +using namespace Filter; + + +EconLogic::EconLogic() +{ +} + + +EconLogic::~EconLogic() +{ +} + +void EconLogic::logic() +{ + //Broodwar << "Econ logic " << std::endl; +} \ No newline at end of file diff --git a/Source/EconLogic.h b/Source/EconLogic.h new file mode 100644 index 0000000..2df3eb0 --- /dev/null +++ b/Source/EconLogic.h @@ -0,0 +1,13 @@ +#pragma once +#include + +using namespace BWAPI; +using namespace Filter; + +class EconLogic +{ +public: + EconLogic(); + ~EconLogic(); + static void logic(); +}; diff --git a/Source/KoraBot.h b/Source/KoraBot.h new file mode 100644 index 0000000..811e1bc --- /dev/null +++ b/Source/KoraBot.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +#include "Scouting.h" +#include "CheckItem.h" +#include "Macro.h" +#include "Military.h" + +// Remember not to use "Broodwar" in any global class constructor! + +class KoraBot : public BWAPI::AIModule +{ +public: + // Virtual functions for callbacks, leave these as they are. + virtual void onStart(); + virtual void onEnd(bool isWinner); + virtual void onFrame(); + virtual void onSendText(std::string text); + virtual void onReceiveText(BWAPI::Player player, std::string text); + virtual void onPlayerLeft(BWAPI::Player player); + virtual void onNukeDetect(BWAPI::Position target); + virtual void onUnitDiscover(BWAPI::Unit unit); + virtual void onUnitEvade(BWAPI::Unit unit); + virtual void onUnitShow(BWAPI::Unit unit); + virtual void onUnitHide(BWAPI::Unit unit); + virtual void onUnitCreate(BWAPI::Unit unit); + virtual void onUnitDestroy(BWAPI::Unit unit); + virtual void onUnitMorph(BWAPI::Unit unit); + virtual void onUnitRenegade(BWAPI::Unit unit); + virtual void onSaveGame(std::string gameName); + virtual void onUnitComplete(BWAPI::Unit unit); + // Everything below this line is safe to modify. + + KoraBot(); +}; diff --git a/Source/Korabot.cpp b/Source/Korabot.cpp new file mode 100644 index 0000000..493ec97 --- /dev/null +++ b/Source/Korabot.cpp @@ -0,0 +1,418 @@ +#include "KoraBot.h" +#include "BWEM/src/bwem.h" +#include + +using namespace BWAPI; +using namespace Filter; +using namespace BWEM; +using namespace BWEM::BWAPI_ext; +using namespace BWEM::utils; + +namespace { auto& theMap = BWEM::Map::Instance(); } + +KoraBot::KoraBot() +{ +} + +void KoraBot::onStart() +{ + Broodwar->setLatCom(false); + // Hello World! + //Broodwar->sendText("Hello world!"); + + //self = custom::Player(Broodwar->self()->getID()); + // Print the map name. + // BWAPI returns std::string when retrieving a string, don't forget to add .c_str() when printing! + + // Broodwar << "The map is " << Broodwar->mapName() << "!" << std::endl; + + // Enable the UserInput flag, which allows us to control the bot and type messages. + Broodwar->enableFlag(Flag::UserInput); + + // Uncomment the following line and the bot will know about everything through the fog of war (cheat). + //Broodwar->enableFlag(Flag::CompleteMapInformation); + + // Set the command optimization level so that common commands can be grouped + // and reduce the bot's APM (Actions Per Minute). + Broodwar->setCommandOptimizationLevel(2); + + // Check if this is a replay + if (Broodwar->isReplay()) + { + // Announce the players in the replay + Broodwar << "The following players are in this replay:" << std::endl; + + // Iterate all the players in the game using a std:: iterator + Playerset players = Broodwar->getPlayers(); + for (auto p : players) + { + // Only print the player if they are not an observer + if (!p->isObserver()) + Broodwar << p->getName() << ", playing as " << p->getRace() << std::endl; + } + + } + else // if this is not a replay + { + // Retrieve you and your enemy's races. enemy() will just return the first enemy. + // If you wish to deal with multiple enemies then you must use enemies(). + /*if (Broodwar->enemy()) // First make sure there is an enemy + Broodwar << "The matchup is " << Broodwar->self()->getRace() << " vs " << Broodwar->enemy()->getRace() << std::endl;*/ + + // Retrieve you and your enemy's races. enemy() will just return the first enemy. + // If you wish to deal with multiple enemies then you must use enemies(). + //if (Broodwar->enemy()) // First make sure there is an enemy + // Broodwar << "The matchup is " << Broodwar->self()->getRace() << " vs " << Broodwar->enemy()->getRace() << std::endl; + + for (auto p : Broodwar->getPlayers()) + { + if (!p->isNeutral()) + { + + Macro::addPlayer(p); + if (p->getID() == Broodwar->self()->getID()) + { + Macro::setSelfID(p->getID()); + } + else if (p->isEnemy(Broodwar->self())) Macro::setEnemyID(p->getID()); + } + } + + try + { + theMap.Initialize(); + theMap.EnableAutomaticPathAnalysis(); + bool startingLocationsOK = theMap.FindBasesForStartingLocations(); + assert(startingLocationsOK); + + BWEM::utils::MapPrinter::Initialize(&theMap); + BWEM::utils::printMap(theMap); // will print the map into the file /bwapi-data/map.bmp + BWEM::utils::pathExample(theMap); // add to the printed map a path between two starting locations + + int count = 0; + for (const Area& area : theMap.Areas()) + { + for (const Base& base : area.Bases()) + { + Macro::addBase(base.Location()); + Macro::addLocation(base.Location(), base.Starting()); + } + } + Macro::addSquad(); + } + catch (const std::exception& e) + { + Broodwar << "EXCEPTION: " << e.what() << std::endl; + } + + Broodwar << "KoraBot 0.0" << std::endl; + } + +} + +void KoraBot::onEnd(bool isWinner) +{ + // Called when the game ends + if (isWinner) + { + // Log your win here! + } +} + +void KoraBot::onFrame() +{ + Broodwar->drawTextScreen(60, 0, "FPS: %d", Broodwar->getFPS()); + Broodwar->drawTextScreen(60, 20, "Average FPS: %f", Broodwar->getAverageFPS()); + + // Called once every game frame + try + { + // these work but i don't need them right now + //BWEM::utils::gridMapExample(theMap); + //BWEM::utils::drawMap(theMap); + + if (Broodwar->getFrameCount() > 5) + { + int count = 0; + + //for (auto s : Macro::scouts) + //{ + // Broodwar->drawTextScreen(200, 10 + (count * 20), "%d", s.unit->getID()); + // count++; + //} + Broodwar->drawTextScreen(200, 10, "likelihood: %f", Military::likelihoodToAttack()); + Broodwar->drawTextScreen(200, 30, "stop production: %d", Macro::stopProduction); + for (auto e : Macro::queue) + { + if(e.unit != UnitTypes::Unknown) + Broodwar->drawTextScreen(200, 50 + (count * 20), "%s: %d", e.unit.c_str(), e.inProgress); + else + Broodwar->drawTextScreen(200, 50 + (count * 20), "%s: %d", e.upgrade.c_str(), e.inProgress); + count++; + } + //for (auto unit : BWAPI::Broodwar->getNeutralUnits()) + //{ + //Broodwar->drawTextMap(unit->getPosition(), unit->getType().c_str()); + //} + //for (auto u : Macro::players.at(Macro::selfID).units) + //{ + // Broodwar->drawTextMap(u.unit->getPosition(), "%d", u.unit->getID()); + //} + //for (auto u : Broodwar->self()->getUnits()) + //{ + // Position p = Position(u->getPosition().x - 10, u->getPosition().y - 10); + // Broodwar->drawTextMap(p, "%d", u->getID()); + //} + //for (auto b : Macro::players.at(Macro::selfID).bases) + //{ + // Broodwar->drawTextMap(b.position, "%d", b.mineralPatches); + //} + } + } + catch (const std::exception& e) + { + Broodwar << "EXCEPTION: " << e.what() << std::endl; + } + + /// Return if the game is a replay or is paused + if (Broodwar->isReplay() || Broodwar->isPaused() || !Broodwar->self()) + return; + + // Prevent spamming by only running our onFrame once every number of latency frames. + // Latency frames are the number of frames before commands are processed. + if (Broodwar->getFrameCount() % Broodwar->getLatencyFrames() != 0) + return; + + // in the top right spawn of jade, exposted to center is false for the natural unless it's run later + if (Broodwar->getFrameCount() == 240 * 24) + { + for (unsigned int i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + Macro::players.at(Macro::selfID).bases.at(i).exposedToCenter = Macro::players.at(Macro::selfID).bases.at(i).isExposedToCenter(); + } + } + + // functions to be run about every eight seconds (191 frames, 192 frames is 8 seconds) + if (Broodwar->getFrameCount() % 191 == 0) + { + CheckItem::checkBases(); + CheckItem::checkEvoChamber(); + CheckItem::checkGas(); + CheckItem::checkGroundArmor(); + CheckItem::checkHive(); + CheckItem::checkHydraDen(); + CheckItem::checkHydraRange(); + CheckItem::checkHydraSpeed(); + CheckItem::checkLair(); + CheckItem::checkLingSpeed(); + CheckItem::checkMissile(); + CheckItem::checkOverlordSpeed(); + CheckItem::checkMacroHatch(); + CheckItem::checkQueensNest(); + CheckItem::checkSpawningPool(); + CheckItem::checkSpire(); + CheckItem::checkSpore(); + CheckItem::checkSunken(); + CheckItem::checkUltraCavern(); + Macro::morphCreepColonies(); + Macro::setScouts(); + Military::checkAttack(); + Military::checkSquads(); + } + + // functions to be run about every two seconds + if (Broodwar->getFrameCount() % 47 == 0) + { + Scouting::scout(); + } + + // functions to be run about once a second + if (Broodwar->getFrameCount() % 23 == 0) + { + Military::checkDefense(); + CheckItem::checkNewBase(); + } + + // functions to be run every thirteenth frame (about twice a second) + if (Broodwar->getFrameCount() % 13 == 0) + { + if (Broodwar->getFrameCount() > 10) + { + CheckItem::checkMutalisk(); + CheckItem::checkHydralisk(); + CheckItem::checkSupply(); + //auto start = std::chrono::high_resolution_clock::now(); + CheckItem::checkWorkers(); + //std::chrono::duration elapsed = std::chrono::high_resolution_clock::now() - start; + CheckItem::checkUltralisk(); + CheckItem::checkZergling(); + + // start here - use this to check other function times + /*Broodwar->registerEvent( + [elapsed](Game*) { + Broodwar->drawTextScreen(Position(400, 10), "%f", elapsed.count()); + }, + nullptr, + 12);*/ + } + } + + // functions to be run every fifth frame (4.8 times a second) - unit production / macro analysis + if (Broodwar->getFrameCount() % 5 == 0) + { + if (Broodwar->getFrameCount() > 6) + { + Macro::analyzeQueue(); + //Macro::drawActions(); + } + } + + + if (Broodwar->getFrameCount() > 5) + { + //Macro::drawPlayers(); + } + // functions to be run every tick (24 times a second) - unit micro +} + +void KoraBot::onSendText(std::string text) +{ + BWEM::utils::MapDrawer::ProcessCommand(text); + + // Send the text to the game if it is not being processed. + Broodwar->sendText("%s", text.c_str()); + + // Make sure to use %s and pass the text as a parameter, + // otherwise you may run into problems when you use the %(percent) character! +} + +void KoraBot::onReceiveText(BWAPI::Player player, std::string text) +{ + // Parse the received text + //Broodwar << player->getName() << " said \"" << text << "\"" << std::endl; +} + +void KoraBot::onPlayerLeft(BWAPI::Player player) +{ + // Interact verbally with the other players in the game by + // announcing that the other player has left. + Broodwar->sendText("GG %s!", player->getName().c_str()); +} + +void KoraBot::onNukeDetect(BWAPI::Position target) +{ + + // Check if the target is a valid position + if (target) + { + // if so, print the location of the nuclear strike target + Broodwar << "Nuclear Launch Detected at " << target << std::endl; + } + else + { + // Otherwise, ask other players where the nuke is! + Broodwar->sendText("Where's the nuke?"); + } + + // You can also retrieve all the nuclear missile targets using Broodwar->getNukeDots()! +} + +void KoraBot::onUnitDiscover(BWAPI::Unit unit) +{ + Macro::onUnitDiscover(unit); +} + +void KoraBot::onUnitEvade(BWAPI::Unit unit) +{ +} + +void KoraBot::onUnitShow(BWAPI::Unit unit) +{ + Macro::onUnitShow(unit); +} + +void KoraBot::onUnitHide(BWAPI::Unit unit) +{ +} + +void KoraBot::onUnitCreate(BWAPI::Unit unit) +{ + + if (Broodwar->isReplay()) + { + // if we are in a replay, then we will print out the build order of the structures + if (unit->getType().isBuilding() && !unit->getPlayer()->isNeutral()) + { + int seconds = Broodwar->getFrameCount() / 24; + int minutes = seconds / 60; + seconds %= 60; + Broodwar->sendText("%.2d:%.2d: %s creates a %s", minutes, seconds, unit->getPlayer()->getName().c_str(), unit->getType().c_str()); + } + } + else + { + Macro::onUnitCreate(unit); + } + +} + +void KoraBot::onUnitDestroy(BWAPI::Unit unit) +{ + try + { + if (!Broodwar->isReplay()) + { + Macro::onUnitDestroy(unit); + } + else + { + if (unit->getType().isMineralField()) + theMap.OnMineralDestroyed(unit); + else if (unit->getType().isSpecialBuilding()) + theMap.OnStaticBuildingDestroyed(unit); + } + } + catch (const std::exception& e) + { + Broodwar << "EXCEPTION: " << e.what() << std::endl; + } +} + +void KoraBot::onUnitMorph(BWAPI::Unit unit) +{ + if (Broodwar->isReplay()) + { + // if we are in a replay, then we will print out the build order of the structures + if (unit->getType().isBuilding() && !unit->getPlayer()->isNeutral()) + { + int seconds = Broodwar->getFrameCount() / 24; + int minutes = seconds / 60; + seconds %= 60; + Broodwar->sendText("%.2d:%.2d: %s morphs a %s", minutes, seconds, unit->getPlayer()->getName().c_str(), unit->getType().c_str()); + } + } + else + { + Macro::onUnitMorph(unit); + } +} + +void KoraBot::onUnitRenegade(BWAPI::Unit unit) +{ +} + +void KoraBot::onSaveGame(std::string gameName) +{ + Broodwar << "The game was saved to \"" << gameName << "\"" << std::endl; +} + +void KoraBot::onUnitComplete(BWAPI::Unit unit) +{ + if (Broodwar->getFrameCount() > 0 && !Broodwar->isReplay()) + { + if (unit->getPlayer() == Broodwar->self()) + { + Macro::onUnitComplete(unit); + } + } +} \ No newline at end of file diff --git a/Source/Location.cpp b/Source/Location.cpp new file mode 100644 index 0000000..0ec3866 --- /dev/null +++ b/Source/Location.cpp @@ -0,0 +1,8 @@ +#include "Location.h" + +Location::Location(TilePosition tp, bool sl) +{ + this->lastScouted = 0; + this->startLocation = sl; + this->tilePosition = tp; +} \ No newline at end of file diff --git a/Source/Location.h b/Source/Location.h new file mode 100644 index 0000000..9e2e8fc --- /dev/null +++ b/Source/Location.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +using namespace BWAPI; +using namespace Filter; + +class Location +{ +public: + Location(TilePosition tp, bool sl); + + int lastScouted; + bool startLocation; + TilePosition tilePosition; +}; \ No newline at end of file diff --git a/Source/Macro.cpp b/Source/Macro.cpp new file mode 100644 index 0000000..d30f359 --- /dev/null +++ b/Source/Macro.cpp @@ -0,0 +1,555 @@ +#include "Macro.h" + +std::vector Macro::bases; +std::vector Macro::enemyIDs; +std::vector Macro::locations; +std::vector Macro::players; +std::vector Macro::queue; +std::vector Macro::scouts; +std::vector Macro::squads; +int Macro::lastAttack = 0; +int Macro::selfID; +TilePosition Macro::lastAttackLocation; +TilePosition Macro::lastBuildLocation; +bool Macro::macroHatch; +bool Macro::newBase; +bool Macro::stopProduction; +bool Macro::workerScout = false; +namespace { auto & map = BWEM::Map::Instance(); } + +void Macro::addBase(TilePosition tp) +{ + bases.push_back(CBase(tp)); +} + +void Macro::addLocation(TilePosition tp, bool StartLocation) +{ + locations.push_back(Location(tp, StartLocation)); +} + +void Macro::addPlayer(BWAPI::Player player) +{ + stopProduction = false; // needed to initialize it somewhere + players.push_back(CPlayer(player)); +} + +void Macro::addSquad() +{ + squads.push_back(Squad()); +} + +// does top-level analysis of what to build +void Macro::analyzeQueue() +{ + std::string entry = ""; + bool status = false; + double score = 0; + int index = -1; + int count = 0; + + for (auto e : queue) + { + if ((!e.unit.isBuilding() && e.unit.supplyProvided() == 0) || !e.inProgress) + { + if (e.isTech()) e.score = Analysis::analyze(e.tech); + else if (e.isUnit()) e.score = Analysis::analyze(e.unit); + else if (e.isUpgrade()) e.score = Analysis::analyze(e.upgrade); + if (e.score > score) + { + score = e.score; + index = count; + } + } + count++; + } + + if (index > -1) + { + if (queue.at(index).isTech()) + { + } + else if (queue.at(index).isUnit()) + { + //Broodwar << queue.at(index).unit << std::endl; + if (!queue.at(index).unit.isBuilding()) + { + if (!stopProduction || Broodwar->self()->minerals() > 300 + queue.at(index).unit.mineralPrice()) + { + queue.at(index).inProgress = getTrainingBase().unit->train(queue.at(index).unit); + if (queue.at(index).inProgress) queue.erase(queue.begin() + index); + } + } + else if (queue.at(index).unit.isBuilding()) + { + if (queue.at(index).unit == UnitTypes::Zerg_Lair || queue.at(index).unit == UnitTypes::Zerg_Hive) + { + queue.at(index).inProgress = players.at(selfID).bases.front().base->morph(queue.at(index).unit); + } + else + { + queue.at(index).inProgress = true; + // find a drone and send them off + int workerIndex = Util::buildWorkerIndex(); + + if (workerIndex > -1) + { + players.at(selfID).bases.at(players.at(selfID).units.at(workerIndex).miningBase).subtractWorker("minerals"); + if (newBase && queue.at(index).unit == UnitTypes::Zerg_Hatchery && !stopProduction) + { + players.at(selfID).units.at(workerIndex).action = "expand"; + CBase base = Util::getNextExpand(selfID); + players.at(selfID).units.at(workerIndex).target = base.tilePosition; + players.at(selfID).units.at(workerIndex).unit->move(Position(players.at(selfID).units.at(workerIndex).target)); + stopProduction = true; + } + else + { + players.at(selfID).units.at(workerIndex).action = "build"; + TilePosition target; + if (queue.at(index).unit == UnitTypes::Zerg_Sunken_Colony) + { + players.at(selfID).units.at(workerIndex).targetUnit = UnitTypes::Zerg_Creep_Colony; + target = BuildingPlacement::getPosition(UnitTypes::Zerg_Sunken_Colony); + } + else if (queue.at(index).unit == UnitTypes::Zerg_Spore_Colony) + { + players.at(selfID).units.at(workerIndex).targetUnit = UnitTypes::Zerg_Creep_Colony; + target = BuildingPlacement::getPosition(UnitTypes::Zerg_Spore_Colony); + } + else + { + players.at(selfID).units.at(workerIndex).targetUnit = queue.at(index).unit; + target = BuildingPlacement::getPosition(players.at(selfID).units.at(workerIndex).targetUnit); + } + players.at(selfID).units.at(workerIndex).target = target; + } + } + } + } + } + else if (queue.at(index).isUpgrade()) + { + if (!stopProduction || Broodwar->self()->minerals() > 300 + queue.at(index).upgrade.mineralPrice()) + { + if (Util::findUpgradeBuilding(queue.at(index).upgrade) > -1) + { + players.at(selfID).units.at(Util::findUpgradeBuilding(queue.at(index).upgrade)).unit->upgrade(queue.at(index).upgrade); + queue.erase(queue.begin() + index); + } + } + } + } +} + +// assigns a base to a player +void Macro::assignBase(int playerID, CBase base) +{ + players.at(playerID).bases.push_back(base); +} + +// find the base that's closest to the unit and also unsaturated +int Macro::findMiningBase(BWAPI::Unit unit, std::string type) +{ + bool check = false; + int distance = 10000; + int index = -1; + index = Util::getClosestOwnedBase(unit); + + if (players.at(selfID).bases.at(index).isSaturated(type)) index = -1; + + if (index == -1) + { + for (size_t i = 0; i < players.at(selfID).bases.size(); i++) + { + if (players.at(selfID).bases.at(i).complete) + { + int d = unit->getDistance(players.at(selfID).bases.at(i).base); + + if (d < distance && + !players.at(selfID).bases.at(i).isSaturated(type)) + { + distance = d; + index = i; + } + } + } + } + return index; +} + +void Macro::freeUnit(int index) +{ + if (players.at(selfID).units.at(index).action == "minerals" + || players.at(selfID).units.at(index).action == "gas") + { + int base = players.at(selfID).units.at(index).miningBase; + if (base > -1) + players.at(selfID).bases.at(base).subtractWorker(players.at(selfID).units.at(index).action); + } + players.at(selfID).units.at(index).action = ""; + players.at(selfID).units.at(index).miningBase = -1; + players.at(selfID).units.at(index).target = TilePosition(-1, -1); + players.at(selfID).units.at(index).unit->move(players.at(selfID).units.at(index).unit->getPosition()); + //players.at(selfID).units.at(index).unit->stop(); +} + +CUnit Macro::getTrainingBase() +{ + int id = 0; + int count = 0; + for (auto u : players.at(selfID).units) + { + if (u.unit->getType() == UnitTypes::Zerg_Hatchery) id = count; + if (u.unit->getLarva().size() > 0) + { + return u; + } + count++; + } + + return players.at(selfID).units.at(id); +} + +void Macro::morphCreepColonies() +{ + for (size_t i = 0; i < players.at(selfID).units.size(); i++) + { + if (players.at(selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Creep_Colony) + { + int base = Util::getClosestOwnedBase(players.at(selfID).units.at(i).unit); + if(players.at(selfID).bases.at(base).sunkenCount < players.at(selfID).bases.at(base).desiredSunkenCount()) + { + players.at(selfID).units.at(i).unit->morph(UnitTypes::Zerg_Sunken_Colony); + } + else players.at(selfID).units.at(i).unit->morph(UnitTypes::Zerg_Spore_Colony); + } + } +} + +void Macro::onUnitComplete(Unit unit) +{ + if (int id = unit->getPlayer()->getID() == selfID + && unit->getType() != UnitTypes::Zerg_Sunken_Colony + && unit->getType() != UnitTypes::Zerg_Spore_Colony) + { + int index = -1; + int count = 0; + for (auto e : queue) + { + if (e.unit == unit->getType() && e.inProgress) + { + index = count; + break; + } + count++; + } + if(index > -1) queue.erase(queue.begin()+index); + if (unit->getType() == UnitTypes::Zerg_Hatchery) + { + bool chk = false; + + for (auto b : bases) + { + if (unit->getTilePosition().x == b.tilePosition.x + && unit->getTilePosition().y == b.tilePosition.y) + { + chk = true; + break; + } + } + + if (chk && players.at(selfID).bases.size() > 0) players.at(selfID).bases.back().complete = true; + } + int unitIndex = Util::getUnitIndex(unit); + if (unitIndex > -1) + { + players.at(selfID).units.at(unitIndex).unitType = unit->getType(); + } + } +} + +// assigns a unit or base to the respective player +void Macro::onUnitCreate(Unit unit) +{ + if (unit->getPlayer() != Broodwar->neutral()) + { + int id = unit->getPlayer()->getID(); + if (unit->getType().isResourceDepot()) + { + for (auto b : bases) + { + if (unit->getTilePosition().x == b.tilePosition.x && unit->getTilePosition().y == b.tilePosition.y) + { + CBase base = CBase(unit); + base.complete = true; + assignBase(id, base); + } + } + } + players.at(id).units.push_back(CUnit(unit)); + } +} + +void Macro::onUnitDestroy(Unit unit) +{ + if (unit->getPlayer() != Broodwar->neutral()) + { + int id = unit->getPlayer()->getID(); + int index = Util::getUnitIndex(unit); + players.at(id).gasLost += unit->getType().gasPrice(); + players.at(id).mineralsLost += unit->getType().mineralPrice(); + + if (index > -1) + { + if (unit->getPlayer() == Broodwar->self()) + { + if (players.at(id).units.at(index).action == "build" + || players.at(id).units.at(index).action == "expand") + { + //TODO newbase isn't being cleared properly when a drone dies + // on the way to build a hatchery + if (players.at(id).units.at(index).action == "expand") + { + newBase = false; + } + int queueIndex = -1; + for (size_t i = 0; i < queue.size(); i++) + { + if (queue.at(i).unit == players.at(id).units.at(index).targetUnit) + { + queueIndex = i; + break; + } + } + if (queueIndex > -1) queue.erase(queue.begin() + queueIndex); + } + + else if (players.at(id).units.at(index).action == "minerals") + { + int baseIndex = players.at(id).units.at(index).miningBase; + if (baseIndex > -1) + { + players.at(id).bases.at(baseIndex).mineralWorkers--; + } + } + + else if (players.at(id).units.at(index).action == "gas") + { + int baseIndex = players.at(id).units.at(index).miningBase; + if (baseIndex > -1) + { + players.at(id).bases.at(baseIndex).gasWorkers--; + } + } + + int squadindex = -1; + int unitindex = -1; + for (size_t j = 0; j < squads.size(); j++) + { + for (size_t k = 0; k < squads.at(j).units.size(); k++) + { + if (squads.at(j).units.at(k).id == unit->getID()) + { + squadindex = j; + unitindex = k; + break; + } + } + } + if (unitindex > -1) + { + squads.at(squadindex).units.erase(squads.at(squadindex).units.begin() + unitindex); + } + int scoutindex = -1; + for (size_t j = 0; j < scouts.size(); j++) + { + if (scouts.at(j).unit->getID() == unit->getID()) + { + scoutindex = j; + break; + } + } + if (scoutindex > -1) scouts.erase(scouts.begin() + scoutindex); + + } + players.at(id).units.erase(players.at(id).units.begin() + index); + } + + if (unit->getType().isResourceDepot()) + { + index = Util::getBaseIndex(unit); + + if (index > -1) players.at(id).bases.erase(players.at(id).bases.begin() + index); + } + } +} + +void Macro::onUnitDiscover(Unit unit) +{ + if (unit->getPlayer() != Broodwar->neutral() && unit->getPlayer() != Broodwar->self() && !Util::unitCounted(unit)) + { + int id = unit->getPlayer()->getID(); + players.at(id).gasSpent += unit->getType().gasPrice(); + players.at(id).mineralsSpent += unit->getType().mineralPrice(); + if (unit->getType().isResourceDepot()) + { + for (auto b : bases) + { + if (unit->getTilePosition().x == b.tilePosition.x && unit->getTilePosition().y == b.tilePosition.y) + { + CBase base = CBase(unit); + base.complete = unit->isCompleted(); + assignBase(id, base); + } + } + } + CUnit u = CUnit(unit); + u.lastSeen = u.unit->getTilePosition(); + players.at(id).units.push_back(u); + } +} + +void Macro::onUnitMorph(Unit unit) +{ + if (unit->getPlayer()->getID() == selfID) + { + if (unit->getType().isRefinery()) + { + players.at(selfID).units.push_back(CUnit(unit)); + } + int index = Util::getQueueIndex(unit->getType()); + + if (index > -1) + { + queue.erase(queue.begin() + index); + } + + if (unit->getType() == UnitTypes::Zerg_Hatchery && newBase + && Util::isBasePosition(unit->getTilePosition())) + { + players.at(selfID).bases.push_back(CBase(unit)); + newBase = false; + stopProduction = false; + } + else if (unit->getType() == UnitTypes::Zerg_Hatchery && macroHatch) + { + macroHatch = false; + } + + int unitIndex = Util::getUnitIndex(unit); + if (unitIndex > -1) + { + players.at(selfID).units.at(unitIndex).unitType = unit->getType(); + } + } +} + +void Macro::onUnitShow(Unit unit) +{ + if (!unit->getPlayer()->isNeutral() && !unit->getPlayer()->getID() == Broodwar->self()->getID()) + { + int id = unit->getPlayer()->getID(); + for (size_t i = 0; i < players.at(id).units.size(); i++) + { + if (players.at(id).units.at(i).unit->getID() == unit->getID()) + { + players.at(id).units.at(i).lastSeen = unit->getTilePosition(); + players.at(id).units.at(i).unitType = unit->getType(); + } + } + } +} + +int Macro::queueCount(BWAPI::UnitType unit) +{ + int count = 0; + for (auto e : queue) + { + if (e.unit == unit) count++; + } + return count; +} + +bool Macro::queueHas(BWAPI::TechType tech) +{ + for (auto e : queue) + { + if (e.tech == tech) return true; + } + return false; +} + +bool Macro::queueHas(BWAPI::UnitType unit) +{ + for (auto e : queue) + { + if (e.unit == unit) return true; + } + return false; +} + +bool Macro::queueHas(BWAPI::UpgradeType upgrade) +{ + for (auto e : queue) + { + if (e.upgrade == upgrade) return true; + } + return false; +} + +void Macro::setEnemyID(int id) +{ + enemyIDs.push_back(id); +} + +void Macro::setLastAttack(int frame) +{ + lastAttack = frame; +} + +void Macro::setLastBuildLocation(TilePosition tp) +{ + lastBuildLocation = tp; +} + +void Macro::setMacroHatch(bool status) +{ + macroHatch = status; +} + +void Macro::setNewBase(bool status) +{ + newBase = status; +} + +void Macro::setScouts() +{ + if (scouts.size() < 4) + { + bool added = false; + for (size_t i = 0; i < players.at(selfID).units.size(); i++) + { + if (players.at(selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Overlord && !added) + { + bool found = Scouting::isScout(players.at(selfID).units.at(i)); + + if (!added && !found || scouts.size() == 0) + { + players.at(selfID).units.at(i).action = "scout"; + scouts.push_back(players.at(selfID).units.at(i)); + added = true; + } + } + } + } +} + +void Macro::setSelfID(int id) +{ + selfID = id; +} + +void Macro::setStopProduction(bool status) +{ + stopProduction = status; +} \ No newline at end of file diff --git a/Source/Macro.h b/Source/Macro.h new file mode 100644 index 0000000..f72f3d8 --- /dev/null +++ b/Source/Macro.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include "BWEM/src/bwem.h" + +#include "Analysis.h" +#include "BuildingPlacement.h" +#include "CBase.h" +#include "CPlayer.h" +#include "CUnit.h" +#include "Location.h" +#include "QueueEntry.h" +#include "Squad.h" +#include "Util.h" + +using namespace BWAPI; +class CBase; +class CPlayer; +class CUnit; +class QueueEntry; +class Squad; + + +class Macro +{ + // all functions and variables are sorted by category here but are alphabetical in the macro.cpp file + +public: + static std::vector bases; + static std::vector players; + static std::vector queue; // a function will then pick from the queue what's most important right now + static std::vector scouts; + static std::vector squads; + static int selfID; + static void setSelfID(int); + static std::vector enemyIDs; + static void setEnemyID(int id); + + // macro functions + static void analyzeQueue(); + static void freeUnit(int index); + static CUnit getTrainingBase(); + static TilePosition lastBuildLocation; + static void setLastBuildLocation(TilePosition tp); + static bool macroHatch; + static void morphCreepColonies(); + static bool newBase; + static void setMacroHatch(bool); + static void setNewBase(bool); + static bool stopProduction; + static void setStopProduction(bool status); + + static int lastAttack; + static void setLastAttack(int frame); + static TilePosition lastAttackLocation; + + // map functions + static void addBase(TilePosition tp); + static void addLocation(TilePosition tp, bool startLocation); + static void assignBase(int playerID, CBase base); + static std::vector locations; + + // utility / event functions + static void addPlayer(BWAPI::Player player); + static void addSquad(); + static int findMiningBase(BWAPI::Unit unit, std::string type); + static void onUnitComplete(Unit unit); + static void onUnitCreate(Unit unit); + static void onUnitDestroy(Unit unit); + static void onUnitDiscover(Unit unit); + static void onUnitMorph(Unit unit); + static void onUnitShow(Unit unit); + static int queueCount(BWAPI::UnitType unit); + static bool queueHas(BWAPI::TechType tech); + static bool queueHas(BWAPI::UnitType unit); + static bool queueHas(BWAPI::UpgradeType upgrade); + + // scouting + static void setScouts(); + static bool workerScout; +}; \ No newline at end of file diff --git a/Source/Military.cpp b/Source/Military.cpp new file mode 100644 index 0000000..cb36f25 --- /dev/null +++ b/Source/Military.cpp @@ -0,0 +1,435 @@ +#include "Military.h" + +void Military::addSquadlessUnits(int squadIndex) +{ + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (u.unit->getType().supplyProvided() == 0 + && !u.unit->getType().isBuilding() + && u.unit->getType() != UnitTypes::Zerg_Larva + && u.unit->getType() != UnitTypes::Zerg_Egg + && u.unit->getType() != UnitTypes::Zerg_Drone + && u.unit->getType() != UnitTypes::Zerg_Overlord + && !u.isInSquad() + ) + { + Macro::squads.at(squadIndex).units.push_back(u); + } + } +} + +//TODO clean this up +void Military::checkAttack() +{ + // things to consider: + // if supply is maxed + // if a base is vulnerable (hit and run) + // if the enemy is at a disadvantage (do some damage and/or go for the win) + + if (Broodwar->self()->supplyUsed() > 380) + { + // get every squad except one for defense and go attack somewhere + int skippedSquads = 0; + int squadIndex = -1; + + for (size_t i = 0; i < Macro::squads.size(); i++) + { + if (Macro::squads.at(i).action == "defend" && skippedSquads == 0) + { + skippedSquads++; + } + else + { + Macro::squads.at(i).action = "attack"; + squadIndex = i; + } + } + + if (squadIndex == -1) + { + Macro::squads.push_back(Squad()); + squadIndex = Macro::squads.size() - 1; + } + + addSquadlessUnits(squadIndex); + + Position attackLocation = Position(getAttackLocation().first); + for (size_t i = 0; i < Macro::squads.size(); i++) + { + if (Macro::squads.at(i).action == "attack") + { + for (auto u : Macro::squads.at(i).units) + { + u.unit->attack(attackLocation); + if (u.unit->getType() == UnitTypes::Zerg_Overlord) + { + u.unit->move(attackLocation); + } + } + } + } + } + else if(shouldAttackNonMaxed()) + { + // things to consider: + // how strong the most vulnerable enemy base is + // how strong the attack squads are + std::pair baseToAttack = getAttackLocation(); + int damagePadding = 120; + + int squadIndex = getIdleSquad(); + + // find which squad to send to attack + if (squadIndex > -1) + { + addSquadlessUnits(squadIndex); + Macro::squads.at(squadIndex).checkDamage(); + + if (Macro::squads.at(squadIndex).groundDamage > baseToAttack.second + damagePadding) + { + Macro::squads.at(squadIndex).attack(baseToAttack); + } + } + } +} + +// determine where is being attacked and send units to defend +void Military::checkDefense() +{ + bool attacking = false; + Position attackLocation; + + // check if enemies are in certain locations and send defending units if they are + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + Macro::players.at(Macro::selfID).bases.at(i).getDefense(); + double attackerScore = checkEnemiesAt(Macro::players.at(Macro::selfID).bases.at(i)); + if (attackerScore > 5) + { + attacking = true; + Macro::setLastAttack(Broodwar->getFrameCount()); + sendDefense( + Macro::players.at(Macro::selfID).bases.at(i).base->getClosestUnit(Filter::IsEnemy)->getTilePosition(), + attackerScore, + Macro::players.at(Macro::selfID).bases.at(i).sunkenCount * UnitTypes::Zerg_Sunken_Colony.groundWeapon().damageAmount()/2); + //attackLocation = Macro::players.at(Macro::selfID).bases.at(i).base->getClosestUnit(Filter::IsEnemy)->getPosition(); + } + } + + for (size_t i = 0; i < Macro::squads.size(); i++) + { + + } + + /* + if (attacking) + { + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (u.unit->getType().supplyProvided() == 0 + && !u.unit->getType().isBuilding() + && u.unit->getType() != UnitTypes::Zerg_Larva + && u.unit->getType() != UnitTypes::Zerg_Egg + && u.unit->getType() != UnitTypes::Zerg_Drone + && u.unit->getType() != UnitTypes::Zerg_Overlord + && !Util::isInSquad(u) + ) + { + if (Macro::squads.size() == 0) + Macro::squads.push_back(Squad()); + Macro::squads.back().units.push_back(u); + u.action = "defend"; + u.unit->attack(attackLocation); + } + } + }*/ +} + +double Military::checkEnemiesAt(CBase b) +{ + double score = 0; + for (auto u : b.base->getUnitsInRadius(32*20, Filter::IsEnemy)) + { + if (u->getType().groundWeapon() != WeaponTypes::None) + { + score += u->getType().groundWeapon().damageAmount(); + } + } + return score; +} + +void Military::checkSquads() +{ + int added = 0; + for (size_t i = 0; i < Macro::squads.size(); i++) + { + Macro::squads.at(i).checkCenter(); + Macro::squads.at(i).checkDamage(); + + if (Macro::squads.at(i).units.size() > 0) + { + int baseIndex = Util::getClosestOwnedBase(Macro::squads.at(i).units.front().unit); + if (baseIndex > -1) + { + if (Broodwar->getFrameCount() > Macro::players.at(Macro::selfID).bases.at(baseIndex).lastAttack + (10 * 24)) + { + Macro::squads.at(i).action = ""; + for (size_t j = 0; j < Macro::squads.at(i).units.size(); j++) + { + Macro::squads.at(i).units.at(j).action = ""; + } + } + } + } + + if (!Macro::squads.at(i).containsType(UnitTypes::Zerg_Overlord)) + { + for (size_t j = 0; j < Macro::players.at(Macro::selfID).units.size(); j++) + { + if (Macro::players.at(Macro::selfID).units.at(j).unit->getType() == UnitTypes::Zerg_Overlord + && Macro::players.at(Macro::selfID).units.at(j).action != "scout" + && Macro::players.at(Macro::selfID).units.at(j).action != "squad" + && added < 2) + { + added++; + Macro::squads.at(i).units.push_back(Macro::players.at(Macro::selfID).units.at(j)); + Macro::players.at(Macro::selfID).units.at(j).action = "squad"; + } + } + } + } +} + +std::pair Military::getAttackLocation() +{ + // start here - pick the most vulnerable base (getbasedefense) and attack + if (Macro::players.at(Macro::enemyIDs.front()).bases.size() > 0) + { + std::pair pair = getMostVulnerableBase(Macro::enemyIDs.front()); + int baseIndex = pair.first; + std::pair returnPair; + try + { + returnPair.first = Macro::players.at(Macro::enemyIDs.front()).bases.at(baseIndex).tilePosition; + returnPair.second = pair.second; + } + catch (const std::exception& e) + { + Broodwar << "out of range error: base index = " << baseIndex << std::endl; + returnPair.first = TilePosition(50,50); + returnPair.second = 1000; + } + + Broodwar << " first return: " << returnPair.first << std::endl; + return returnPair; + } + else + { + for (auto u : Macro::players.at(Macro::enemyIDs.front()).units) + { + //TODO sometimes a unit isn't being added properly and this returns a wrong value + if (u.unit->getType().isBuilding()) + { + std::pair returnPair; + returnPair.first = u.lastSeen; + returnPair.second = u.unit->getType().groundWeapon().damageAmount(); + Broodwar << " second return: " << returnPair.first << std::endl; + return returnPair; + } + } + } + std::pair returnPair; + returnPair.first = TilePosition(Broodwar->mapWidth() / 2, Broodwar->mapHeight() / 2); + returnPair.second = 0; + return returnPair; +} + +double Military::getBaseDefense(CBase base, std::string type, int playerID) +{ + double score = 0; + if (type == "ground") + { + for (size_t i = 0; i < Macro::players.at(playerID).units.size(); i++) + { + if (Util::getDistance(Macro::players.at(playerID).units.at(i).lastSeen, base.tilePosition) < 15) + if (Macro::players.at(playerID).units.at(i).unitType.groundWeapon() != WeaponTypes::None) + { + score += Macro::players.at(playerID).units.at(i).unitType.groundWeapon().damageAmount(); + } + } + return score + base.sunkenCount; + } + else + { + for (auto u : Broodwar->getUnitsInRadius(base.base->getPosition(), 15*32)) + { + if (u->getType().airWeapon() != WeaponTypes::None + && u->getPlayer()->getID() == playerID) + { + score += u->getType().airWeapon().damageAmount(); + } + } + return score + base.sporeCount; + } +} + +TilePosition Military::getChokepointCenter(const BWEM::Area* area, const BWEM::Area* neighborArea) +{ + TilePosition average = TilePosition(0, 0); + for (auto a : area->ChokePointsByArea()) + { + if (a.first->Id() == neighborArea->Id()) + { + average = TilePosition(a.second->front().Center()); + break; + } + } + return average; +} + +int Military::getClosestSquad(TilePosition tp) +{ + int index = -1; + double distance = 1000; + for (size_t i = 0; i < Macro::squads.size(); i++) + { + if (Util::getDistance(tp, Macro::squads.at(i).center) < distance + && Macro::squads.at(i).action != "defend") + { + index = i; + distance = Util::getDistance(tp, Macro::squads.at(i).center); + } + } + return index; +} + +int Military::getIdleSquad() +{ + int squadIndex = -1; + for (size_t i = 0; i < Macro::squads.size(); i++) + { + if (Macro::squads.at(i).action != "defend" + && Macro::squads.at(i).action != "attack") + { + squadIndex = i; + break; + } + } + return squadIndex; +} + +std::pair Military::getMostVulnerableBase(int playerID) +{ + int index = -1; + double score = 10000; + + for (size_t i = 0; i < Macro::players.at(playerID).bases.size(); i++) + { + int baseScore = getBaseDefense(Macro::players.at(playerID).bases.at(i), "ground", playerID); + if (baseScore < score && + (Macro::players.at(playerID).bases.at(i).exposedToCenter || Macro::players.at(playerID).bases.size() == 1)) + { + score = baseScore; + index = i; + } + } + if (index == -1 && Macro::players.at(playerID).bases.size() == 1) + { + index = 0; + } + std::pair pair; + pair.first = index; + pair.second = score; + return pair; +} + +bool Military::isThreatened(TilePosition tp) +{ + for (auto u : Broodwar->getUnitsInRadius(Position(tp), 20, Filter::IsEnemy)) + { + if (u->canAttackUnit(UnitTypes::Zerg_Overlord) + && inThreatRange(tp, CUnit(u))) return true; + } + return false; +} + +bool Military::inThreatRange(TilePosition tp, CUnit unit) +{ + return Util::getDistance(tp, unit.unit->getTilePosition()) < unit.unit->getType().seekRange() + 1; +} + +double Military::likelihoodToAttack() +{ + // things to consider: + // how long it's been since the last time they attacked + // how many units the enemy might have + + double score = 1; + double timeModifier = 0; + double attackTime = 360.0 * 24; + if (!Macro::enemyIDs.empty()) + { + if (Macro::players.at(Macro::enemyIDs.front()).bases.size() == 2) attackTime = 540.0 * 24; + } + + if(Broodwar->getFrameCount() < 360*24) + timeModifier = Broodwar->getFrameCount() / attackTime; + + else + timeModifier = (Broodwar->getFrameCount() - Macro::lastAttack) / (540.0 * 24); + + if (Broodwar->getFrameCount() - Macro::lastAttack < 10*24 && Macro::lastAttack > 0) return 1; + if (timeModifier > 1) timeModifier = 1.0; + + if (Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Terran_Barracks) == 0 + && Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Protoss_Gateway) == 0 + && Util::countUnits(Macro::enemyIDs.front(), UnitTypes::Zerg_Spawning_Pool) == 0) + { + score = 0; + } + + return score * timeModifier; +} + +// things to consider: +// how important the place being attacked is +// if a bigger win can be found by attacking instead (base race) +// how powerful the attacking force is +// how powerful the defending force is +void Military::sendDefense(TilePosition tp, int attackerScore, int defenseScore) +{ + int squadIndex = getClosestSquad(tp); + if (squadIndex == -1) squadIndex = 0; + int padding = 80; // give the defense some breathing room + + Macro::squads.at(squadIndex).action = "defend"; + if (defenseScore + Macro::squads.at(squadIndex).groundDamage < attackerScore + padding) + { + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (defenseScore + Macro::squads.at(squadIndex).groundDamage < attackerScore + padding) + { + if (Util::isAttackingUnit(u) + && !u.isInSquad() + ) + { + Macro::squads.at(squadIndex).addUnit(u); + /*Macro::squads.at(squadIndex).units.push_back(u); + Macro::squads.at(squadIndex).airDamage += u.unitType.airWeapon().damageAmount(); + Macro::squads.at(squadIndex).groundDamage += u.unitType.groundWeapon().damageAmount();*/ + } + } + } + } + for (auto u : Macro::squads.at(squadIndex).units) + { + u.unit->attack(Position(tp)); + u.action = "defend"; + } +} + +bool Military::shouldAttackNonMaxed() +{ + return ((Macro::lastAttack > 0 && (Broodwar->getFrameCount() - Macro::lastAttack > 24 * 10)) + || Broodwar->getFrameCount() > 24 * 360); +} \ No newline at end of file diff --git a/Source/Military.h b/Source/Military.h new file mode 100644 index 0000000..728f063 --- /dev/null +++ b/Source/Military.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "CUnit.h" +#include "Macro.h" + +using namespace BWAPI; + +class Military +{ +public: + static void addSquadlessUnits(int squadIndex); + static void checkAttack(); + static void checkDefense(); + static double checkEnemiesAt(CBase base); + static void checkSquads(); + static std::pair getAttackLocation(); + static double getBaseDefense(CBase b, std::string type, int playerID); + static TilePosition getChokepointCenter(const BWEM::Area* area, const BWEM::Area* neighborArea); + static int getClosestSquad(TilePosition tp); + static int getIdleSquad(); + static std::pair getMostVulnerableBase(int playerID); + static bool inThreatRange(TilePosition tp, CUnit unit); + static bool isThreatened(TilePosition tp); + static double likelihoodToAttack(); + static void sendDefense(TilePosition tp, int attackerScore, int defenseScore); + static bool shouldAttackNonMaxed(); +}; \ No newline at end of file diff --git a/Source/Mining.cpp b/Source/Mining.cpp new file mode 100644 index 0000000..8530e51 --- /dev/null +++ b/Source/Mining.cpp @@ -0,0 +1,7 @@ +#include "Mining.h" + +bool Mining::resourceToMine() +{ + //TODO unused? + return true; +} \ No newline at end of file diff --git a/Source/Mining.h b/Source/Mining.h new file mode 100644 index 0000000..18014ab --- /dev/null +++ b/Source/Mining.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include "CUnit.h" +#include "Macro.h" + +using namespace BWAPI; + +const bool MINERALS = 0; +const bool GAS = 1; + +class Mining +{ +public: + Mining(); + static bool resourceToMine(); +}; \ No newline at end of file diff --git a/Source/PlayerAssessment.cpp b/Source/PlayerAssessment.cpp new file mode 100644 index 0000000..d6e3eec --- /dev/null +++ b/Source/PlayerAssessment.cpp @@ -0,0 +1,37 @@ +#include "PlayerAssessment.h" + +int PlayerAssessment::estimateBaseMinedMinerals(int playerID, CBase b) +{ + // things to consider: + // when the base was last seen + // how many resources have been mined + // how many workers are there + // how many workers might be there now + return b.mineralWorkers * 45; +} + +int PlayerAssessment::estimateTotalGas(int playerID) +{ + +} + +int PlayerAssessment::estimateTotalMinerals(int playerID) +{ + int originalMinerals = 0; + int currentMinerals = 0; + int minsProbablyMinedSince = 0; + + for (auto b : Macro::players.at(playerID).bases) + { + currentMinerals += b.minsAmount; + originalMinerals += b.originalMinerals; + + } + + +} + +int PlayerAssessment::estimateUnitCount(int playerID, BWAPI::UnitType unit) +{ + +} \ No newline at end of file diff --git a/Source/PlayerAssessment.h b/Source/PlayerAssessment.h new file mode 100644 index 0000000..ba6c192 --- /dev/null +++ b/Source/PlayerAssessment.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Macro.h" +#include "Util.h" + +using namespace BWAPI; + +class PlayerAssessment +{ +public: + // determine what a player has, and what they can do with it + static int estimateBaseMinedMinerals(int playerID, CBase b); + static int estimateTotalGas(int playerID); + static int estimateTotalMinerals(int playerID); + + static int estimateUnitCount(int playerID, BWAPI::UnitType unit); +}; \ No newline at end of file diff --git a/Source/QueueEntry.cpp b/Source/QueueEntry.cpp new file mode 100644 index 0000000..58a5333 --- /dev/null +++ b/Source/QueueEntry.cpp @@ -0,0 +1,48 @@ +#include "QueueEntry.h" + +QueueEntry::QueueEntry() +{ + +} + +QueueEntry::QueueEntry(BWAPI::TechType tech, bool inProgress, float score) +{ + this->tech = tech; + this->unit = UnitTypes::Unknown; + this->upgrade = UpgradeTypes::Unknown; + this->inProgress = inProgress; + this->score = score; +} + +QueueEntry::QueueEntry(BWAPI::UnitType unit, bool inProgress, float score) +{ + this->tech = TechTypes::Unknown; + this->unit = unit; + this->upgrade = UpgradeTypes::Unknown; + this->inProgress = inProgress; + this->score = score; +} + +QueueEntry::QueueEntry(BWAPI::UpgradeType upgrade, bool inProgress, float score) +{ + this->tech = TechTypes::Unknown; + this->unit = UnitTypes::Unknown; + this->upgrade = upgrade; + this->inProgress = inProgress; + this->score = score; +} + +bool QueueEntry::isTech() +{ + return this->tech != TechTypes::Unknown; +} + +bool QueueEntry::isUnit() +{ + return this->unit != UnitTypes::Unknown; +} + +bool QueueEntry::isUpgrade() +{ + return this->upgrade != UpgradeTypes::Unknown; +} \ No newline at end of file diff --git a/Source/QueueEntry.h b/Source/QueueEntry.h new file mode 100644 index 0000000..3f6f348 --- /dev/null +++ b/Source/QueueEntry.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +using namespace BWAPI; + +class CUnit; + +class QueueEntry +{ +public: + QueueEntry(); + QueueEntry(BWAPI::TechType tech, bool inProgress, float score); + QueueEntry(BWAPI::UnitType unit, bool inProgress, float score); + QueueEntry(BWAPI::UpgradeType upgrade, bool inProgress, float score); + + BWAPI::TechType tech; + BWAPI::UnitType unit; + BWAPI::UpgradeType upgrade; + bool inProgress; + bool isTech(); + bool isUnit(); + bool isUpgrade(); + double score; +}; \ No newline at end of file diff --git a/Source/Scouting.cpp b/Source/Scouting.cpp new file mode 100644 index 0000000..d00266c --- /dev/null +++ b/Source/Scouting.cpp @@ -0,0 +1,169 @@ +#include "Scouting.h" + +TilePosition Scouting::getScoutingLocation() +{ + TilePosition location = TilePosition(0, 0); + + int count = 0; + // check start locations first + int lastChecked = 180*60*24; // a game probably won't go on three hours + int index = -1; + + // give priority to scouting the next likely enemy base and then their bases + if (!Macro::enemyIDs.empty()) + { + if (!Macro::players.at(Macro::enemyIDs.front()).bases.empty()) + { + CBase nextExpand = Util::getNextExpand(Macro::enemyIDs.front()); + for (size_t i = 0; i < Macro::locations.size(); i++) + { + if (nextExpand.tilePosition == Macro::locations.at(i).tilePosition) + { + if (!locationScouted(Macro::locations.at(i)) + && Macro::locations.at(i).lastScouted < lastChecked + && Macro::locations.at(i).lastScouted > Broodwar->getFrameCount() - (30 * 24)) + { + lastChecked = Macro::locations.at(i).lastScouted; + index = i; + } + } + } + if (index > -1) return Macro::locations.at(index).tilePosition; + } + + for (size_t i = 0; i < Macro::locations.size(); i++) + { + + if (Util::ownedBy(Macro::locations.at(i).tilePosition) == Macro::enemyIDs.front()) + { + if (!locationScouted(Macro::locations.at(i)) + && Macro::locations.at(i).lastScouted < lastChecked + && Macro::locations.at(i).lastScouted > Broodwar->getFrameCount() - (60 * 24)) + { + lastChecked = Macro::locations.at(i).lastScouted; + index = i; + } + } + } + if (index > -1) return Macro::locations.at(index).tilePosition; + } + + // start locations if the enemy hasn't been found + else + { + for (size_t i = 0; i < Macro::locations.size(); i++) + { + if (!locationScouted(Macro::locations.at(i)) + && Macro::locations.at(i).startLocation) + { + if (Macro::locations.at(i).lastScouted < lastChecked + && Macro::locations.at(i).lastScouted > Broodwar->getFrameCount() - (60 * 24)) + { + lastChecked = Macro::locations.at(i).lastScouted; + index = i; + } + } + } + if (index > -1) return Macro::locations.at(index).tilePosition; + } + + lastChecked = 180 * 60 * 24; + index = -1; + for (size_t i = 0; i < Macro::locations.size(); i++) + { + if (!locationScouted(Macro::locations.at(i))) + { + if (Macro::locations.at(i).lastScouted < lastChecked) + { + lastChecked = Macro::locations.at(i).lastScouted; + index = i; + } + } + } + + return Macro::locations.at(index).tilePosition; +} + +bool Scouting::isScout(CUnit u) +{ + for (size_t j = 0; j < Macro::scouts.size(); j++) + { + if (Macro::scouts.at(j).unit->getID() == u.unit->getID()) + { + return true; + } + } + return false; +} + +bool Scouting::locationScouted(Location l) +{ + for (size_t i = 0; i < Macro::scouts.size(); i++) + { + if (Macro::scouts.at(i).target == l.tilePosition) return true; + if (Macro::players.at(Macro::selfID).bases.front().tilePosition == l.tilePosition) return true; + } + return false; +} + +void Scouting::scout() +{ + if (Broodwar->getFrameCount() > 60 * 24 + && Broodwar->getFrameCount() < 120 * 24 + && !Macro::workerScout) + { + for (size_t i = 0; i < Macro::players.at(Macro::selfID).units.size(); i++) + { + if (Macro::players.at(Macro::selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Drone + && !Macro::workerScout) + { + Macro::freeUnit(i); + Macro::players.at(Macro::selfID).units.at(i).action = "scout"; + Macro::players.at(Macro::selfID).units.at(i).unit->move(Position(Macro::players.at(Macro::selfID).bases.front().tilePosition), false); + //Macro::players.at(Macro::selfID).units.at(i).unit->stop(); + for (size_t j = 0; j < Macro::locations.size(); j++) + { + if (Macro::locations.at(j).startLocation) + { + Macro::players.at(Macro::selfID).units.at(i).unit->move(Position(Macro::locations.at(j).tilePosition), true); + } + } + Macro::players.at(Macro::selfID).units.at(i).unit->move(Position(Macro::players.at(Macro::selfID).bases.front().tilePosition), true); + Macro::workerScout = true; + } + } + } + + for (size_t i = 0; i < Macro::scouts.size(); i++) + { + if (Macro::scouts.at(i).target == TilePosition(-1, -1)) + { + Macro::scouts.at(i).target = getScoutingLocation(); + Macro::scouts.at(i).unit->move(Position(Macro::scouts.at(i).target)); + } + else + { + if (Military::isThreatened(Macro::scouts.at(i).unit->getTilePosition())) + { + std::vector direction = BuildingPlacement::getDirection(Macro::scouts.at(i).unit->getTilePosition(), Macro::scouts.at(i).target); + int x = Macro::scouts.at(i).unit->getTilePosition().x + direction.front() * 5; + int y = Macro::scouts.at(i).unit->getTilePosition().y + direction.back() * 5; + + TilePosition moveTo = TilePosition(x, y); + Macro::scouts.at(i).unit->move(Position(moveTo)); + } + else if (Military::isThreatened(Macro::scouts.at(i).target)) + { + Macro::scouts.at(i).target = getScoutingLocation(); + Macro::scouts.at(i).unit->move(Position(Macro::scouts.at(i).target)); + } + if (Util::getDistance(Macro::scouts.at(i).unit->getTilePosition(), Macro::scouts.at(i).target) < 8) + { + int loc = Util::getLocation(Macro::scouts.at(i).target); + Macro::locations.at(loc).lastScouted = Broodwar->getFrameCount(); + Macro::scouts.at(i).target = getScoutingLocation(); + Macro::scouts.at(i).unit->move(Position(Macro::scouts.at(i).target)); + } + } + } +} diff --git a/Source/Scouting.h b/Source/Scouting.h new file mode 100644 index 0000000..943d810 --- /dev/null +++ b/Source/Scouting.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Location.h" +#include "Macro.h" + +using namespace BWAPI; + +class CUnit; + +class Scouting +{ +public: + static void scout(); + + static int checkNextBase(); + static TilePosition getScoutingLocation(); + static bool isScout(CUnit unit); + static bool locationScouted(Location l); +}; \ No newline at end of file diff --git a/Source/Squad.cpp b/Source/Squad.cpp new file mode 100644 index 0000000..bd72388 --- /dev/null +++ b/Source/Squad.cpp @@ -0,0 +1,80 @@ +#include "Squad.h" + +Squad::Squad() +{ + this->action = ""; + this->center = TilePosition(0, 0); +} + +void Squad::addUnit(CUnit unit) +{ + this->units.push_back(unit); + this->airDamage += unit.unitType.airWeapon().damageAmount(); + this->groundDamage += unit.unitType.groundWeapon().damageAmount(); +} + +void Squad::attack(std::pair baseToAttack) +{ + this->action = "attack"; + Position attackLocation = Position(baseToAttack.first); + this->target = TilePosition(attackLocation); + for (auto u : this->units) + { + u.unit->attack(attackLocation); + if (u.unit->getType() == UnitTypes::Zerg_Overlord) + { + u.unit->move(this->units.at(rand() % this->units.size()).unit->getPosition()); + } + } +} + +//TODO rename +void Squad::checkCenter() +{ + int averageX = 0; + int averageY = 0; + int size = this->units.size(); + if (size == 0) size = 1; + + for (auto u : this->units) + { + averageX += u.unit->getTilePosition().x; + averageY += u.unit->getTilePosition().y; + } + + this->center = TilePosition((averageX / size), (averageY / size)); +} + +//TODO rename +void Squad::checkDamage() +{ + int ground = 0; + int air = 0; + for (auto u : this->units) + { + ground += u.unitType.groundWeapon().damageAmount(); + air += u.unitType.airWeapon().damageAmount(); + } + this->groundDamage = ground; + this->airDamage = air; +} + +bool Squad::contains(CUnit unit) +{ + for (size_t i = 0; i < this->units.size(); i++) + { + if (this->units.at(i).unit->getID() == unit.unit->getID()) return true; + } + + return false; +} + +bool Squad::containsType(UnitType ut) +{ + for (size_t i = 0; i < this->units.size(); i++) + { + if (this->units.at(i).unit->getType() == ut) return true; + } + + return false; +} \ No newline at end of file diff --git a/Source/Squad.h b/Source/Squad.h new file mode 100644 index 0000000..2454e59 --- /dev/null +++ b/Source/Squad.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "CUnit.h" + +using namespace BWAPI; + +class Squad//alah, we are off +{ +public: + Squad(); + + std::string action; + int airDamage; + BWAPI::TilePosition center; + int groundDamage; + BWAPI::TilePosition target; + std::vector units; + + void addUnit(CUnit unit); + void attack(std::pair baseToAttack); + void checkCenter(); + void checkDamage(); + bool contains(CUnit unit); + bool containsType(UnitType ut); +}; \ No newline at end of file diff --git a/Source/Tests/MacroTest.cpp b/Source/Tests/MacroTest.cpp new file mode 100644 index 0000000..a27e715 --- /dev/null +++ b/Source/Tests/MacroTest.cpp @@ -0,0 +1,25 @@ +/*#include "CppUnitTest.h" +#include "../kora-bot/Source/Macro.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace MacroTest +{ + TEST_CLASS(MacroTest) + { + public: + TEST_METHOD(addLocation) + { + TilePosition tp1 = TilePosition(1, 1); + Macro::addLocation(tp1, false); + Assert::AreEqual(int(Macro::locations.size()), 1); + } + + TEST_METHOD(addPlayer) + { + BWAPI::Player p = BWAPI::Player(); + Macro::addPlayer(p); + Assert::AreEqual(int(Macro::players.size()), 1); + } + }; +}*/ diff --git a/Source/Tests/MilitaryTest.cpp b/Source/Tests/MilitaryTest.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Source/Util.cpp b/Source/Util.cpp new file mode 100644 index 0000000..7ba3b3f --- /dev/null +++ b/Source/Util.cpp @@ -0,0 +1,458 @@ +#include "Util.h" + +namespace { auto & map = BWEM::Map::Instance(); } + +// gets the index of a drone that's currently mining minerals +int Util::buildWorkerIndex() +{ + for (size_t i = 0; i < Macro::players.at(Macro::selfID).units.size(); i++) + { + if (Macro::players.at(Macro::selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Drone + && Macro::players.at(Macro::selfID).units.at(i).action == "minerals" + && Macro::players.at(Macro::selfID).units.at(i).miningBase > -1) + { + return i; + } + } + return -1; +} + +// checks if there's at least one complete building of the type specified +bool Util::completedBuilding(BWAPI::UnitType unitType) +{ + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (u.unit->getType() == unitType && u.unit->isCompleted()) return true; + if (unitType == UnitTypes::Zerg_Spire + && u.unit->getType() == UnitTypes::Zerg_Greater_Spire && u.unit->isCompleted()) return true; + if (unitType == UnitTypes::Zerg_Hatchery + && u.unit->getType() == UnitTypes::Zerg_Lair && u.unit->isCompleted()) return true; + if (unitType == UnitTypes::Zerg_Hatchery + && u.unit->getType() == UnitTypes::Zerg_Hive && u.unit->isCompleted()) return true; + if (unitType == UnitTypes::Zerg_Lair + && u.unit->getType() == UnitTypes::Zerg_Hive && u.unit->isCompleted()) return true; + } + return false; +} + +int Util::countUnits(int playerID, BWAPI::UnitType unitType) +{ + int count = 0; + for (auto u : Macro::players.at(playerID).units) + { + if (u.unitType == unitType) count++; + // if the unit is changing into the target UnitType, + // count it + else if (u.targetUnit == unitType) count++; + // lairs and hives count as hatcheries, + // hives count as lairs + if (unitType == UnitTypes::Zerg_Hatchery) + { + if (u.unitType == UnitTypes::Zerg_Lair) count++; + if (u.unitType == UnitTypes::Zerg_Hive) count++; + } + else if (unitType == UnitTypes::Zerg_Lair) + if (u.unitType == UnitTypes::Zerg_Hive) count++; + } + return count; +} + +// number of units of a particular type being produced +int Util::eggCount(BWAPI::UnitType unitType) +{ + int count = 0; + for (auto u : Macro::players.at(Macro::selfID).units) + { + + if (u.unit->getBuildType() == unitType && + u.unit->isMorphing()) count++; + } + + return count; +} + +// return the index of a building that can research a particular upgrade +int Util::findUpgradeBuilding(UpgradeType upgradeType) +{ + for (size_t i = 0; i < Macro::players.at(Macro::selfID).units.size(); i++) + { + for (auto u : Macro::players.at(Macro::selfID).units.at(i).unit->getType().upgradesWhat()) + { + if (upgradeType == u) return i; + } + if (Macro::players.at(Macro::selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Hive) + { + for (auto u : UnitTypes::Zerg_Lair.upgradesWhat()) + { + if (upgradeType == u) return i; + } + for (auto u : UnitTypes::Zerg_Hatchery.upgradesWhat()) + { + if (upgradeType == u) return i; + } + } + if (Macro::players.at(Macro::selfID).units.at(i).unit->getType() == UnitTypes::Zerg_Lair) + { + for (auto u : UnitTypes::Zerg_Hatchery.upgradesWhat()) + { + if (upgradeType == u) return i; + } + } + } + return -1; +} + +int Util::getBaseIndex(Unit u) +{ + for (size_t i = 0; i < Macro::players.at(u->getPlayer()->getID()).bases.size(); i++) + { + if (u->getID() == Macro::players.at(u->getPlayer()->getID()).bases.at(i).base->getID()) + { + return i; + } + } + return -1; +} + +//@TODO move this to CUnit +int Util::getClosestOwnedBase(Unit unit) +{ + double distance = 1000; + int index = -1; + for (size_t i = 0; i < Macro::players.at(Macro::selfID).bases.size(); i++) + { + double d = getDistance(unit->getTilePosition(), Macro::players.at(Macro::selfID).bases.at(i).tilePosition); + if (d < distance) + { + distance = d; + index = i; + } + } + return index; +} + +// get air distance between two TilePositions +double Util::getDistance(TilePosition tp1, TilePosition tp2) +{ + double deltaX = tp1.x - tp2.x; + double deltaY = tp1.y - tp2.y; + return sqrt(pow(deltaX, 2.0) + pow(deltaY, 2.0)); +} + +// get the index of a Location +int Util::getLocation(TilePosition tp) +{ + for (size_t i = 0; i < Macro::locations.size(); i++) + { + if (Macro::locations.at(i).tilePosition == tp) + return i; + } + return -1; +} + +// on a path from a1 to a2, get the Area adjacent to a1 +std::pair Util::getNeighborArea(const BWEM::Area* a1, const BWEM::Area* a2) +{ + const BWEM::Area* area; + int count = 0; + + std::vector< const BWEM::Area*> path = Util::getPath(a1, a2); + if(path.size() > 1) + return std::pair(path.at(1), true); + + return std::pair(area, false); +} + +// things to consider: +// how close the base is by ground +// how close the base is to the enemy +CBase Util::getNextExpand(int playerID) +{ + double distance = 1000; + int index = -1; + int count = 0; + for (auto b : Macro::bases) + { + if (!b.isOwned()) + { + CBase home = Macro::players.at(playerID).bases.front(); + double dist = getWalkDistance(home.tilePosition, b.tilePosition); + if (dist < distance + && !(home.tilePosition.x == b.tilePosition.x && home.tilePosition.y == b.tilePosition.y) + && map.GetArea(home.tilePosition)->AccessibleFrom(map.GetArea(b.tilePosition))) + { + distance = dist; + index = count; + } + } + count++; + } + return Macro::bases.at(index); +} + +// gets a walking path from one Area to another +// this is just the a* pathfinding algorithm +std::vector Util::getPath(const BWEM::Area* a1, const BWEM::Area* a2) +{ + std::vector returnPath; + std::vector closedSet; + std::vector openSet; + openSet.push_back(a1); + + std::map cameFrom; + std::map gScore; + + for (const BWEM::Area& a : map.Areas()) + { + gScore.insert(std::pair(a.Id(), 10000)); + } + + gScore[a1->Id()] = 0; + + while (!openSet.empty()) + { + const BWEM::Area* current; + + int score = 9999; + for (auto o : openSet) + { + if (gScore[o->Id()] < score) + { + score = gScore[o->Id()]; + current = o; + } + } + + if (current->Id() == a2->Id()) // found the goal + { + std::vector path; + + for (auto const& ent : cameFrom) + { + path.push_back(ent.first); + } + + path.push_back(current); + return reconstructPath(cameFrom, current); + } + + // remove current from openSet + int index = 0; + for (int i = 0; i < openSet.size(); i++) + { + if (current == openSet.at(i)) + { + index = i; + break; + } + } + openSet.erase(openSet.begin() + index); + + // add current to closedSet + closedSet.push_back(current); + + for (auto n : current->AccessibleNeighbours()) + { + if (std::find(closedSet.begin(), closedSet.end(), n) != closedSet.end()) // if the neighbor is in the closed set, ignore + { + continue; + } + + TilePosition tp1 = CMap::getAreaAverage(current); + TilePosition tp2 = CMap::getAreaAverage(n); + + int tentativeGScore = gScore[current->Id()] + 2; + + if (!(std::find(openSet.begin(), openSet.end(), n) != openSet.end())) + { + openSet.push_back(n); + } + else if(tentativeGScore >= gScore[n->Id()]) + { + continue; + } + cameFrom[n] = current; + //cameFrom.emplace(n, current); + gScore[n->Id()] = tentativeGScore; + } + } + + return returnPath; +} + +int Util::getQueueIndex(UnitType ut) +{ + for (size_t i = 0; i < Macro::queue.size(); i++) + { + if (Macro::queue.at(i).unit == ut) + { + return i; + } + } + return -1; +} + +// return another starting location besides the one provided +// for the purpose of pathing / defense placement +// +//@TODO once the enemy's base is known, just return that +std::pair Util::getTargetArea(const BWEM::Area* startingArea, bool startLocation) +{ + const BWEM::Area* area; + + for (const TilePosition tp : map.StartingLocations()) + { + const BWEM::Area* nearestArea = map.GetNearestArea(tp); + if (!isSameArea(nearestArea, startingArea)); + { + bool chk = true; + if (!startLocation + && Macro::players.at(Macro::selfID).bases.size() > 0 + && Macro::players.at(Macro::selfID).bases.front().tilePosition == tp) + { + chk = false; + } + if (chk) + { + return std::pair(nearestArea, true); + } + } + } + + /*for (const BWEM::Area& a : map.Areas()) + { + for (const BWEM::Base& b : a.Bases()) + { + if (b.Starting() && !(isSameArea(a, startingArea)) + { + bool chk = true; + if (!startLocation + && Macro::players.at(Macro::selfID).bases.size() > 0 + && Macro::players.at(Macro::selfID).bases.front().tilePosition == b.Location()) + { + chk = false; + } + if (chk) + { + return std::pair(&a, true); + } + } + } + }*/ + return std::pair(area, false); +} + +int Util::getUnitIndex(Unit unit) +{ + for (size_t i = 0; i < Macro::players.at(unit->getPlayer()->getID()).units.size(); i++) + { + if (unit->getID() == Macro::players.at(unit->getPlayer()->getID()).units.at(i).unit->getID()) + { + return i; + } + } + return -1; +} + +double Util::getWalkDistance(TilePosition tp1, TilePosition tp2) +{ + double distance = 0; + const BWEM::Area* area = map.GetArea(tp1); + const BWEM::Area* targetArea = map.GetArea(tp2); + TilePosition currentPosition = tp1; + + int count = 0; + for (auto a : getPath(area, targetArea)) + { + if (count > 0) + { + TilePosition nextPosition = CMap::getAreaAverage(a); + distance += getDistance(currentPosition, nextPosition); + currentPosition = nextPosition; + } + count++; + } + return distance; +} + +bool Util::isAttackingUnit(CUnit u) +{ + return (!u.unit->getType().isBuilding() + && u.unit->getType() != UnitTypes::Zerg_Larva + && u.unit->getType() != UnitTypes::Zerg_Egg + && u.unit->getType() != UnitTypes::Zerg_Drone + && u.unit->getType() != UnitTypes::Zerg_Overlord); +} + +// determines if a tileposition is a base position +bool Util::isBasePosition(TilePosition tp) +{ + for (auto b : Macro::bases) + { + if (tp == b.tilePosition) + return true; + } + return false; +} + +bool Util::isSameArea(const BWEM::Area* area1, const BWEM::Area* area2) +{ + return (area1->TopLeft().x == area2->TopLeft().x + && area1->TopLeft().y == area2->TopLeft().y); +} + +// returns id of player who owns a base at tileposition tp +int Util::ownedBy(TilePosition tp) +{ + for (int i = 0; i < Macro::players.size(); i++) + { + for (int j = 0; j < Macro::players.at(i).bases.size(); j++) + { + if (Macro::players.at(i).bases.at(j).tilePosition == tp) return i; + } + } + + return -1; +} + +// used by Util::getPath +std::vector Util::reconstructPath(std::map cameFrom, const BWEM::Area* current) +{ + std::vector returnPath; + returnPath.push_back(current); + + while (cameFrom.find(current) != cameFrom.end()) + { + current = cameFrom[current]; + returnPath.push_back(current); + } + + std::reverse(returnPath.begin(), returnPath.end()); + + return returnPath; +} + +bool Util::unitCounted(Unit unit) +{ + for (auto u : Macro::players.at(unit->getPlayer()->getID()).units) + { + if (unit->getID() == u.unit->getID()) + { + return true; + } + } + return false; +} + +// checks number of workers who have been told to build a certain building +int Util::workerBuildingTargetCount(UnitType unit) +{ + int i = 0; + for (auto u : Macro::players.at(Macro::selfID).units) + { + if (u.unit->getType() == UnitTypes::Zerg_Drone + && u.targetUnit == unit) + i++; + } + return i; +} \ No newline at end of file diff --git a/Source/Util.h b/Source/Util.h new file mode 100644 index 0000000..24de10c --- /dev/null +++ b/Source/Util.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "CBase.h" +#include "CUnit.h" +#include "Macro.h" +#include "CMap.h" + +using namespace BWAPI; + +class CBase; +class CUnit; + +class Util +{ +public: + static int buildWorkerIndex(); + static bool completedBuilding(BWAPI::UnitType unitType); + static int countUnits(int playerID, BWAPI::UnitType unitType); + static int eggCount(BWAPI::UnitType unitType); + static int findUpgradeBuilding(BWAPI::UpgradeType upgradeType); + static int getBaseIndex(Unit unit); + static int getClosestOwnedBase(Unit unit); + static double getDistance(TilePosition tp1, TilePosition tp2); + static int getLocation(TilePosition tp); + static std::pair getNeighborArea(const BWEM::Area* a1, const BWEM::Area* a2); + static CBase getNextExpand(int playerID); + static std::vector getPath(const BWEM::Area* a1, const BWEM::Area* a2); + static int getQueueIndex(UnitType ut); + static std::pair getTargetArea(const BWEM::Area* startingArea, bool startLocation); + static int getUnitIndex(Unit unit); + static double getWalkDistance(TilePosition tp1, TilePosition tp2); + static bool isAttackingUnit(CUnit u); + static bool isBasePosition(TilePosition tp); + static bool isSameArea(const BWEM::Area* area1, const BWEM::Area* area2); + static int ownedBy(TilePosition tp); + static std::vector reconstructPath(std::map cameFrom, const BWEM::Area* current); + static bool unitCounted(Unit unit); + static int workerBuildingTargetCount(UnitType unit); +}; \ No newline at end of file diff --git a/packages/gmock.1.11.0/.signature.p7s b/packages/gmock.1.11.0/.signature.p7s new file mode 100644 index 0000000..8401c38 Binary files /dev/null and b/packages/gmock.1.11.0/.signature.p7s differ diff --git a/packages/gmock.1.11.0/LICENSE b/packages/gmock.1.11.0/LICENSE new file mode 100644 index 0000000..54e9979 --- /dev/null +++ b/packages/gmock.1.11.0/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/gmock.1.11.0/build/native/gmock.targets b/packages/gmock.1.11.0/build/native/gmock.targets new file mode 100644 index 0000000..7448c64 --- /dev/null +++ b/packages/gmock.1.11.0/build/native/gmock.targets @@ -0,0 +1,22 @@ + + + + + ;%(PreprocessorDefinitions) + $(MSBuildThisFileDirectory)..\..\lib\native\include\;%(AdditionalIncludeDirectories) + + + + + + ;%(PreprocessorDefinitions) + + + + + + ;%(PreprocessorDefinitions) + + + \ No newline at end of file diff --git a/packages/gmock.1.11.0/gmock.1.11.0.nupkg b/packages/gmock.1.11.0/gmock.1.11.0.nupkg new file mode 100644 index 0000000..9feb40f Binary files /dev/null and b/packages/gmock.1.11.0/gmock.1.11.0.nupkg differ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-actions.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-actions.h new file mode 100644 index 0000000..f83e1c8 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-actions.h @@ -0,0 +1,1687 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// Users can, however, define any local functors (e.g. a lambda) that +// can be used as actions. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' on +// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ + +#ifndef _WIN32_WCE +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gmock/internal/gmock-pp.h" + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +namespace testing { + +// To implement an action Foo, define: +// 1. a class FooAction that implements the ActionInterface interface, and +// 2. a factory function that creates an Action object from a +// const FooAction*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Action objects can now be copied like plain values. + +namespace internal { + +// BuiltInDefaultValueGetter::Get() returns a +// default-constructed T value. BuiltInDefaultValueGetter::Get() crashes with an error. +// +// This primary template is used when kDefaultConstructible is true. +template +struct BuiltInDefaultValueGetter { + static T Get() { return T(); } +}; +template +struct BuiltInDefaultValueGetter { + static T Get() { + Assert(false, __FILE__, __LINE__, + "Default action undefined for the function return type."); + return internal::Invalid(); + // The above statement will never be reached, but is required in + // order for this function to compile. + } +}; + +// BuiltInDefaultValue::Get() returns the "built-in" default value +// for type T, which is NULL when T is a raw pointer type, 0 when T is +// a numeric type, false when T is bool, or "" when T is string or +// std::string. In addition, in C++11 and above, it turns a +// default-constructed T value if T is default constructible. For any +// other type T, the built-in default T value is undefined, and the +// function will abort the process. +template +class BuiltInDefaultValue { + public: + // This function returns true if and only if type T has a built-in default + // value. + static bool Exists() { + return ::std::is_default_constructible::value; + } + + static T Get() { + return BuiltInDefaultValueGetter< + T, ::std::is_default_constructible::value>::Get(); + } +}; + +// This partial specialization says that we use the same built-in +// default value for T and const T. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return BuiltInDefaultValue::Exists(); } + static T Get() { return BuiltInDefaultValue::Get(); } +}; + +// This partial specialization defines the default values for pointer +// types. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return true; } + static T* Get() { return nullptr; } +}; + +// The following specializations define the default values for +// specific types we care about. +#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ + template <> \ + class BuiltInDefaultValue { \ + public: \ + static bool Exists() { return true; } \ + static type Get() { return value; } \ + } + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); + +// There's no need for a default action for signed wchar_t, as that +// type is the same as wchar_t for gcc, and invalid for MSVC. +// +// There's also no need for a default action for unsigned wchar_t, as +// that type is the same as unsigned int for gcc, and invalid for +// MSVC. +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT +#endif + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long long, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long long, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); + +#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ + +// Simple two-arg form of std::disjunction. +template +using disjunction = typename ::std::conditional::type; + +} // namespace internal + +// When an unexpected function call is encountered, Google Mock will +// let it return a default value if the user has specified one for its +// return type, or if the return type has a built-in default value; +// otherwise Google Mock won't know what value to return and will have +// to abort the process. +// +// The DefaultValue class allows a user to specify the +// default value for a type T that is both copyable and publicly +// destructible (i.e. anything that can be used as a function return +// type). The usage is: +// +// // Sets the default value for type T to be foo. +// DefaultValue::Set(foo); +template +class DefaultValue { + public: + // Sets the default value for type T; requires T to be + // copy-constructable and have a public destructor. + static void Set(T x) { + delete producer_; + producer_ = new FixedValueProducer(x); + } + + // Provides a factory function to be called to generate the default value. + // This method can be used even if T is only move-constructible, but it is not + // limited to that case. + typedef T (*FactoryFunction)(); + static void SetFactory(FactoryFunction factory) { + delete producer_; + producer_ = new FactoryValueProducer(factory); + } + + // Unsets the default value for type T. + static void Clear() { + delete producer_; + producer_ = nullptr; + } + + // Returns true if and only if the user has set the default value for type T. + static bool IsSet() { return producer_ != nullptr; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T if the user has set one; + // otherwise returns the built-in default value. Requires that Exists() + // is true, which ensures that the return value is well-defined. + static T Get() { + return producer_ == nullptr ? internal::BuiltInDefaultValue::Get() + : producer_->Produce(); + } + + private: + class ValueProducer { + public: + virtual ~ValueProducer() {} + virtual T Produce() = 0; + }; + + class FixedValueProducer : public ValueProducer { + public: + explicit FixedValueProducer(T value) : value_(value) {} + T Produce() override { return value_; } + + private: + const T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer); + }; + + class FactoryValueProducer : public ValueProducer { + public: + explicit FactoryValueProducer(FactoryFunction factory) + : factory_(factory) {} + T Produce() override { return factory_(); } + + private: + const FactoryFunction factory_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer); + }; + + static ValueProducer* producer_; +}; + +// This partial specialization allows a user to set default values for +// reference types. +template +class DefaultValue { + public: + // Sets the default value for type T&. + static void Set(T& x) { // NOLINT + address_ = &x; + } + + // Unsets the default value for type T&. + static void Clear() { address_ = nullptr; } + + // Returns true if and only if the user has set the default value for type T&. + static bool IsSet() { return address_ != nullptr; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T& if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T& Get() { + return address_ == nullptr ? internal::BuiltInDefaultValue::Get() + : *address_; + } + + private: + static T* address_; +}; + +// This specialization allows DefaultValue::Get() to +// compile. +template <> +class DefaultValue { + public: + static bool Exists() { return true; } + static void Get() {} +}; + +// Points to the user-set default value for type T. +template +typename DefaultValue::ValueProducer* DefaultValue::producer_ = nullptr; + +// Points to the user-set default value for type T&. +template +T* DefaultValue::address_ = nullptr; + +// Implement this interface to define an action for function type F. +template +class ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + ActionInterface() {} + virtual ~ActionInterface() {} + + // Performs the action. This method is not const, as in general an + // action can have side effects and be stateful. For example, a + // get-the-next-element-from-the-collection action will need to + // remember the current element. + virtual Result Perform(const ArgumentTuple& args) = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); +}; + +// An Action is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function +// of type F is called. The implementation of Action is just a +// std::shared_ptr to const ActionInterface. Don't inherit from Action! +// You can view an object implementing ActionInterface as a +// concrete action (including its current state), and an Action +// object as a handle to it. +template +class Action { + // Adapter class to allow constructing Action from a legacy ActionInterface. + // New code should create Actions from functors instead. + struct ActionAdapter { + // Adapter must be copyable to satisfy std::function requirements. + ::std::shared_ptr> impl_; + + template + typename internal::Function::Result operator()(Args&&... args) { + return impl_->Perform( + ::std::forward_as_tuple(::std::forward(args)...)); + } + }; + + template + using IsCompatibleFunctor = std::is_constructible, G>; + + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + // Constructs a null Action. Needed for storing Action objects in + // STL containers. + Action() {} + + // Construct an Action from a specified callable. + // This cannot take std::function directly, because then Action would not be + // directly constructible from lambda (it would require two conversions). + template < + typename G, + typename = typename std::enable_if, std::is_constructible, + G>>::value>::type> + Action(G&& fun) { // NOLINT + Init(::std::forward(fun), IsCompatibleFunctor()); + } + + // Constructs an Action from its implementation. + explicit Action(ActionInterface* impl) + : fun_(ActionAdapter{::std::shared_ptr>(impl)}) {} + + // This constructor allows us to turn an Action object into an + // Action, as long as F's arguments can be implicitly converted + // to Func's and Func's return type can be implicitly converted to F's. + template + explicit Action(const Action& action) : fun_(action.fun_) {} + + // Returns true if and only if this is the DoDefault() action. + bool IsDoDefault() const { return fun_ == nullptr; } + + // Performs the action. Note that this method is const even though + // the corresponding method in ActionInterface is not. The reason + // is that a const Action means that it cannot be re-bound to + // another concrete action, not that the concrete action it binds to + // cannot change state. (Think of the difference between a const + // pointer and a pointer to const.) + Result Perform(ArgumentTuple args) const { + if (IsDoDefault()) { + internal::IllegalDoDefault(__FILE__, __LINE__); + } + return internal::Apply(fun_, ::std::move(args)); + } + + private: + template + friend class Action; + + template + void Init(G&& g, ::std::true_type) { + fun_ = ::std::forward(g); + } + + template + void Init(G&& g, ::std::false_type) { + fun_ = IgnoreArgs::type>{::std::forward(g)}; + } + + template + struct IgnoreArgs { + template + Result operator()(const Args&...) const { + return function_impl(); + } + + FunctionImpl function_impl; + }; + + // fun_ is an empty function if and only if this is the DoDefault() action. + ::std::function fun_; +}; + +// The PolymorphicAction class template makes it easy to implement a +// polymorphic action (i.e. an action that can be used in mock +// functions of than one type, e.g. Return()). +// +// To define a polymorphic action, a user first provides a COPYABLE +// implementation class that has a Perform() method template: +// +// class FooAction { +// public: +// template +// Result Perform(const ArgumentTuple& args) const { +// // Processes the arguments and returns a result, using +// // std::get(args) to get the N-th (0-based) argument in the tuple. +// } +// ... +// }; +// +// Then the user creates the polymorphic action using +// MakePolymorphicAction(object) where object has type FooAction. See +// the definition of Return(void) and SetArgumentPointee(value) for +// complete examples. +template +class PolymorphicAction { + public: + explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} + + template + operator Action() const { + return Action(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + Result Perform(const ArgumentTuple& args) override { + return impl_.template Perform(args); + } + + private: + Impl impl_; + }; + + Impl impl_; +}; + +// Creates an Action from its implementation and returns it. The +// created Action object owns the implementation. +template +Action MakeAction(ActionInterface* impl) { + return Action(impl); +} + +// Creates a polymorphic action from its implementation. This is +// easier to use than the PolymorphicAction constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicAction(foo); +// vs +// PolymorphicAction(foo); +template +inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { + return PolymorphicAction(impl); +} + +namespace internal { + +// Helper struct to specialize ReturnAction to execute a move instead of a copy +// on return. Useful for move-only types, but could be used on any type. +template +struct ByMoveWrapper { + explicit ByMoveWrapper(T value) : payload(std::move(value)) {} + T payload; +}; + +// Implements the polymorphic Return(x) action, which can be used in +// any function that returns the type of x, regardless of the argument +// types. +// +// Note: The value passed into Return must be converted into +// Function::Result when this action is cast to Action rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action is a +// good place for that. +// +// The real life example of the above scenario happens when an invocation +// of gtl::Container() is passed into Return. +// +template +class ReturnAction { + public: + // Constructs a ReturnAction object from the value to be returned. + // 'value' is passed by value instead of by const reference in order + // to allow Return("string literal") to compile. + explicit ReturnAction(R value) : value_(new R(std::move(value))) {} + + // This template type conversion operator allows Return(x) to be + // used in ANY function that returns x's type. + template + operator Action() const { // NOLINT + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename Function::Result Result; + GTEST_COMPILE_ASSERT_( + !std::is_reference::value, + use_ReturnRef_instead_of_Return_to_return_a_reference); + static_assert(!std::is_void::value, + "Can't use Return() on an action expected to return `void`."); + return Action(new Impl(value_)); + } + + private: + // Implements the Return(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. ImplicitCast_ forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(const std::shared_ptr& value) + : value_before_cast_(*value), + value_(ImplicitCast_(value_before_cast_)) {} + + Result Perform(const ArgumentTuple&) override { return value_; } + + private: + GTEST_COMPILE_ASSERT_(!std::is_reference::value, + Result_cannot_be_a_reference_type); + // We save the value before casting just in case it is being cast to a + // wrapper type. + R value_before_cast_; + Result value_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl); + }; + + // Partially specialize for ByMoveWrapper. This version of ReturnAction will + // move its contents instead. + template + class Impl, F> : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const std::shared_ptr& wrapper) + : performed_(false), wrapper_(wrapper) {} + + Result Perform(const ArgumentTuple&) override { + GTEST_CHECK_(!performed_) + << "A ByMove() action should only be performed once."; + performed_ = true; + return std::move(wrapper_->payload); + } + + private: + bool performed_; + const std::shared_ptr wrapper_; + }; + + const std::shared_ptr value_; +}; + +// Implements the ReturnNull() action. +class ReturnNullAction { + public: + // Allows ReturnNull() to be used in any pointer-returning function. In C++11 + // this is enforced by returning nullptr, and in non-C++11 by asserting a + // pointer type on compile time. + template + static Result Perform(const ArgumentTuple&) { + return nullptr; + } +}; + +// Implements the Return() action. +class ReturnVoidAction { + public: + // Allows Return() to be used in any void-returning function. + template + static void Perform(const ArgumentTuple&) { + static_assert(std::is_void::value, "Result should be void."); + } +}; + +// Implements the polymorphic ReturnRef(x) action, which can be used +// in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefAction { + public: + // Constructs a ReturnRefAction object from the reference to be returned. + explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT + + // This template type conversion operator allows ReturnRef(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRef(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_(std::is_reference::value, + use_Return_instead_of_ReturnRef_to_return_a_value); + return Action(new Impl(ref_)); + } + + private: + // Implements the ReturnRef(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(T& ref) : ref_(ref) {} // NOLINT + + Result Perform(const ArgumentTuple&) override { return ref_; } + + private: + T& ref_; + }; + + T& ref_; +}; + +// Implements the polymorphic ReturnRefOfCopy(x) action, which can be +// used in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefOfCopyAction { + public: + // Constructs a ReturnRefOfCopyAction object from the reference to + // be returned. + explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT + + // This template type conversion operator allows ReturnRefOfCopy(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRefOfCopy(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_( + std::is_reference::value, + use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); + return Action(new Impl(value_)); + } + + private: + // Implements the ReturnRefOfCopy(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const T& value) : value_(value) {} // NOLINT + + Result Perform(const ArgumentTuple&) override { return value_; } + + private: + T value_; + }; + + const T value_; +}; + +// Implements the polymorphic ReturnRoundRobin(v) action, which can be +// used in any function that returns the element_type of v. +template +class ReturnRoundRobinAction { + public: + explicit ReturnRoundRobinAction(std::vector values) { + GTEST_CHECK_(!values.empty()) + << "ReturnRoundRobin requires at least one element."; + state_->values = std::move(values); + } + + template + T operator()(Args&&...) const { + return state_->Next(); + } + + private: + struct State { + T Next() { + T ret_val = values[i++]; + if (i == values.size()) i = 0; + return ret_val; + } + + std::vector values; + size_t i = 0; + }; + std::shared_ptr state_ = std::make_shared(); +}; + +// Implements the polymorphic DoDefault() action. +class DoDefaultAction { + public: + // This template type conversion operator allows DoDefault() to be + // used in any function. + template + operator Action() const { return Action(); } // NOLINT +}; + +// Implements the Assign action to set a given pointer referent to a +// particular value. +template +class AssignAction { + public: + AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} + + template + void Perform(const ArgumentTuple& /* args */) const { + *ptr_ = value_; + } + + private: + T1* const ptr_; + const T2 value_; +}; + +#if !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetErrnoAndReturn action to simulate return from +// various system calls and libc functions. +template +class SetErrnoAndReturnAction { + public: + SetErrnoAndReturnAction(int errno_value, T result) + : errno_(errno_value), + result_(result) {} + template + Result Perform(const ArgumentTuple& /* args */) const { + errno = errno_; + return result_; + } + + private: + const int errno_; + const T result_; +}; + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetArgumentPointee(x) action for any function +// whose N-th argument (0-based) is a pointer to x's type. +template +struct SetArgumentPointeeAction { + A value; + + template + void operator()(const Args&... args) const { + *::std::get(std::tie(args...)) = value; + } +}; + +// Implements the Invoke(object_ptr, &Class::Method) action. +template +struct InvokeMethodAction { + Class* const obj_ptr; + const MethodPtr method_ptr; + + template + auto operator()(Args&&... args) const + -> decltype((obj_ptr->*method_ptr)(std::forward(args)...)) { + return (obj_ptr->*method_ptr)(std::forward(args)...); + } +}; + +// Implements the InvokeWithoutArgs(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. InvokeWithoutArgs(f) can be used as an +// Action as long as f's type is compatible with F. +template +struct InvokeWithoutArgsAction { + FunctionImpl function_impl; + + // Allows InvokeWithoutArgs(f) to be used as any action whose type is + // compatible with f. + template + auto operator()(const Args&...) -> decltype(function_impl()) { + return function_impl(); + } +}; + +// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. +template +struct InvokeMethodWithoutArgsAction { + Class* const obj_ptr; + const MethodPtr method_ptr; + + using ReturnType = + decltype((std::declval()->*std::declval())()); + + template + ReturnType operator()(const Args&...) const { + return (obj_ptr->*method_ptr)(); + } +}; + +// Implements the IgnoreResult(action) action. +template +class IgnoreResultAction { + public: + explicit IgnoreResultAction(const A& action) : action_(action) {} + + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename internal::Function::Result Result; + + // Asserts at compile time that F returns void. + static_assert(std::is_void::value, "Result type should be void."); + + return Action(new Impl(action_)); + } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const A& action) : action_(action) {} + + void Perform(const ArgumentTuple& args) override { + // Performs the action and ignores its result. + action_.Perform(args); + } + + private: + // Type OriginalFunction is the same as F except that its return + // type is IgnoredValue. + typedef typename internal::Function::MakeResultIgnoredValue + OriginalFunction; + + const Action action_; + }; + + const A action_; +}; + +template +struct WithArgsAction { + InnerAction action; + + // The inner action could be anything convertible to Action. + // We use the conversion operator to detect the signature of the inner Action. + template + operator Action() const { // NOLINT + using TupleType = std::tuple; + Action::type...)> + converted(action); + + return [converted](Args... args) -> R { + return converted.Perform(std::forward_as_tuple( + std::get(std::forward_as_tuple(std::forward(args)...))...)); + }; + } +}; + +template +struct DoAllAction { + private: + template + using NonFinalType = + typename std::conditional::value, T, const T&>::type; + + template + std::vector Convert(IndexSequence) const { + return {ActionT(std::get(actions))...}; + } + + public: + std::tuple actions; + + template + operator Action() const { // NOLINT + struct Op { + std::vector...)>> converted; + Action last; + R operator()(Args... args) const { + auto tuple_args = std::forward_as_tuple(std::forward(args)...); + for (auto& a : converted) { + a.Perform(tuple_args); + } + return last.Perform(std::move(tuple_args)); + } + }; + return Op{Convert...)>>( + MakeIndexSequence()), + std::get(actions)}; + } +}; + +template +struct ReturnNewAction { + T* operator()() const { + return internal::Apply( + [](const Params&... unpacked_params) { + return new T(unpacked_params...); + }, + params); + } + std::tuple params; +}; + +template +struct ReturnArgAction { + template + auto operator()(const Args&... args) const -> + typename std::tuple_element>::type { + return std::get(std::tie(args...)); + } +}; + +template +struct SaveArgAction { + Ptr pointer; + + template + void operator()(const Args&... args) const { + *pointer = std::get(std::tie(args...)); + } +}; + +template +struct SaveArgPointeeAction { + Ptr pointer; + + template + void operator()(const Args&... args) const { + *pointer = *std::get(std::tie(args...)); + } +}; + +template +struct SetArgRefereeAction { + T value; + + template + void operator()(Args&&... args) const { + using argk_type = + typename ::std::tuple_element>::type; + static_assert(std::is_lvalue_reference::value, + "Argument must be a reference type."); + std::get(std::tie(args...)) = value; + } +}; + +template +struct SetArrayArgumentAction { + I1 first; + I2 last; + + template + void operator()(const Args&... args) const { + auto value = std::get(std::tie(args...)); + for (auto it = first; it != last; ++it, (void)++value) { + *value = *it; + } + } +}; + +template +struct DeleteArgAction { + template + void operator()(const Args&... args) const { + delete std::get(std::tie(args...)); + } +}; + +template +struct ReturnPointeeAction { + Ptr pointer; + template + auto operator()(const Args&...) const -> decltype(*pointer) { + return *pointer; + } +}; + +#if GTEST_HAS_EXCEPTIONS +template +struct ThrowAction { + T exception; + // We use a conversion operator to adapt to any return type. + template + operator Action() const { // NOLINT + T copy = exception; + return [copy](Args...) -> R { throw copy; }; + } +}; +#endif // GTEST_HAS_EXCEPTIONS + +} // namespace internal + +// An Unused object can be implicitly constructed from ANY value. +// This is handy when defining actions that ignore some or all of the +// mock function arguments. For example, given +// +// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); +// MOCK_METHOD3(Bar, double(int index, double x, double y)); +// +// instead of +// +// double DistanceToOriginWithLabel(const string& label, double x, double y) { +// return sqrt(x*x + y*y); +// } +// double DistanceToOriginWithIndex(int index, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXPECT_CALL(mock, Foo("abc", _, _)) +// .WillOnce(Invoke(DistanceToOriginWithLabel)); +// EXPECT_CALL(mock, Bar(5, _, _)) +// .WillOnce(Invoke(DistanceToOriginWithIndex)); +// +// you could write +// +// // We can declare any uninteresting argument as Unused. +// double DistanceToOrigin(Unused, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); +// EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); +typedef internal::IgnoredValue Unused; + +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. All but the last action will have a readonly view of the +// arguments. +template +internal::DoAllAction::type...> DoAll( + Action&&... action) { + return {std::forward_as_tuple(std::forward(action)...)}; +} + +// WithArg(an_action) creates an action that passes the k-th +// (0-based) argument of the mock function to an_action and performs +// it. It adapts an action accepting one argument to one that accepts +// multiple arguments. For convenience, we also provide +// WithArgs(an_action) (defined below) as a synonym. +template +internal::WithArgsAction::type, k> +WithArg(InnerAction&& action) { + return {std::forward(action)}; +} + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. +template +internal::WithArgsAction::type, k, ks...> +WithArgs(InnerAction&& action) { + return {std::forward(action)}; +} + +// WithoutArgs(inner_action) can be used in a mock function with a +// non-empty argument list to perform inner_action, which takes no +// argument. In other words, it adapts an action accepting no +// argument to one that accepts (and ignores) arguments. +template +internal::WithArgsAction::type> +WithoutArgs(InnerAction&& action) { + return {std::forward(action)}; +} + +// Creates an action that returns 'value'. 'value' is passed by value +// instead of const reference - otherwise Return("string literal") +// will trigger a compiler error about using array as initializer. +template +internal::ReturnAction Return(R value) { + return internal::ReturnAction(std::move(value)); +} + +// Creates an action that returns NULL. +inline PolymorphicAction ReturnNull() { + return MakePolymorphicAction(internal::ReturnNullAction()); +} + +// Creates an action that returns from a void function. +inline PolymorphicAction Return() { + return MakePolymorphicAction(internal::ReturnVoidAction()); +} + +// Creates an action that returns the reference to a variable. +template +inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT + return internal::ReturnRefAction(x); +} + +// Prevent using ReturnRef on reference to temporary. +template +internal::ReturnRefAction ReturnRef(R&&) = delete; + +// Creates an action that returns the reference to a copy of the +// argument. The copy is created when the action is constructed and +// lives as long as the action. +template +inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { + return internal::ReturnRefOfCopyAction(x); +} + +// Modifies the parent action (a Return() action) to perform a move of the +// argument instead of a copy. +// Return(ByMove()) actions can only be executed once and will assert this +// invariant. +template +internal::ByMoveWrapper ByMove(R x) { + return internal::ByMoveWrapper(std::move(x)); +} + +// Creates an action that returns an element of `vals`. Calling this action will +// repeatedly return the next value from `vals` until it reaches the end and +// will restart from the beginning. +template +internal::ReturnRoundRobinAction ReturnRoundRobin(std::vector vals) { + return internal::ReturnRoundRobinAction(std::move(vals)); +} + +// Creates an action that returns an element of `vals`. Calling this action will +// repeatedly return the next value from `vals` until it reaches the end and +// will restart from the beginning. +template +internal::ReturnRoundRobinAction ReturnRoundRobin( + std::initializer_list vals) { + return internal::ReturnRoundRobinAction(std::vector(vals)); +} + +// Creates an action that does the default action for the give mock function. +inline internal::DoDefaultAction DoDefault() { + return internal::DoDefaultAction(); +} + +// Creates an action that sets the variable pointed by the N-th +// (0-based) function argument to 'value'. +template +internal::SetArgumentPointeeAction SetArgPointee(T value) { + return {std::move(value)}; +} + +// The following version is DEPRECATED. +template +internal::SetArgumentPointeeAction SetArgumentPointee(T value) { + return {std::move(value)}; +} + +// Creates an action that sets a pointer referent to a given value. +template +PolymorphicAction > Assign(T1* ptr, T2 val) { + return MakePolymorphicAction(internal::AssignAction(ptr, val)); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Creates an action that sets errno and returns the appropriate error. +template +PolymorphicAction > +SetErrnoAndReturn(int errval, T result) { + return MakePolymorphicAction( + internal::SetErrnoAndReturnAction(errval, result)); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Various overloads for Invoke(). + +// Legacy function. +// Actions can now be implicitly constructed from callables. No need to create +// wrapper objects. +// This function exists for backwards compatibility. +template +typename std::decay::type Invoke(FunctionImpl&& function_impl) { + return std::forward(function_impl); +} + +// Creates an action that invokes the given method on the given object +// with the mock function's arguments. +template +internal::InvokeMethodAction Invoke(Class* obj_ptr, + MethodPtr method_ptr) { + return {obj_ptr, method_ptr}; +} + +// Creates an action that invokes 'function_impl' with no argument. +template +internal::InvokeWithoutArgsAction::type> +InvokeWithoutArgs(FunctionImpl function_impl) { + return {std::move(function_impl)}; +} + +// Creates an action that invokes the given method on the given object +// with no argument. +template +internal::InvokeMethodWithoutArgsAction InvokeWithoutArgs( + Class* obj_ptr, MethodPtr method_ptr) { + return {obj_ptr, method_ptr}; +} + +// Creates an action that performs an_action and throws away its +// result. In other words, it changes the return type of an_action to +// void. an_action MUST NOT return void, or the code won't compile. +template +inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { + return internal::IgnoreResultAction(an_action); +} + +// Creates a reference wrapper for the given L-value. If necessary, +// you can explicitly specify the type of the reference. For example, +// suppose 'derived' is an object of type Derived, ByRef(derived) +// would wrap a Derived&. If you want to wrap a const Base& instead, +// where Base is a base class of Derived, just write: +// +// ByRef(derived) +// +// N.B. ByRef is redundant with std::ref, std::cref and std::reference_wrapper. +// However, it may still be used for consistency with ByMove(). +template +inline ::std::reference_wrapper ByRef(T& l_value) { // NOLINT + return ::std::reference_wrapper(l_value); +} + +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +template +internal::ReturnNewAction::type...> ReturnNew( + Params&&... params) { + return {std::forward_as_tuple(std::forward(params)...)}; +} + +// Action ReturnArg() returns the k-th argument of the mock function. +template +internal::ReturnArgAction ReturnArg() { + return {}; +} + +// Action SaveArg(pointer) saves the k-th (0-based) argument of the +// mock function to *pointer. +template +internal::SaveArgAction SaveArg(Ptr pointer) { + return {pointer}; +} + +// Action SaveArgPointee(pointer) saves the value pointed to +// by the k-th (0-based) argument of the mock function to *pointer. +template +internal::SaveArgPointeeAction SaveArgPointee(Ptr pointer) { + return {pointer}; +} + +// Action SetArgReferee(value) assigns 'value' to the variable +// referenced by the k-th (0-based) argument of the mock function. +template +internal::SetArgRefereeAction::type> SetArgReferee( + T&& value) { + return {std::forward(value)}; +} + +// Action SetArrayArgument(first, last) copies the elements in +// source range [first, last) to the array pointed to by the k-th +// (0-based) argument, which can be either a pointer or an +// iterator. The action does not take ownership of the elements in the +// source range. +template +internal::SetArrayArgumentAction SetArrayArgument(I1 first, + I2 last) { + return {first, last}; +} + +// Action DeleteArg() deletes the k-th (0-based) argument of the mock +// function. +template +internal::DeleteArgAction DeleteArg() { + return {}; +} + +// This action returns the value pointed to by 'pointer'. +template +internal::ReturnPointeeAction ReturnPointee(Ptr pointer) { + return {pointer}; +} + +// Action Throw(exception) can be used in a mock function of any type +// to throw the given exception. Any copyable value can be thrown. +#if GTEST_HAS_EXCEPTIONS +template +internal::ThrowAction::type> Throw(T&& exception) { + return {std::forward(exception)}; +} +#endif // GTEST_HAS_EXCEPTIONS + +namespace internal { + +// A macro from the ACTION* family (defined later in gmock-generated-actions.h) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// Builds an implementation of an Action<> for some particular signature, using +// a class defined by an ACTION* macro. +template struct ActionImpl; + +template +struct ImplBase { + struct Holder { + // Allows each copy of the Action<> to get to the Impl. + explicit operator const Impl&() const { return *ptr; } + std::shared_ptr ptr; + }; + using type = typename std::conditional::value, + Impl, Holder>::type; +}; + +template +struct ActionImpl : ImplBase::type { + using Base = typename ImplBase::type; + using function_type = R(Args...); + using args_type = std::tuple; + + ActionImpl() = default; // Only defined if appropriate for Base. + explicit ActionImpl(std::shared_ptr impl) : Base{std::move(impl)} { } + + R operator()(Args&&... arg) const { + static constexpr size_t kMaxArgs = + sizeof...(Args) <= 10 ? sizeof...(Args) : 10; + return Apply(MakeIndexSequence{}, + MakeIndexSequence<10 - kMaxArgs>{}, + args_type{std::forward(arg)...}); + } + + template + R Apply(IndexSequence, IndexSequence, + const args_type& args) const { + // Impl need not be specific to the signature of action being implemented; + // only the implementing function body needs to have all of the specific + // types instantiated. Up to 10 of the args that are provided by the + // args_type get passed, followed by a dummy of unspecified type for the + // remainder up to 10 explicit args. + static constexpr ExcessiveArg kExcessArg{}; + return static_cast(*this).template gmock_PerformImpl< + /*function_type=*/function_type, /*return_type=*/R, + /*args_type=*/args_type, + /*argN_type=*/typename std::tuple_element::type...>( + /*args=*/args, std::get(args)..., + ((void)excess_id, kExcessArg)...); + } +}; + +// Stores a default-constructed Impl as part of the Action<>'s +// std::function<>. The Impl should be trivial to copy. +template +::testing::Action MakeAction() { + return ::testing::Action(ActionImpl()); +} + +// Stores just the one given instance of Impl. +template +::testing::Action MakeAction(std::shared_ptr impl) { + return ::testing::Action(ActionImpl(std::move(impl))); +} + +#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \ + , const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_ +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \ + const args_type& args GTEST_ATTRIBUTE_UNUSED_ GMOCK_PP_REPEAT( \ + GMOCK_INTERNAL_ARG_UNUSED, , 10) + +#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_ \ + const args_type& args GMOCK_PP_REPEAT(GMOCK_INTERNAL_ARG, , 10) + +#define GMOCK_INTERNAL_TEMPLATE_ARG(i, data, el) , typename arg##i##_type +#define GMOCK_ACTION_TEMPLATE_ARGS_NAMES_ \ + GMOCK_PP_TAIL(GMOCK_PP_REPEAT(GMOCK_INTERNAL_TEMPLATE_ARG, , 10)) + +#define GMOCK_INTERNAL_TYPENAME_PARAM(i, data, param) , typename param##_type +#define GMOCK_ACTION_TYPENAME_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPENAME_PARAM, , params)) + +#define GMOCK_INTERNAL_TYPE_PARAM(i, data, param) , param##_type +#define GMOCK_ACTION_TYPE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_PARAM, , params)) + +#define GMOCK_INTERNAL_TYPE_GVALUE_PARAM(i, data, param) \ + , param##_type gmock_p##i +#define GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_GVALUE_PARAM, , params)) + +#define GMOCK_INTERNAL_GVALUE_PARAM(i, data, param) \ + , std::forward(gmock_p##i) +#define GMOCK_ACTION_GVALUE_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GVALUE_PARAM, , params)) + +#define GMOCK_INTERNAL_INIT_PARAM(i, data, param) \ + , param(::std::forward(gmock_p##i)) +#define GMOCK_ACTION_INIT_PARAMS_(params) \ + GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_INIT_PARAM, , params)) + +#define GMOCK_INTERNAL_FIELD_PARAM(i, data, param) param##_type param; +#define GMOCK_ACTION_FIELD_PARAMS_(params) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_FIELD_PARAM, , params) + +#define GMOCK_INTERNAL_ACTION(name, full_name, params) \ + template \ + class full_name { \ + public: \ + explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ + : impl_(std::make_shared( \ + GMOCK_ACTION_GVALUE_PARAMS_(params))) { } \ + full_name(const full_name&) = default; \ + full_name(full_name&&) noexcept = default; \ + template \ + operator ::testing::Action() const { \ + return ::testing::internal::MakeAction(impl_); \ + } \ + private: \ + class gmock_Impl { \ + public: \ + explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ + : GMOCK_ACTION_INIT_PARAMS_(params) {} \ + template \ + return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ + GMOCK_ACTION_FIELD_PARAMS_(params) \ + }; \ + std::shared_ptr impl_; \ + }; \ + template \ + inline full_name name( \ + GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) { \ + return full_name( \ + GMOCK_ACTION_GVALUE_PARAMS_(params)); \ + } \ + template \ + template \ + return_type full_name::gmock_Impl:: \ + gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +} // namespace internal + +// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored. +#define ACTION(name) \ + class name##Action { \ + public: \ + explicit name##Action() noexcept {} \ + name##Action(const name##Action&) noexcept {} \ + template \ + operator ::testing::Action() const { \ + return ::testing::internal::MakeAction(); \ + } \ + private: \ + class gmock_Impl { \ + public: \ + template \ + return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ + }; \ + }; \ + inline name##Action name() GTEST_MUST_USE_RESULT_; \ + inline name##Action name() { return name##Action(); } \ + template \ + return_type name##Action::gmock_Impl::gmock_PerformImpl( \ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP, (__VA_ARGS__)) + +#define ACTION_P2(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP2, (__VA_ARGS__)) + +#define ACTION_P3(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP3, (__VA_ARGS__)) + +#define ACTION_P4(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP4, (__VA_ARGS__)) + +#define ACTION_P5(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP5, (__VA_ARGS__)) + +#define ACTION_P6(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP6, (__VA_ARGS__)) + +#define ACTION_P7(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP7, (__VA_ARGS__)) + +#define ACTION_P8(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP8, (__VA_ARGS__)) + +#define ACTION_P9(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP9, (__VA_ARGS__)) + +#define ACTION_P10(name, ...) \ + GMOCK_INTERNAL_ACTION(name, name##ActionP10, (__VA_ARGS__)) + +} // namespace testing + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-cardinalities.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-cardinalities.h new file mode 100644 index 0000000..71f9a48 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-cardinalities.h @@ -0,0 +1,157 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used cardinalities. More +// cardinalities can be defined by the user implementing the +// CardinalityInterface interface if necessary. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ + +#include +#include +#include // NOLINT +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// To implement a cardinality Foo, define: +// 1. a class FooCardinality that implements the +// CardinalityInterface interface, and +// 2. a factory function that creates a Cardinality object from a +// const FooCardinality*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Cardinality objects can now be copied like plain values. + +// The implementation of a cardinality. +class CardinalityInterface { + public: + virtual ~CardinalityInterface() {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return 0; } + virtual int ConservativeUpperBound() const { return INT_MAX; } + + // Returns true if and only if call_count calls will satisfy this + // cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true if and only if call_count calls will saturate this + // cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; + +// A Cardinality is a copyable and IMMUTABLE (except by assignment) +// object that specifies how many times a mock function is expected to +// be called. The implementation of Cardinality is just a std::shared_ptr +// to const CardinalityInterface. Don't inherit from Cardinality! +class GTEST_API_ Cardinality { + public: + // Constructs a null cardinality. Needed for storing Cardinality + // objects in STL containers. + Cardinality() {} + + // Constructs a Cardinality from its implementation. + explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } + int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } + + // Returns true if and only if call_count calls will satisfy this + // cardinality. + bool IsSatisfiedByCallCount(int call_count) const { + return impl_->IsSatisfiedByCallCount(call_count); + } + + // Returns true if and only if call_count calls will saturate this + // cardinality. + bool IsSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count); + } + + // Returns true if and only if call_count calls will over-saturate this + // cardinality, i.e. exceed the maximum number of allowed calls. + bool IsOverSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count) && + !impl_->IsSatisfiedByCallCount(call_count); + } + + // Describes self to an ostream + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the given actual call count to an ostream. + static void DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os); + + private: + std::shared_ptr impl_; +}; + +// Creates a cardinality that allows at least n calls. +GTEST_API_ Cardinality AtLeast(int n); + +// Creates a cardinality that allows at most n calls. +GTEST_API_ Cardinality AtMost(int n); + +// Creates a cardinality that allows any number of calls. +GTEST_API_ Cardinality AnyNumber(); + +// Creates a cardinality that allows between min and max calls. +GTEST_API_ Cardinality Between(int min, int max); + +// Creates a cardinality that allows exactly n calls. +GTEST_API_ Cardinality Exactly(int n); + +// Creates a cardinality from its implementation. +inline Cardinality MakeCardinality(const CardinalityInterface* c) { + return Cardinality(c); +} + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-function-mocker.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-function-mocker.h new file mode 100644 index 0000000..d7b4248 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-function-mocker.h @@ -0,0 +1,479 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements MOCK_METHOD. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT +#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ // NOLINT + +#include // IWYU pragma: keep +#include // IWYU pragma: keep + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-pp.h" + +namespace testing { +namespace internal { +template +using identity_t = T; + +template +struct ThisRefAdjuster { + template + using AdjustT = typename std::conditional< + std::is_const::type>::value, + typename std::conditional::value, + const T&, const T&&>::type, + typename std::conditional::value, T&, + T&&>::type>::type; + + template + static AdjustT Adjust(const MockType& mock) { + return static_cast>(const_cast(mock)); + } +}; + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; +} // namespace testing + +#define MOCK_METHOD(...) \ + GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_2(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \ + GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ()) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \ + GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \ + GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \ + GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ + GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \ + GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ + GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ + GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \ + GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \ + GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec), \ + GMOCK_INTERNAL_GET_CALLTYPE(_Spec), GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \ + (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args))) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_6(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHOD_ARG_7(...) \ + GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) + +#define GMOCK_INTERNAL_WRONG_ARITY(...) \ + static_assert( \ + false, \ + "MOCK_METHOD must be called with 3 or 4 arguments. _Ret, " \ + "_MethodName, _Args and optionally _Spec. _Args and _Spec must be " \ + "enclosed in parentheses. If _Ret is a type with unprotected commas, " \ + "it must also be enclosed in parentheses.") + +#define GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Tuple) \ + static_assert( \ + GMOCK_PP_IS_ENCLOSED_PARENS(_Tuple), \ + GMOCK_PP_STRINGIZE(_Tuple) " should be enclosed in parentheses.") + +#define GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(_N, ...) \ + static_assert( \ + std::is_function<__VA_ARGS__>::value, \ + "Signature must be a function type, maybe return type contains " \ + "unprotected comma."); \ + static_assert( \ + ::testing::tuple_size::ArgumentTuple>::value == _N, \ + "This method does not take " GMOCK_PP_STRINGIZE( \ + _N) " arguments. Parenthesize all types with unprotected commas.") + +#define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec) + +#define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness, \ + _Override, _Final, _NoexceptSpec, \ + _CallType, _RefSpec, _Signature) \ + typename ::testing::internal::Function::Result \ + GMOCK_INTERNAL_EXPAND(_CallType) \ + _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N)) \ + GMOCK_PP_IF(_Constness, const, ) _RefSpec _NoexceptSpec \ + GMOCK_PP_IF(_Override, override, ) GMOCK_PP_IF(_Final, final, ) { \ + GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .SetOwnerAndName(this, #_MethodName); \ + return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .Invoke(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, _Signature, _N)); \ + } \ + ::testing::MockSpec gmock_##_MethodName( \ + GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N)) \ + GMOCK_PP_IF(_Constness, const, ) _RefSpec { \ + GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this); \ + return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ + .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N)); \ + } \ + ::testing::MockSpec gmock_##_MethodName( \ + const ::testing::internal::WithoutMatchers&, \ + GMOCK_PP_IF(_Constness, const, )::testing::internal::Function< \ + GMOCK_PP_REMOVE_PARENS(_Signature)>*) const _RefSpec _NoexceptSpec { \ + return ::testing::internal::ThisRefAdjuster::Adjust(*this) \ + .gmock_##_MethodName(GMOCK_PP_REPEAT( \ + GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \ + } \ + mutable ::testing::FunctionMocker \ + GMOCK_MOCKER_(_N, _Constness, _MethodName) + +#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__ + +// Five Valid modifiers. +#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple)) + +#define GMOCK_INTERNAL_HAS_OVERRIDE(_Tuple) \ + GMOCK_PP_HAS_COMMA( \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_OVERRIDE, ~, _Tuple)) + +#define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \ + GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple)) + +#define GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT, ~, _Tuple) + +#define GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT(_i, _, _elem) \ + GMOCK_PP_IF( \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \ + _elem, ) + +#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple) + +#define GMOCK_INTERNAL_REF_SPEC_IF_REF(_i, _, _elem) \ + GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \ + GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), ) + +#define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple) + +#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \ + static_assert( \ + (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \ + GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) + \ + GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1, \ + GMOCK_PP_STRINGIZE( \ + _elem) " cannot be recognized as a valid specification modifier."); + +// Modifiers implementation. +#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CONST_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_CONST_I_const , + +#define GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_OVERRIDE_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_OVERRIDE_I_override , + +#define GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_FINAL_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_FINAL_I_final , + +#define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept , + +#define GMOCK_INTERNAL_DETECT_REF(_i, _, _elem) \ + GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_REF_I_, _elem) + +#define GMOCK_INTERNAL_DETECT_REF_I_ref , + +#define GMOCK_INTERNAL_UNPACK_ref(x) x + +#define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem) \ + GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem), \ + GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \ + (_elem) + +// TODO(iserna): GMOCK_INTERNAL_IS_CALLTYPE and +// GMOCK_INTERNAL_GET_VALUE_CALLTYPE needed more expansions to work on windows +// maybe they can be simplified somehow. +#define GMOCK_INTERNAL_IS_CALLTYPE(_arg) \ + GMOCK_INTERNAL_IS_CALLTYPE_I( \ + GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg)) +#define GMOCK_INTERNAL_IS_CALLTYPE_I(_arg) GMOCK_PP_IS_ENCLOSED_PARENS(_arg) + +#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE(_arg) \ + GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I( \ + GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg)) +#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \ + GMOCK_PP_IDENTITY _arg + +#define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype + +// Note: The use of `identity_t` here allows _Ret to represent return types that +// would normally need to be specified in a different way. For example, a method +// returning a function pointer must be written as +// +// fn_ptr_return_t (*method(method_args_t...))(fn_ptr_args_t...) +// +// But we only support placing the return type at the beginning. To handle this, +// we wrap all calls in identity_t, so that a declaration will be expanded to +// +// identity_t method(method_args_t...) +// +// This allows us to work around the syntactic oddities of function/method +// types. +#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args) \ + ::testing::internal::identity_t( \ + GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args)) + +#define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_elem), GMOCK_PP_REMOVE_PARENS, \ + GMOCK_PP_IDENTITY) \ + (_elem) + +#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ + gmock_a##_i + +#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + ::std::forward(gmock_a##_i) + +#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + GMOCK_INTERNAL_MATCHER_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ + gmock_a##_i + +#define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \ + GMOCK_PP_COMMA_IF(_i) \ + gmock_a##_i + +#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \ + GMOCK_PP_COMMA_IF(_i) \ + ::testing::A() + +#define GMOCK_INTERNAL_ARG_O(_i, ...) \ + typename ::testing::internal::Function<__VA_ARGS__>::template Arg<_i>::type + +#define GMOCK_INTERNAL_MATCHER_O(_i, ...) \ + const ::testing::Matcher::template Arg<_i>::type>& + +#define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__) +#define MOCK_METHOD1(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 1, __VA_ARGS__) +#define MOCK_METHOD2(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 2, __VA_ARGS__) +#define MOCK_METHOD3(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 3, __VA_ARGS__) +#define MOCK_METHOD4(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 4, __VA_ARGS__) +#define MOCK_METHOD5(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 5, __VA_ARGS__) +#define MOCK_METHOD6(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 6, __VA_ARGS__) +#define MOCK_METHOD7(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 7, __VA_ARGS__) +#define MOCK_METHOD8(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 8, __VA_ARGS__) +#define MOCK_METHOD9(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 9, __VA_ARGS__) +#define MOCK_METHOD10(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, , m, 10, __VA_ARGS__) + +#define MOCK_CONST_METHOD0(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 0, __VA_ARGS__) +#define MOCK_CONST_METHOD1(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 1, __VA_ARGS__) +#define MOCK_CONST_METHOD2(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 2, __VA_ARGS__) +#define MOCK_CONST_METHOD3(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 3, __VA_ARGS__) +#define MOCK_CONST_METHOD4(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 4, __VA_ARGS__) +#define MOCK_CONST_METHOD5(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 5, __VA_ARGS__) +#define MOCK_CONST_METHOD6(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 6, __VA_ARGS__) +#define MOCK_CONST_METHOD7(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 7, __VA_ARGS__) +#define MOCK_CONST_METHOD8(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 8, __VA_ARGS__) +#define MOCK_CONST_METHOD9(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 9, __VA_ARGS__) +#define MOCK_CONST_METHOD10(m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, , m, 10, __VA_ARGS__) + +#define MOCK_METHOD0_T(m, ...) MOCK_METHOD0(m, __VA_ARGS__) +#define MOCK_METHOD1_T(m, ...) MOCK_METHOD1(m, __VA_ARGS__) +#define MOCK_METHOD2_T(m, ...) MOCK_METHOD2(m, __VA_ARGS__) +#define MOCK_METHOD3_T(m, ...) MOCK_METHOD3(m, __VA_ARGS__) +#define MOCK_METHOD4_T(m, ...) MOCK_METHOD4(m, __VA_ARGS__) +#define MOCK_METHOD5_T(m, ...) MOCK_METHOD5(m, __VA_ARGS__) +#define MOCK_METHOD6_T(m, ...) MOCK_METHOD6(m, __VA_ARGS__) +#define MOCK_METHOD7_T(m, ...) MOCK_METHOD7(m, __VA_ARGS__) +#define MOCK_METHOD8_T(m, ...) MOCK_METHOD8(m, __VA_ARGS__) +#define MOCK_METHOD9_T(m, ...) MOCK_METHOD9(m, __VA_ARGS__) +#define MOCK_METHOD10_T(m, ...) MOCK_METHOD10(m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T(m, ...) MOCK_CONST_METHOD0(m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T(m, ...) MOCK_CONST_METHOD1(m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T(m, ...) MOCK_CONST_METHOD2(m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T(m, ...) MOCK_CONST_METHOD3(m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T(m, ...) MOCK_CONST_METHOD4(m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T(m, ...) MOCK_CONST_METHOD5(m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T(m, ...) MOCK_CONST_METHOD6(m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T(m, ...) MOCK_CONST_METHOD7(m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T(m, ...) MOCK_CONST_METHOD8(m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T(m, ...) MOCK_CONST_METHOD9(m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T(m, ...) MOCK_CONST_METHOD10(m, __VA_ARGS__) + +#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 0, __VA_ARGS__) +#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 1, __VA_ARGS__) +#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 2, __VA_ARGS__) +#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 3, __VA_ARGS__) +#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 4, __VA_ARGS__) +#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 5, __VA_ARGS__) +#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 6, __VA_ARGS__) +#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 7, __VA_ARGS__) +#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 8, __VA_ARGS__) +#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 9, __VA_ARGS__) +#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 10, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 0, __VA_ARGS__) +#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 1, __VA_ARGS__) +#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 2, __VA_ARGS__) +#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 3, __VA_ARGS__) +#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 4, __VA_ARGS__) +#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 5, __VA_ARGS__) +#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 6, __VA_ARGS__) +#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 7, __VA_ARGS__) +#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 8, __VA_ARGS__) +#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 9, __VA_ARGS__) +#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 10, __VA_ARGS__) + +#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) + +#define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \ + GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ + args_num, ::testing::internal::identity_t<__VA_ARGS__>); \ + GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ + args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, , \ + (::testing::internal::identity_t<__VA_ARGS__>)) + +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h new file mode 100644 index 0000000..981af78 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h @@ -0,0 +1,1884 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-actions.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include +#include + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: + static Result Perform(Impl* impl, const ::std::tuple<>& args) { + return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, std::get<0>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, std::get<0>(args), + std::get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, + std::get<0>(args), std::get<1>(args), std::get<2>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, + std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, + std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, + std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), std::get<5>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, + std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), std::get<5>(args), + std::get<6>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), std::get<5>(args), + std::get<6>(args), std::get<7>(args), ExcessiveArg(), ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), std::get<5>(args), + std::get<6>(args), std::get<7>(args), std::get<8>(args), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tuple& args) { + return impl->template gmock_PerformImpl(args, std::get<0>(args), std::get<1>(args), std::get<2>(args), + std::get<3>(args), std::get<4>(args), std::get<5>(args), + std::get<6>(args), std::get<7>(args), std::get<8>(args), + std::get<9>(args)); + } +}; + +} // namespace internal +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// Users can, however, define any local functors (e.g. a lambda) that +// can be used as actions. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' on +// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_, \ + const arg0_type& arg0 GTEST_ATTRIBUTE_UNUSED_, \ + const arg1_type& arg1 GTEST_ATTRIBUTE_UNUSED_, \ + const arg2_type& arg2 GTEST_ATTRIBUTE_UNUSED_, \ + const arg3_type& arg3 GTEST_ATTRIBUTE_UNUSED_, \ + const arg4_type& arg4 GTEST_ATTRIBUTE_UNUSED_, \ + const arg5_type& arg5 GTEST_ATTRIBUTE_UNUSED_, \ + const arg6_type& arg6 GTEST_ATTRIBUTE_UNUSED_, \ + const arg7_type& arg7 GTEST_ATTRIBUTE_UNUSED_, \ + const arg8_type& arg8 GTEST_ATTRIBUTE_UNUSED_, \ + const arg9_type& arg9 GTEST_ATTRIBUTE_UNUSED_ + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(::std::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. +#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 +#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) kind0 name0, kind1 name1 +#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) kind0 name0, kind1 name1, kind2 name2 +#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3 +#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ + kind2 name2, kind3 name3, kind4 name4 +#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 +#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ + kind5 name5, kind6 name6 +#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ + kind4 name4, kind5 name5, kind6 name6, kind7 name7 +#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ + kind8 name8 +#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ + kind6 name6, kind7 name7, kind8 name8, kind9 name9 + +// Lists the template parameters. +#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 +#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) name0, name1 +#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) name0, name1, name2 +#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) name0, name1, name2, name3 +#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ + name4 +#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ + name2, name3, name4, name5 +#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) name0, name1, name2, name3, name4, name5, name6 +#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 +#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ + name6, name7, name8 +#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ + name3, name4, name5, name6, name7, name8, name9 + +// Declares the types of value parameters. +#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ + typename p0##_type, typename p1##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ + typename p0##_type, typename p1##_type, typename p2##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type, typename p8##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ + typename p2##_type, typename p3##_type, typename p4##_type, \ + typename p5##_type, typename p6##_type, typename p7##_type, \ + typename p8##_type, typename p9##_type + +// Initializes the value parameters. +#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ + () +#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ + (p0##_type gmock_p0) : p0(::std::move(gmock_p0)) +#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ + (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)) +#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ + (p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)) +#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)) +#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)) +#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)) +#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)) +#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)) +#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)) +#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \ + p9(::std::move(gmock_p9)) + +// Declares the fields for storing the value parameters. +#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; +#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ + p1##_type p1; +#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ + p1##_type p1; p2##_type p2; +#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ + p1##_type p1; p2##_type p2; p3##_type p3; +#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; +#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; +#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; +#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; p7##_type p7; +#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; +#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ + p9##_type p9; + +// Lists the value parameters. +#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 +#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 +#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 +#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 +#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ + p2, p3, p4 +#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ + p1, p2, p3, p4, p5 +#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0, p1, p2, p3, p4, p5, p6 +#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0, p1, p2, p3, p4, p5, p6, p7 +#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 +#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 + +// Lists the value parameter types. +#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ + p1##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ + p1##_type, p2##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + p0##_type, p1##_type, p2##_type, p3##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ + p6##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type, p9##_type + +// Declares the value parameters. +#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 +#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ + p1##_type p1 +#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ + p1##_type p1, p2##_type p2 +#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3 +#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 +#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5 +#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6 +#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6, p7##_type p7 +#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 +#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9 + +// The suffix of the class template implementing the action template. +#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P +#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 +#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 +#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 +#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 +#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 +#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 +#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) P8 +#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) P9 +#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) P10 + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION(name)\ + class name##Action {\ + public:\ + name##Action() {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl() {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl());\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Action);\ + };\ + inline name##Action name() {\ + return name##Action();\ + }\ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##Action::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, p0)\ + template \ + class name##ActionP {\ + public:\ + explicit name##ActionP(p0##_type gmock_p0) : \ + p0(::std::forward(gmock_p0)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl(p0##_type gmock_p0) : \ + p0(::std::forward(gmock_p0)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0));\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP);\ + };\ + template \ + inline name##ActionP name(p0##_type p0) {\ + return name##ActionP(p0);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P2(name, p0, p1)\ + template \ + class name##ActionP2 {\ + public:\ + name##ActionP2(p0##_type gmock_p0, \ + p1##_type gmock_p1) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, \ + p1##_type gmock_p1) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP2);\ + };\ + template \ + inline name##ActionP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##ActionP2(p0, p1);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP2::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P3(name, p0, p1, p2)\ + template \ + class name##ActionP3 {\ + public:\ + name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP3);\ + };\ + template \ + inline name##ActionP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##ActionP3(p0, p1, p2);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP3::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P4(name, p0, p1, p2, p3)\ + template \ + class name##ActionP4 {\ + public:\ + name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP4);\ + };\ + template \ + inline name##ActionP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##ActionP4(p0, p1, \ + p2, p3);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP4::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P5(name, p0, p1, p2, p3, p4)\ + template \ + class name##ActionP5 {\ + public:\ + name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP5);\ + };\ + template \ + inline name##ActionP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##ActionP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP5::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\ + template \ + class name##ActionP6 {\ + public:\ + name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP6);\ + };\ + template \ + inline name##ActionP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##ActionP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP6::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\ + template \ + class name##ActionP7 {\ + public:\ + name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP7);\ + };\ + template \ + inline name##ActionP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##ActionP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP7::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\ + template \ + class name##ActionP8 {\ + public:\ + name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP8);\ + };\ + template \ + inline name##ActionP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##ActionP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP8::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\ + template \ + class name##ActionP9 {\ + public:\ + name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)), \ + p8(::std::forward(gmock_p8)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)), \ + p8(::std::forward(gmock_p8)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP9);\ + };\ + template \ + inline name##ActionP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##ActionP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP9::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\ + template \ + class name##ActionP10 {\ + public:\ + name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)), \ + p8(::std::forward(gmock_p8)), \ + p9(::std::forward(gmock_p9)) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(::std::forward(gmock_p0)), \ + p1(::std::forward(gmock_p1)), \ + p2(::std::forward(gmock_p2)), \ + p3(::std::forward(gmock_p3)), \ + p4(::std::forward(gmock_p4)), \ + p5(::std::forward(gmock_p5)), \ + p6(::std::forward(gmock_p6)), \ + p7(::std::forward(gmock_p7)), \ + p8(::std::forward(gmock_p8)), \ + p9(::std::forward(gmock_p9)) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, \ + const arg0_type& arg0, const arg1_type& arg1, \ + const arg2_type& arg2, const arg3_type& arg3, \ + const arg4_type& arg4, const arg5_type& arg5, \ + const arg6_type& arg6, const arg7_type& arg7, \ + const arg8_type& arg8, const arg9_type& arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP10);\ + };\ + template \ + inline name##ActionP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##ActionP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP10::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +namespace testing { + + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +namespace internal { +namespace invoke_argument { + +// Appears in InvokeArgumentAdl's argument list to help avoid +// accidental calls to user functions of the same name. +struct AdlTag {}; + +// InvokeArgumentAdl - a helper for InvokeArgument. +// The basic overloads are provided here for generic functors. +// Overloads for other custom-callables are provided in the +// internal/custom/callback-actions.h header. + +template +R InvokeArgumentAdl(AdlTag, F f) { + return f(); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1) { + return f(a1); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2) { + return f(a1, a2); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3) { + return f(a1, a2, a3); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4) { + return f(a1, a2, a3, a4); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return f(a1, a2, a3, a4, a5); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return f(a1, a2, a3, a4, a5, a6); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7) { + return f(a1, a2, a3, a4, a5, a6, a7); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8) { + return f(a1, a2, a3, a4, a5, a6, a7, a8); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9) { + return f(a1, a2, a3, a4, a5, a6, a7, a8, a9); +} +template +R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9, A10 a10) { + return f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); +} +} // namespace invoke_argument +} // namespace internal + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args)); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p0)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(p0, p1)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + return new T(); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + return new T(p0); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + return new T(p0, p1); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return new T(p0, p1, p2); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return new T(p0, p1, p2, p3); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return new T(p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return new T(p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return new T(p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +// Include any custom callback actions added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gmock/internal/custom/gmock-generated-actions.h" + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h.pump b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h.pump new file mode 100644 index 0000000..209603c --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-actions.h.pump @@ -0,0 +1,627 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-actions.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$}} This meta comment fixes auto-indentation in editors. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include +#include + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: +$range i 0..n +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + template <$for j, [[typename A$j]]> +]]]] +$range j 0..i-1 +$var As = [[$for j, [[A$j]]]] +$var as = [[$for j, [[std::get<$j>(args)]]]] +$range k 1..n-i +$var eas = [[$for k, [[ExcessiveArg()]]]] +$var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]] +$template + static Result Perform(Impl* impl, const ::std::tuple<$As>& args) { + return impl->template gmock_PerformImpl<$As>(args, $arg_list); + } + +]] +}; + +} // namespace internal +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P$n to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// Users can, however, define any local functors (e.g. a lambda) that +// can be used as actions. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' on +// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md + +$range i 0..n +$range k 0..n-1 + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_ +$for k [[, \ + const arg$k[[]]_type& arg$k GTEST_ATTRIBUTE_UNUSED_]] + + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(::std::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. + +$range j 1..n +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_DECL_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[kind$m name$m]] + + +]] + +// Lists the template parameters. + +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_LIST_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[name$m]] + + +]] + +// Declares the types of value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, typename p$j##_type]] + + +]] + +// Initializes the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_INIT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])\ + ($for j, [[p$j##_type gmock_p$j]])$if i>0 [[ : ]]$for j, [[p$j(::std::move(gmock_p$j))]] + + +]] + +// Declares the fields for storing the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DEFN_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[p$j##_type p$j; ]] + + +]] + +// Lists the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j, [[p$j]] + + +]] + +// Lists the value parameter types. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, p$j##_type]] + + +]] + +// Declares the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$for j, [[p$j##_type p$j]] + + +]] + +// The suffix of the class template implementing the action template. +$for i [[ + + +$range j 0..i-1 +#define GMOCK_INTERNAL_COUNT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$if i==1 [[P]] $elif i>=2 [[P$i]] +]] + + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +$range k 0..n-1 + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + explicit GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$for k, [[typename arg$k[[]]_type]]>\ + return_type gmock_PerformImpl(const args_type& args[[]] +$for k [[, const arg$k[[]]_type& arg$k]]) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var class_name = [[name##Action[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::forward(gmock_p$j))]]]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var typename_arg_types = [[$for k, [[typename arg$k[[]]_type]]]] +$var arg_types_and_names = [[$for k, [[const arg$k[[]]_type& arg$k]]]] +$var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]] + $else [[ACTION_P$i]]]] + +#define $macro_name(name$for j [[, p$j]])\$template + class $class_name {\ + public:\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$typename_arg_types>\ + return_type gmock_PerformImpl(const args_type& args, [[]] +$arg_types_and_names) const;\$param_field_decls + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl($params));\ + }\$param_field_decls2 + private:\ + GTEST_DISALLOW_ASSIGN_($class_name);\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + template <$typename_arg_types>\ + typename ::testing::internal::Function::Result\ + $class_name$param_types::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const +]] +$$ } // This meta comment fixes auto-indentation in Emacs. It won't +$$ // show up in the generated code. + + +namespace testing { + + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +namespace internal { +namespace invoke_argument { + +// Appears in InvokeArgumentAdl's argument list to help avoid +// accidental calls to user functions of the same name. +struct AdlTag {}; + +// InvokeArgumentAdl - a helper for InvokeArgument. +// The basic overloads are provided here for generic functors. +// Overloads for other custom-callables are provided in the +// internal/custom/callback-actions.h header. + +$range i 0..n +$for i +[[ +$range j 1..i + +template +R InvokeArgumentAdl(AdlTag, F f[[$for j [[, A$j a$j]]]]) { + return f([[$for j, [[a$j]]]]); +} +]] + +} // namespace invoke_argument +} // namespace internal + +$range i 0..n +$for i [[ +$range j 0..i-1 + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])) { + using internal::invoke_argument::InvokeArgumentAdl; + return InvokeArgumentAdl( + internal::invoke_argument::AdlTag(), + ::std::get(args)$for j [[, p$j]]); +} + +]] + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +$range i 0..n +$for i [[ +$range j 0..i-1 +$var ps = [[$for j, [[p$j]]]] + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_$i[[]]_VALUE_PARAMS($ps)) { + return new T($ps); +} + +]] + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +// Include any custom callback actions added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gmock/internal/custom/gmock-generated-actions.h" + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h new file mode 100644 index 0000000..cd95781 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h @@ -0,0 +1,752 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-function-mockers.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include +#include + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +namespace testing { +namespace internal { +// Removes the given pointer; this is a helper for the expectation setter method +// for parameterless matchers. +// +// We want to make sure that the user cannot set a parameterless expectation on +// overloaded methods, including methods which are overloaded on const. Example: +// +// class MockClass { +// MOCK_METHOD0(GetName, string&()); +// MOCK_CONST_METHOD0(GetName, const string&()); +// }; +// +// TEST() { +// // This should be an error, as it's not clear which overload is expected. +// EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value)); +// } +// +// Here are the generated expectation-setter methods: +// +// class MockClass { +// // Overload 1 +// MockSpec gmock_GetName() { ... } +// // Overload 2. Declared const so that the compiler will generate an +// // error when trying to resolve between this and overload 4 in +// // 'gmock_GetName(WithoutMatchers(), nullptr)'. +// MockSpec gmock_GetName( +// const WithoutMatchers&, const Function*) const { +// // Removes const from this, calls overload 1 +// return AdjustConstness_(this)->gmock_GetName(); +// } +// +// // Overload 3 +// const string& gmock_GetName() const { ... } +// // Overload 4 +// MockSpec gmock_GetName( +// const WithoutMatchers&, const Function*) const { +// // Does not remove const, calls overload 3 +// return AdjustConstness_const(this)->gmock_GetName(); +// } +// } +// +template +const MockType* AdjustConstness_const(const MockType* mock) { + return mock; +} + +// Removes const from and returns the given pointer; this is a helper for the +// expectation setter method for parameterless matchers. +template +MockType* AdjustConstness_(const MockType* mock) { + return const_cast(mock); +} + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// GMOCK_RESULT_(tn, F) expands to the result type of function type F. +// We define this as a variadic macro in case F contains unprotected +// commas (the same reason that we use variadic macros in other places +// in this file). +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Result + +// The type of argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, N, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::template Arg::type + +// The matcher type for argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, N, ...) \ + const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \ + static_assert(0 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + ) constness { \ + GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(0, constness, Method).Invoke(); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method() constness { \ + GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(0, constness, Method).With(); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \ + static_assert(1 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ + GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(1, constness, \ + Method).Invoke(::std::forward(gmock_a1)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ + GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \ + static_assert(2 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2) constness { \ + GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(2, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \ + GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \ + static_assert(3 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, \ + __VA_ARGS__) gmock_a3) constness { \ + GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(3, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \ + GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \ + static_assert(4 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ + GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(4, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ + GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \ + static_assert(5 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5) constness { \ + GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(5, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness { \ + GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \ + static_assert(6 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, \ + __VA_ARGS__) gmock_a6) constness { \ + GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(6, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5), \ + ::std::forward(gmock_a6)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness { \ + GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \ + static_assert(7 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ + GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(7, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5), \ + ::std::forward(gmock_a6), \ + ::std::forward(gmock_a7)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ + GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \ + static_assert(8 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \ + __VA_ARGS__) gmock_a8) constness { \ + GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(8, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5), \ + ::std::forward(gmock_a6), \ + ::std::forward(gmock_a7), \ + ::std::forward(gmock_a8)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness { \ + GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \ + static_assert(9 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \ + __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, \ + __VA_ARGS__) gmock_a9) constness { \ + GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(9, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5), \ + ::std::forward(gmock_a6), \ + ::std::forward(gmock_a7), \ + ::std::forward(gmock_a8), \ + ::std::forward(gmock_a9)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness { \ + GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(9, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \ + gmock_a9); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \ + Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \ + static_assert(10 == \ + ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \ + "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \ + __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \ + __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \ + __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \ + GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(10, constness, \ + Method).Invoke(::std::forward(gmock_a1), \ + ::std::forward(gmock_a2), \ + ::std::forward(gmock_a3), \ + ::std::forward(gmock_a4), \ + ::std::forward(gmock_a5), \ + ::std::forward(gmock_a6), \ + ::std::forward(gmock_a7), \ + ::std::forward(gmock_a8), \ + ::std::forward(gmock_a9), \ + ::std::forward(gmock_a10)); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ + GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ + GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ + GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ + GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ + GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ + GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ + GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ + GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \ + GMOCK_MATCHER_(tn, 10, \ + __VA_ARGS__) gmock_a10) constness { \ + GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(10, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \ + gmock_a10); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method(::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A(), \ + ::testing::A()); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \ + Method) + +#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__) +#define MOCK_METHOD1(m, ...) GMOCK_METHOD1_(, , , m, __VA_ARGS__) +#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__) +#define MOCK_METHOD3(m, ...) GMOCK_METHOD3_(, , , m, __VA_ARGS__) +#define MOCK_METHOD4(m, ...) GMOCK_METHOD4_(, , , m, __VA_ARGS__) +#define MOCK_METHOD5(m, ...) GMOCK_METHOD5_(, , , m, __VA_ARGS__) +#define MOCK_METHOD6(m, ...) GMOCK_METHOD6_(, , , m, __VA_ARGS__) +#define MOCK_METHOD7(m, ...) GMOCK_METHOD7_(, , , m, __VA_ARGS__) +#define MOCK_METHOD8(m, ...) GMOCK_METHOD8_(, , , m, __VA_ARGS__) +#define MOCK_METHOD9(m, ...) GMOCK_METHOD9_(, , , m, __VA_ARGS__) +#define MOCK_METHOD10(m, ...) GMOCK_METHOD10_(, , , m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD1(m, ...) GMOCK_METHOD1_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD3(m, ...) GMOCK_METHOD3_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD4(m, ...) GMOCK_METHOD4_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD5(m, ...) GMOCK_METHOD5_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD6(m, ...) GMOCK_METHOD6_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD7(m, ...) GMOCK_METHOD7_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD8(m, ...) GMOCK_METHOD8_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD9(m, ...) GMOCK_METHOD9_(, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD10(m, ...) GMOCK_METHOD10_(, const, , m, __VA_ARGS__) + +#define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD1_T(m, ...) GMOCK_METHOD1_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD2_T(m, ...) GMOCK_METHOD2_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD3_T(m, ...) GMOCK_METHOD3_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD4_T(m, ...) GMOCK_METHOD4_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD5_T(m, ...) GMOCK_METHOD5_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD6_T(m, ...) GMOCK_METHOD6_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD7_T(m, ...) GMOCK_METHOD7_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD8_T(m, ...) GMOCK_METHOD8_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD9_T(m, ...) GMOCK_METHOD9_(typename, , , m, __VA_ARGS__) +#define MOCK_METHOD10_T(m, ...) GMOCK_METHOD10_(typename, , , m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T(m, ...) \ + GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T(m, ...) \ + GMOCK_METHOD1_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T(m, ...) \ + GMOCK_METHOD2_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T(m, ...) \ + GMOCK_METHOD3_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T(m, ...) \ + GMOCK_METHOD4_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T(m, ...) \ + GMOCK_METHOD5_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T(m, ...) \ + GMOCK_METHOD6_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T(m, ...) \ + GMOCK_METHOD7_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T(m, ...) \ + GMOCK_METHOD8_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T(m, ...) \ + GMOCK_METHOD9_(typename, const, , m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T(m, ...) \ + GMOCK_METHOD10_(typename, const, , m, __VA_ARGS__) + +#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(, , ct, m, __VA_ARGS__) +#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(, , ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(, const, ct, m, __VA_ARGS__) + +#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(typename, , ct, m, __VA_ARGS__) +#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(typename, , ct, m, __VA_ARGS__) + +#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD1_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD2_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD3_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD4_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD5_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD6_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD7_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD8_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD9_(typename, const, ct, m, __VA_ARGS__) +#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__) + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h.pump b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h.pump new file mode 100644 index 0000000..a56e132 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-function-mockers.h.pump @@ -0,0 +1,227 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert +$$ it to gmock-generated-function-mockers.h. +$$ +$var n = 10 $$ The maximum arity we support. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include +#include + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +namespace testing { +namespace internal { + +$range i 0..n +// Removes the given pointer; this is a helper for the expectation setter method +// for parameterless matchers. +// +// We want to make sure that the user cannot set a parameterless expectation on +// overloaded methods, including methods which are overloaded on const. Example: +// +// class MockClass { +// MOCK_METHOD0(GetName, string&()); +// MOCK_CONST_METHOD0(GetName, const string&()); +// }; +// +// TEST() { +// // This should be an error, as it's not clear which overload is expected. +// EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value)); +// } +// +// Here are the generated expectation-setter methods: +// +// class MockClass { +// // Overload 1 +// MockSpec gmock_GetName() { ... } +// // Overload 2. Declared const so that the compiler will generate an +// // error when trying to resolve between this and overload 4 in +// // 'gmock_GetName(WithoutMatchers(), nullptr)'. +// MockSpec gmock_GetName( +// const WithoutMatchers&, const Function*) const { +// // Removes const from this, calls overload 1 +// return AdjustConstness_(this)->gmock_GetName(); +// } +// +// // Overload 3 +// const string& gmock_GetName() const { ... } +// // Overload 4 +// MockSpec gmock_GetName( +// const WithoutMatchers&, const Function*) const { +// // Does not remove const, calls overload 3 +// return AdjustConstness_const(this)->gmock_GetName(); +// } +// } +// +template +const MockType* AdjustConstness_const(const MockType* mock) { + return mock; +} + +// Removes const from and returns the given pointer; this is a helper for the +// expectation setter method for parameterless matchers. +template +MockType* AdjustConstness_(const MockType* mock) { + return const_cast(mock); +} + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// GMOCK_RESULT_(tn, F) expands to the result type of function type F. +// We define this as a variadic macro in case F contains unprotected +// commas (the same reason that we use variadic macros in other places +// in this file). +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::Result + +// The type of argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, N, ...) \ + tn ::testing::internal::Function<__VA_ARGS__>::template Arg::type + +// The matcher type for argument N of the given function type. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, N, ...) \ + const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + + +$for i [[ +$range j 1..i +$var arg_as = [[$for j, [[GMOCK_ARG_(tn, $j, __VA_ARGS__) gmock_a$j]]]] +$var as = [[$for j, \ + [[::std::forward(gmock_a$j)]]]] +$var matcher_arg_as = [[$for j, \ + [[GMOCK_MATCHER_(tn, $j, __VA_ARGS__) gmock_a$j]]]] +$var matcher_as = [[$for j, [[gmock_a$j]]]] +$var anything_matchers = [[$for j, \ + [[::testing::A()]]]] +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD$i[[]]_(tn, constness, ct, Method, ...) \ + static_assert($i == ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, "MOCK_METHOD must match argument count.");\ + GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ + $arg_as) constness { \ + GMOCK_MOCKER_($i, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_($i, constness, Method).Invoke($as); \ + } \ + ::testing::MockSpec<__VA_ARGS__> \ + gmock_##Method($matcher_arg_as) constness { \ + GMOCK_MOCKER_($i, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_($i, constness, Method).With($matcher_as); \ + } \ + ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ + const ::testing::internal::WithoutMatchers&, \ + constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ + return ::testing::internal::AdjustConstness_##constness(this)-> \ + gmock_##Method($anything_matchers); \ + } \ + mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_($i, constness, Method) + + +]] +$for i [[ +#define MOCK_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, , , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, const, , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T(m, ...) GMOCK_METHOD$i[[]]_(typename, , , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T(m, ...) \ + GMOCK_METHOD$i[[]]_(typename, const, , m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(, , ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(, const, ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(typename, , ct, m, __VA_ARGS__) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ + GMOCK_METHOD$i[[]]_(typename, const, ct, m, __VA_ARGS__) + +]] + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h new file mode 100644 index 0000000..690a57f --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h @@ -0,0 +1,1097 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-matchers.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include +#include +#include "gmock/gmock-matchers.h" + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// std::string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on +// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md + +#define MATCHER(name, description)\ + class name##Matcher {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl()\ + {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple<>()));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl());\ + }\ + name##Matcher() {\ + }\ + private:\ + };\ + inline name##Matcher name() {\ + return name##Matcher();\ + }\ + template \ + bool name##Matcher::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P(name, p0, description)\ + template \ + class name##MatcherP {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + explicit gmock_Impl(p0##_type gmock_p0)\ + : p0(::std::move(gmock_p0)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0));\ + }\ + explicit name##MatcherP(p0##_type gmock_p0) : p0(::std::move(gmock_p0)) {\ + }\ + p0##_type const p0;\ + private:\ + };\ + template \ + inline name##MatcherP name(p0##_type p0) {\ + return name##MatcherP(p0);\ + }\ + template \ + template \ + bool name##MatcherP::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P2(name, p0, p1, description)\ + template \ + class name##MatcherP2 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1));\ + }\ + name##MatcherP2(p0##_type gmock_p0, \ + p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + private:\ + };\ + template \ + inline name##MatcherP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##MatcherP2(p0, p1);\ + }\ + template \ + template \ + bool name##MatcherP2::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P3(name, p0, p1, p2, description)\ + template \ + class name##MatcherP3 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2));\ + }\ + name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + private:\ + };\ + template \ + inline name##MatcherP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##MatcherP3(p0, p1, p2);\ + }\ + template \ + template \ + bool name##MatcherP3::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P4(name, p0, p1, p2, p3, description)\ + template \ + class name##MatcherP4 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, \ + p1, p2, p3)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3));\ + }\ + name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + private:\ + };\ + template \ + inline name##MatcherP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##MatcherP4(p0, \ + p1, p2, p3);\ + }\ + template \ + template \ + bool name##MatcherP4::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\ + template \ + class name##MatcherP5 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, p3, p4)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + private:\ + };\ + template \ + inline name##MatcherP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##MatcherP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + bool name##MatcherP5::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\ + template \ + class name##MatcherP6 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, p3, p4, p5)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + private:\ + };\ + template \ + inline name##MatcherP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##MatcherP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + bool name##MatcherP6::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\ + template \ + class name##MatcherP7 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \ + p6(::std::move(gmock_p6)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, p3, p4, p5, \ + p6)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6));\ + }\ + name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + private:\ + };\ + template \ + inline name##MatcherP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##MatcherP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + bool name##MatcherP7::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\ + template \ + class name##MatcherP8 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \ + p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, \ + p3, p4, p5, p6, p7)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7));\ + }\ + name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + private:\ + };\ + template \ + inline name##MatcherP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##MatcherP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + bool name##MatcherP8::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\ + template \ + class name##MatcherP9 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \ + p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \ + p8(::std::move(gmock_p8)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + p8##_type const p8;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8));\ + }\ + name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + p8##_type const p8;\ + private:\ + };\ + template \ + inline name##MatcherP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##MatcherP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + bool name##MatcherP9::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\ + template \ + class name##MatcherP10 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9)\ + : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \ + p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \ + p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \ + p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \ + p8(::std::move(gmock_p8)), p9(::std::move(gmock_p9)) {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + p8##_type const p8;\ + p9##_type const p9;\ + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\ + }\ + name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \ + p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \ + p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \ + p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \ + p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \ + p9(::std::move(gmock_p9)) {\ + }\ + p0##_type const p0;\ + p1##_type const p1;\ + p2##_type const p2;\ + p3##_type const p3;\ + p4##_type const p4;\ + p5##_type const p5;\ + p6##_type const p6;\ + p7##_type const p7;\ + p8##_type const p8;\ + p9##_type const p9;\ + private:\ + };\ + template \ + inline name##MatcherP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##MatcherP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + bool name##MatcherP10::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h.pump b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h.pump new file mode 100644 index 0000000..ae90917 --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-generated-matchers.h.pump @@ -0,0 +1,346 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert +$$ it to gmock-generated-matchers.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$ }} This line fixes auto-indentation of the following code in Emacs. +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include +#include +#include "gmock/gmock-matchers.h" + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// std::string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on +// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md + +$range i 0..n +$for i + +[[ +$var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]] + $else [[MATCHER_P$i]]]] +$var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var template = [[$if i==0 [[]] $else [[ + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::move(gmock_p$j))]]]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::move(gmock_p$j))]]]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type const p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type const p$j;\ +]]]] + +#define $macro_name(name$for j [[, p$j]], description)\$template + class $class_name {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface<\ + GTEST_REFERENCE_TO_CONST_(arg_type)> {\ + public:\ + [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\ + $impl_inits {}\ + virtual bool MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\$param_field_decls + private:\ + ::std::string FormatDescription(bool negation) const {\ + ::std::string gmock_description = (description);\ + if (!gmock_description.empty()) {\ + return gmock_description;\ + }\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name, \ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]])));\ + }\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl($params));\ + }\ + [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {\ + }\$param_field_decls2 + private:\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + bool $class_name$param_types::gmock_Impl::MatchAndExplain(\ + GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const +]] + + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/packages/gmock.1.11.0/lib/native/include/gmock/gmock-matchers.h b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-matchers.h new file mode 100644 index 0000000..bb770ed --- /dev/null +++ b/packages/gmock.1.11.0/lib/native/include/gmock/gmock-matchers.h @@ -0,0 +1,5392 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Google Mock - a framework for writing C++ mock classes. +// +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// std::string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope as templates cannot be +// declared inside of a local class. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on +// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md +// +// This file also implements some commonly used argument matchers. More +// matchers can be defined by the user implementing the +// MatcherInterface interface if necessary. +// +// See googletest/include/gtest/gtest-matchers.h for the definition of class +// Matcher, class MatcherInterface, and others. + +// GOOGLETEST_CM0002 DO NOT DELETE + +#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ +#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#include +#include +#include +#include +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gmock/internal/gmock-pp.h" +#include "gtest/gtest.h" + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GMOCK_MAYBE_5046_ 5046 +#else +#define GMOCK_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GMOCK_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherImpl that implements the +// MatcherInterface interface, and +// 2. a factory function that creates a Matcher object from a +// FooMatcherImpl*. +// +// The two-level delegation design makes it possible to allow a user +// to write "v" instead of "Eq(v)" where a Matcher is expected, which +// is impossible if we pass matchers by pointers. It also eases +// ownership management as Matcher objects can now be copied like +// plain values. + +// A match result listener that stores the explanation in a string. +class StringMatchResultListener : public MatchResultListener { + public: + StringMatchResultListener() : MatchResultListener(&ss_) {} + + // Returns the explanation accumulated so far. + std::string str() const { return ss_.str(); } + + // Clears the explanation accumulated so far. + void Clear() { ss_.str(""); } + + private: + ::std::stringstream ss_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); +}; + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// The MatcherCastImpl class template is a helper for implementing +// MatcherCast(). We need this helper in order to partially +// specialize the implementation of MatcherCast() (C++ allows +// class/struct templates to be partially specialized, but not +// function templates.). + +// This general version is used when MatcherCast()'s argument is a +// polymorphic matcher (i.e. something that can be converted to a +// Matcher but is not one yet; for example, Eq(value)) or a value (for +// example, "hello"). +template +class MatcherCastImpl { + public: + static Matcher Cast(const M& polymorphic_matcher_or_value) { + // M can be a polymorphic matcher, in which case we want to use + // its conversion operator to create Matcher. Or it can be a value + // that should be passed to the Matcher's constructor. + // + // We can't call Matcher(polymorphic_matcher_or_value) when M is a + // polymorphic matcher because it'll be ambiguous if T has an implicit + // constructor from M (this usually happens when T has an implicit + // constructor from any type). + // + // It won't work to unconditionally implicit_cast + // polymorphic_matcher_or_value to Matcher because it won't trigger + // a user-defined conversion from M to T if one exists (assuming M is + // a value). + return CastImpl(polymorphic_matcher_or_value, + std::is_convertible>{}, + std::is_convertible{}); + } + + private: + template + static Matcher CastImpl(const M& polymorphic_matcher_or_value, + std::true_type /* convertible_to_matcher */, + std::integral_constant) { + // M is implicitly convertible to Matcher, which means that either + // M is a polymorphic matcher or Matcher has an implicit constructor + // from M. In both cases using the implicit conversion will produce a + // matcher. + // + // Even if T has an implicit constructor from M, it won't be called because + // creating Matcher would require a chain of two user-defined conversions + // (first to create T from M and then to create Matcher from T). + return polymorphic_matcher_or_value; + } + + // M can't be implicitly converted to Matcher, so M isn't a polymorphic + // matcher. It's a value of a type implicitly convertible to T. Use direct + // initialization to create a matcher. + static Matcher CastImpl(const M& value, + std::false_type /* convertible_to_matcher */, + std::true_type /* convertible_to_T */) { + return Matcher(ImplicitCast_(value)); + } + + // M can't be implicitly converted to either Matcher or T. Attempt to use + // polymorphic matcher Eq(value) in this case. + // + // Note that we first attempt to perform an implicit cast on the value and + // only fall back to the polymorphic Eq() matcher afterwards because the + // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end + // which might be undefined even when Rhs is implicitly convertible to Lhs + // (e.g. std::pair vs. std::pair). + // + // We don't define this method inline as we need the declaration of Eq(). + static Matcher CastImpl(const M& value, + std::false_type /* convertible_to_matcher */, + std::false_type /* convertible_to_T */); +}; + +// This more specialized version is used when MatcherCast()'s argument +// is already a Matcher. This only compiles when type T can be +// statically converted to type U. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& source_matcher) { + return Matcher(new Impl(source_matcher)); + } + + private: + class Impl : public MatcherInterface { + public: + explicit Impl(const Matcher& source_matcher) + : source_matcher_(source_matcher) {} + + // We delegate the matching logic to the source matcher. + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + using FromType = typename std::remove_cv::type>::type>::type; + using ToType = typename std::remove_cv::type>::type>::type; + // Do not allow implicitly converting base*/& to derived*/&. + static_assert( + // Do not trigger if only one of them is a pointer. That implies a + // regular conversion and not a down_cast. + (std::is_pointer::type>::value != + std::is_pointer::type>::value) || + std::is_same::value || + !std::is_base_of::value, + "Can't implicitly convert from to "); + + // Do the cast to `U` explicitly if necessary. + // Otherwise, let implicit conversions do the trick. + using CastType = + typename std::conditional::value, + T&, U>::type; + + return source_matcher_.MatchAndExplain(static_cast(x), + listener); + } + + void DescribeTo(::std::ostream* os) const override { + source_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + source_matcher_.DescribeNegationTo(os); + } + + private: + const Matcher source_matcher_; + }; +}; + +// This even more specialized version is used for efficiently casting +// a matcher to its own type. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& matcher) { return matcher; } +}; + +// Template specialization for parameterless Matcher. +template +class MatcherBaseImpl { + public: + MatcherBaseImpl() = default; + + template + operator ::testing::Matcher() const { // NOLINT(runtime/explicit) + return ::testing::Matcher(new + typename Derived::template gmock_Impl()); + } +}; + +// Template specialization for Matcher with parameters. +template