[
  {
    "path": ".gitignore",
    "content": "Binaries\nBuild\nDerivedDataCache\nIntermediate\nSaved\nTEMP\nTemp\nPy\n.vscode\n.vs\n*.VC.db\n*.opensdf\n*.opendb\n*.sdf\n*.sln\n*.suo\n*.xcodeproj\n*.xcworkspace\nPython\n\n\n__pycache__\n*-datas\n\n# private*\nprivate*\nContent/Model3D\nPlugins\n7-Zip\nUHMP.uproject\nEnvDesignTutorial.pptx\n\n\n# f'.\\\\7-Zip\\\\7z.exe a -tzip -mx4 ./Build/uhmp-big-file-v{desired_version}.zip  ./Content/Model3D   ./Plugins  ./7-Zip  ./UHMP.uproject  ./EnvDesignTutorial.pptx'\n# ./7-Zip/7z.exe a  -tzip -mx4 ./Build/tt.zip   -ir!Content\\Model3D\\Flying\\Meshes   7-Zip\nContent/StarterContent\n"
  },
  {
    "path": "BuildLinuxRender.py",
    "content": "\"\"\"\nAfter you have installed the toolchain for cross-compilation, \nyou must open the solution for the engine in Rider (or VisualStudio) and \nbuild (rebuild is not necessary) the editor for Win64 (yes, that is Win64 and not Linux). \nThis will create the needed folder “Engine/Binaries/Win64/Linux”. \nThen restart your editor, and packaging LinuxServer will work.\n\"\"\"\n\nimport subprocess, sys, shutil, time, os\n\nEnginePath = \"F:/UnrealEngine-4.27.2-release/Engine\"\nassert os.path.exists(EnginePath), f\"Cannot find Unreal Engine at this path {EnginePath}\"\nWindows_Only = False\nBuild = 'Test' # Development/Test/shipping\nPlatform = 'Linux'  # Win64/Linux\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\ntime_mark = time.strftime(\"%Y-%m-%d-%H-%M\", time.localtime())\ntry:\n    shutil.rmtree('Build/LinuxNoEditor')\nexcept:\n    pass\n\nprint亮绿(f'********* Begin Build: {Build} On {Platform} ***********')\n# build server\npath = os.path.abspath('./').replace(r'\\\\', '/')\n\nprocess = subprocess.Popen([\n    f\"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    f\"-ScriptsForProject={path}/UHMP.uproject\",  \n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    f\"-project={path}/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    f\"-archivedirectory={path}/Build\",\n    \"-package \",\n    f\"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph\",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    f\"-targetplatform={Platform}\",\n    \"-build\",\n    \"-target=UHMP\",\n    \"-serverconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\"\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n"
  },
  {
    "path": "BuildLinuxServer.py",
    "content": "\"\"\"\nAfter you have installed the toolchain for cross-compilation, \nyou must open the solution for the engine in Rider (or VisualStudio) and \nbuild (rebuild is not necessary) the editor for Win64 (yes, that is Win64 and not Linux). \nThis will create the needed folder “Engine/Binaries/Win64/Linux”. \nThen restart your editor, and packaging LinuxServer will work.\n\"\"\"\n\nimport subprocess, sys, shutil, time, os\n\nEnginePath = \"F:/UnrealEngine-4.27.2-release/Engine\"\nassert os.path.exists(EnginePath), f\"Cannot find Unreal Engine at this path {EnginePath}\"\nWindows_Only = False\nBuild = 'Test' # Development/Test/shipping\nPlatform = 'Linux'  # Win64/Linux\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\ntime_mark = time.strftime(\"%Y-%m-%d-%H-%M\", time.localtime())\ntry:\n    shutil.rmtree('Build/LinuxServer')\nexcept:\n    pass\n\nprint亮绿(f'********* Begin Build: {Build} On {Platform} ***********')\n# build server\npath = os.path.abspath('./').replace(r'\\\\', '/')\n\nprocess = subprocess.Popen([\n    f\"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    f\"-ScriptsForProject={path}/UHMP.uproject\",  \n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    f\"-project={path}/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    f\"-archivedirectory={path}/Build\",\n    \"-package \",\n    f\"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph\",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    f\"-targetplatform={Platform}\",\n    \"-build\",\n    \"-target=UHMPServer\",\n    \"-serverconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\"\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n"
  },
  {
    "path": "BuildWindowsRender.py",
    "content": "import subprocess, sys, shutil, time, os\n\nEnginePath = \"F:/UnrealEngine-4.27.2-release/Engine\"\nassert os.path.exists(EnginePath), f\"Cannot find Unreal Engine at this path {EnginePath}\"\nWindows_Only = False\nBuild = 'Test' # Development/Test/shipping\nPlatform = 'Win64'  # Win64/Linux\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\ntime_mark = time.strftime(\"%Y-%m-%d-%H-%M\", time.localtime())\ntry:\n    shutil.rmtree('Build/WindowsNoEditor')\nexcept:\n    pass\n\nprint亮绿(f'********* Begin Build: {Build} On {Platform} ***********')\n# build server\npath = os.path.abspath('./').replace(r'\\\\', '/')\n\nprocess = subprocess.Popen([\n    f\"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    f\"-ScriptsForProject={path}/UHMP.uproject\",  \n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    f\"-project={path}/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    f\"-archivedirectory={path}/Build\",\n    \"-package \",\n    f\"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph\",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    f\"-targetplatform={Platform}\",\n    \"-build\",\n    \"-target=UHMP\",\n    \"-serverconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\"\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n"
  },
  {
    "path": "BuildWindowsServer.py",
    "content": "import subprocess, sys, shutil, time, os\n\nEnginePath = \"F:/UnrealEngine-4.27.2-release/Engine\"\nassert os.path.exists(EnginePath), f\"Cannot find Unreal Engine at this path {EnginePath}\"\nWindows_Only = False\nBuild = 'Test' # Development/Test/shipping\nPlatform = 'Win64'  # Win64/Linux\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\ntime_mark = time.strftime(\"%Y-%m-%d-%H-%M\", time.localtime())\ntry:\n    shutil.rmtree('Build/WindowsServer')\nexcept:\n    pass\n\nprint亮绿(f'********* Begin Build: {Build} On {Platform} ***********')\n# build server\npath = os.path.abspath('./').replace(r'\\\\', '/')\n\nprocess = subprocess.Popen([\n    f\"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    f\"-ScriptsForProject={path}/UHMP.uproject\",  \n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    f\"-project={path}/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    f\"-archivedirectory={path}/Build\",\n    \"-package \",\n    f\"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph\",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    f\"-targetplatform={Platform}\",\n    \"-build\",\n    \"-target=UHMPServer\",\n    \"-serverconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\"\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n"
  },
  {
    "path": "Config/DefaultEditor.ini",
    "content": "\n"
  },
  {
    "path": "Config/DefaultEditorPerProjectUserSettings.ini",
    "content": "[/Script/BlueprintGraph.BlueprintEditorSettings]\nbDrawMidpointArrowsInBlueprints=False\nbShowGraphInstructionText=True\nbHideUnrelatedNodes=False\nbShowShortTooltips=True\nbSplitContextTargetSettings=True\nbExposeAllMemberComponentFunctions=True\nbShowContextualFavorites=False\nbExposeDeprecatedFunctions=False\nbCompactCallOnMemberNodes=False\nbFlattenFavoritesMenus=True\nbFavorPureCastNodes=False\nbAutoCastObjectConnections=False\nbShowViewportOnSimulate=False\nbShowInheritedVariables=False\nbAlwaysShowInterfacesInOverrides=True\nbShowParentClassInOverrides=True\nbShowEmptySections=True\nbShowAccessSpecifier=False\nbSpawnDefaultBlueprintNodes=True\nbHideConstructionScriptComponentsInDetailsView=True\nbHostFindInBlueprintsInGlobalTab=True\nbNavigateToNativeFunctionsFromCallNodes=True\nBookmarks=()\nbIncludeCommentNodesInBookmarksTab=True\nbShowBookmarksForCurrentDocumentOnlyInTab=False\nGraphEditorQuickJumps=()\nSaveOnCompile=SoC_Never\nbJumpToNodeErrors=False\nbAllowExplicitImpureNodeDisabling=True\nbShowActionMenuItemSignatures=False\nbBlueprintNodeUniqueNames=False\nbShowDetailedCompileResults=False\nCompileEventDisplayThresholdMs=5\nNodeTemplateCacheCapMB=20.000000\n\n"
  },
  {
    "path": "Config/DefaultEngine.ini",
    "content": "[/Script/EngineSettings.GameMapsSettings]\nGameDefaultMap=/Game/Maps/AutoEntry.AutoEntry\nEditorStartupMap=/Game/Maps/UhmapLargeScale.UhmapLargeScale\nServerDefaultMap=/Game/Maps/AutoEntry.AutoEntry\n\n[/Script/HardwareTargeting.HardwareTargetingSettings]\nTargetedHardwareClass=Desktop\nAppliedTargetedHardwareClass=Desktop\nDefaultGraphicsPerformance=Maximum\nAppliedDefaultGraphicsPerformance=Maximum\n\n[/Script/Engine.Engine]\n+ActiveGameNameRedirects=(OldGameName=\"TP_BlankBP\",NewGameName=\"/Script/UHMP\")\n+ActiveGameNameRedirects=(OldGameName=\"/Script/TP_BlankBP\",NewGameName=\"/Script/UHMP\")\n+ActiveGameNameRedirects=(OldGameName=\"/Script/MyProject2\", NewGameName=\"/Script/UHMP\")\nCustomTimeStepClassName=None\nbUseFixedFrameRate=True\nFixedFrameRate=64.000000\n\n[/Script/Engine.RendererSettings]\nr.DefaultFeature.AutoExposure=False\nr.DefaultFeature.AutoExposure.Method=0\nr.DefaultFeature.MotionBlur=False\n\n[/Script/Engine.CollisionProfile]\n-Profiles=(Name=\"NoCollision\",CollisionEnabled=NoCollision,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"Camera\",Response=ECR_Ignore)),HelpMessage=\"No collision\",bCanModify=False)\n-Profiles=(Name=\"BlockAll\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"WorldStatic\",CustomResponses=,HelpMessage=\"WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. \",bCanModify=False)\n-Profiles=(Name=\"OverlapAll\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Overlap),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. \",bCanModify=False)\n-Profiles=(Name=\"BlockAllDynamic\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"WorldDynamic\",CustomResponses=,HelpMessage=\"WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. \",bCanModify=False)\n-Profiles=(Name=\"OverlapAllDynamic\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Overlap),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. \",bCanModify=False)\n-Profiles=(Name=\"IgnoreOnlyPawn\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.\",bCanModify=False)\n-Profiles=(Name=\"OverlapOnlyPawn\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. \",bCanModify=False)\n-Profiles=(Name=\"Pawn\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Pawn object. Can be used for capsule of any playerable character or AI. \",bCanModify=False)\n-Profiles=(Name=\"Spectator\",CollisionEnabled=QueryOnly,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Block),(Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"WorldDynamic\",Response=ECR_Ignore),(Channel=\"Camera\",Response=ECR_Ignore),(Channel=\"PhysicsBody\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore),(Channel=\"Destructible\",Response=ECR_Ignore)),HelpMessage=\"Pawn object that ignores all other actors except WorldStatic.\",bCanModify=False)\n-Profiles=(Name=\"CharacterMesh\",CollisionEnabled=QueryOnly,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Pawn object that is used for Character Mesh. All other channels will be set to default.\",bCanModify=False)\n-Profiles=(Name=\"PhysicsActor\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"PhysicsBody\",CustomResponses=,HelpMessage=\"Simulating actors\",bCanModify=False)\n-Profiles=(Name=\"Destructible\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"Destructible\",CustomResponses=,HelpMessage=\"Destructible actors\",bCanModify=False)\n-Profiles=(Name=\"InvisibleWall\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"WorldStatic object that is invisible.\",bCanModify=False)\n-Profiles=(Name=\"InvisibleWallDynamic\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that is invisible.\",bCanModify=False)\n-Profiles=(Name=\"Trigger\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldDynamic object that is used for trigger. All other channels will be set to default.\",bCanModify=False)\n-Profiles=(Name=\"Ragdoll\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"PhysicsBody\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Simulating Skeletal Mesh Component. All other channels will be set to default.\",bCanModify=False)\n-Profiles=(Name=\"Vehicle\",CollisionEnabled=QueryAndPhysics,ObjectTypeName=\"Vehicle\",CustomResponses=,HelpMessage=\"Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.\",bCanModify=False)\n-Profiles=(Name=\"UI\",CollisionEnabled=QueryOnly,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Block),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. \",bCanModify=False)\n+Profiles=(Name=\"NoCollision\",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"Camera\",Response=ECR_Ignore)),HelpMessage=\"No collision\")\n+Profiles=(Name=\"BlockAll\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"WorldStatic\",CustomResponses=,HelpMessage=\"WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. \")\n+Profiles=(Name=\"OverlapAll\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Overlap),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. \")\n+Profiles=(Name=\"BlockAllDynamic\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=,HelpMessage=\"WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. \")\n+Profiles=(Name=\"OverlapAllDynamic\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Overlap),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. \")\n+Profiles=(Name=\"IgnoreOnlyPawn\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.\")\n+Profiles=(Name=\"OverlapOnlyPawn\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. \")\n+Profiles=(Name=\"Pawn\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Pawn object. Can be used for capsule of any playerable character or AI. \")\n+Profiles=(Name=\"Spectator\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"WorldStatic\"),(Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"WorldDynamic\",Response=ECR_Ignore),(Channel=\"Camera\",Response=ECR_Ignore),(Channel=\"PhysicsBody\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore),(Channel=\"Destructible\",Response=ECR_Ignore)),HelpMessage=\"Pawn object that ignores all other actors except WorldStatic.\")\n+Profiles=(Name=\"CharacterMesh\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"Pawn\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Vehicle\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Pawn object that is used for Character Mesh. All other channels will be set to default.\")\n+Profiles=(Name=\"PhysicsActor\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"PhysicsBody\",CustomResponses=,HelpMessage=\"Simulating actors\")\n+Profiles=(Name=\"Destructible\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"Destructible\",CustomResponses=,HelpMessage=\"Destructible actors\")\n+Profiles=(Name=\"InvisibleWall\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"WorldStatic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"WorldStatic object that is invisible.\")\n+Profiles=(Name=\"InvisibleWallDynamic\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"WorldDynamic object that is invisible.\")\n+Profiles=(Name=\"Trigger\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\",Response=ECR_Ignore),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldDynamic object that is used for trigger. All other channels will be set to default.\")\n+Profiles=(Name=\"Ragdoll\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"PhysicsBody\",CustomResponses=((Channel=\"Pawn\",Response=ECR_Ignore),(Channel=\"Visibility\",Response=ECR_Ignore)),HelpMessage=\"Simulating Skeletal Mesh Component. All other channels will be set to default.\")\n+Profiles=(Name=\"Vehicle\",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName=\"Vehicle\",CustomResponses=,HelpMessage=\"Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.\")\n+Profiles=(Name=\"UI\",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName=\"WorldDynamic\",CustomResponses=((Channel=\"WorldStatic\",Response=ECR_Overlap),(Channel=\"Pawn\",Response=ECR_Overlap),(Channel=\"Visibility\"),(Channel=\"WorldDynamic\",Response=ECR_Overlap),(Channel=\"Camera\",Response=ECR_Overlap),(Channel=\"PhysicsBody\",Response=ECR_Overlap),(Channel=\"Vehicle\",Response=ECR_Overlap),(Channel=\"Destructible\",Response=ECR_Overlap)),HelpMessage=\"WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. \")\n+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name=\"Missile\")\n+DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name=\"Agent\")\n+DefaultChannelResponses=(Channel=ECC_GameTraceChannel3,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name=\"Env\")\n-ProfileRedirects=(OldName=\"BlockingVolume\",NewName=\"InvisibleWall\")\n-ProfileRedirects=(OldName=\"InterpActor\",NewName=\"IgnoreOnlyPawn\")\n-ProfileRedirects=(OldName=\"StaticMeshComponent\",NewName=\"BlockAllDynamic\")\n-ProfileRedirects=(OldName=\"SkeletalMeshActor\",NewName=\"PhysicsActor\")\n-ProfileRedirects=(OldName=\"InvisibleActor\",NewName=\"InvisibleWallDynamic\")\n+ProfileRedirects=(OldName=\"BlockingVolume\",NewName=\"InvisibleWall\")\n+ProfileRedirects=(OldName=\"InterpActor\",NewName=\"IgnoreOnlyPawn\")\n+ProfileRedirects=(OldName=\"StaticMeshComponent\",NewName=\"BlockAllDynamic\")\n+ProfileRedirects=(OldName=\"SkeletalMeshActor\",NewName=\"PhysicsActor\")\n+ProfileRedirects=(OldName=\"InvisibleActor\",NewName=\"InvisibleWallDynamic\")\n-CollisionChannelRedirects=(OldName=\"Static\",NewName=\"WorldStatic\")\n-CollisionChannelRedirects=(OldName=\"Dynamic\",NewName=\"WorldDynamic\")\n-CollisionChannelRedirects=(OldName=\"VehicleMovement\",NewName=\"Vehicle\")\n-CollisionChannelRedirects=(OldName=\"PawnMovement\",NewName=\"Pawn\")\n+CollisionChannelRedirects=(OldName=\"Static\",NewName=\"WorldStatic\")\n+CollisionChannelRedirects=(OldName=\"Dynamic\",NewName=\"WorldDynamic\")\n+CollisionChannelRedirects=(OldName=\"VehicleMovement\",NewName=\"Vehicle\")\n+CollisionChannelRedirects=(OldName=\"PawnMovement\",NewName=\"Pawn\")\n\n[/Script/NavigationSystem.RecastNavMesh]\nCellSize=5.000000\nCellHeight=10.000000\nAgentRadius=106.453094\nAgentHeight=74.311516\nTileSizeUU=1000.000000\nAgentMaxStepHeight=100.000000\nAgentMaxSlope=44.000000\nRuntimeGeneration=Static\nbDrawPolyEdges=False\n\n[URL]\nGameName=UHMP\n\n[/Script/NavigationSystem.NavigationSystemV1]\nDefaultAgentName=None\nCrowdManagerClass=/Script/AIModule.CrowdManager\nbAutoCreateNavigationData=True\nbSpawnNavDataInNavBoundsLevel=False\nbAllowClientSideNavigation=False\nbShouldDiscardSubLevelNavData=True\nbTickWhilePaused=False\nbInitialBuildingLocked=False\nbSkipAgentHeightCheckWhenPickingNavData=False\nbGenerateNavigationOnlyAroundNavigationInvokers=False\nActiveTilesUpdateInterval=1.000000\nDataGatheringMode=Instant\nDirtyAreaWarningSizeThreshold=-1.000000\n+SupportedAgents=(Name=\"AirportAgents\",Color=(B=0,G=255,R=140,A=164),DefaultQueryExtent=(X=500.000000,Y=500.000000,Z=500.000000),NavDataClass=/Script/NavigationSystem.RecastNavMesh,AgentRadius=130.000000,AgentHeight=100.000000,AgentStepHeight=50.000000,NavWalkingSearchHeightScale=0.500000,PreferredNavData=/Script/NavigationSystem.RecastNavMesh,bCanCrouch=False,bCanJump=False,bCanWalk=True,bCanSwim=False,bCanFly=True)\n+SupportedAgents=(Name=\"RLA\",Color=(B=0,G=255,R=140,A=164),DefaultQueryExtent=(X=500.000000,Y=500.000000,Z=500.000000),NavDataClass=/Script/NavigationSystem.RecastNavMesh,AgentRadius=130.000000,AgentHeight=100.000000,AgentStepHeight=-1.000000,NavWalkingSearchHeightScale=0.500000,PreferredNavData=None,bCanCrouch=False,bCanJump=False,bCanWalk=True,bCanSwim=False,bCanFly=True)\nSupportedAgentsMask=(bSupportsAgent0=True,bSupportsAgent1=True,bSupportsAgent2=True,bSupportsAgent3=True,bSupportsAgent4=True,bSupportsAgent5=True,bSupportsAgent6=True,bSupportsAgent7=True,bSupportsAgent8=True,bSupportsAgent9=True,bSupportsAgent10=True,bSupportsAgent11=True,bSupportsAgent12=True,bSupportsAgent13=True,bSupportsAgent14=True,bSupportsAgent15=True)\nDirtyAreasUpdateFreq=60.000000\n\n[/Script/Engine.PhysicsSettings]\nMaxPhysicsDeltaTime=0.500000\nInitialAverageFrameRate=0.041667\n\n\n\n[/Script/Engine.GarbageCollectionSettings]\ngc.TimeBetweenPurgingPendingKillObjects=10.000000\ngc.ActorClusteringEnabled=True\n\n"
  },
  {
    "path": "Config/DefaultGame.ini",
    "content": "\n\n[/Script/EngineSettings.GeneralProjectSettings]\nProjectID=2433E6A446E19AAC82A379AF7E021098\n\n[StartupActions]\nbAddPacks=True\nInsertPack=(PackSource=\"StarterContent.upack\",PackName=\"StarterContent\")\n\n[/Script/UnrealEd.ProjectPackagingSettings]\nBuild=IfProjectHasCode\nBuildConfiguration=PPBC_Development\nBuildTarget=UHMP\nStagingDirectory=(Path=\"F:/2/git_test/uhmap/Build\")\nFullRebuild=False\nForDistribution=False\nIncludeDebugFiles=False\nBlueprintNativizationMethod=Disabled\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/BlueprintLib/Agent\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/BlueprintLib/BpHmpPythonBridgeV2\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/Core/BpHmpPythonBridgeV2\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/Core/Main\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/Core/CoreControl\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/CoreSystem/CoreControl\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/CoreAgent/Agent\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/CoreActors/Agent\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/AbstractActors/Agent\")\n+NativizeBlueprintAssets=(FilePath=\"/Game/Assets/AbstractActor/Agent\")\nbIncludeNativizedAssetsInProjectGeneration=False\nbExcludeMonolithicEngineHeadersInNativizedCode=False\nUsePakFile=True\nbUseIoStore=False\nbMakeBinaryConfig=False\nbGenerateChunks=False\nbGenerateNoChunks=False\nbChunkHardReferencesOnly=False\nbForceOneChunkPerFile=False\nMaxChunkSize=0\nbBuildHttpChunkInstallData=False\nHttpChunkInstallDataDirectory=(Path=\"\")\nbCompressed=True\nPakFileCompressionFormats=Oodle\nbForceUseProjectCompressionFormatIgnoreHardwareOverride=False\nPakFileAdditionalCompressionOptions=-compressionblocksize=256KB\nPakFileCompressionMethod=Kraken\nPakFileCompressionLevel_DebugDevelopment=3\nPakFileCompressionLevel_TestShipping=5\nPakFileCompressionLevel_Distribution=7\nHttpChunkInstallDataVersion=\nIncludePrerequisites=True\nIncludeAppLocalPrerequisites=False\nbShareMaterialShaderCode=True\nbDeterministicShaderCodeOrder=False\nbSharedMaterialNativeLibraries=True\nApplocalPrerequisitesDirectory=(Path=\"\")\nIncludeCrashReporter=False\nInternationalizationPreset=English\n-CulturesToStage=en\n+CulturesToStage=en\nLocalizationTargetCatchAllChunkId=0\nbCookAll=False\nbCookMapsOnly=False\nbSkipEditorContent=False\nbSkipMovies=False\n-IniKeyBlacklist=KeyStorePassword\n-IniKeyBlacklist=KeyPassword\n-IniKeyBlacklist=rsa.privateexp\n-IniKeyBlacklist=rsa.modulus\n-IniKeyBlacklist=rsa.publicexp\n-IniKeyBlacklist=aes.key\n-IniKeyBlacklist=SigningPublicExponent\n-IniKeyBlacklist=SigningModulus\n-IniKeyBlacklist=SigningPrivateExponent\n-IniKeyBlacklist=EncryptionKey\n-IniKeyBlacklist=IniKeyBlacklist\n-IniKeyBlacklist=IniSectionBlacklist\n+IniKeyBlacklist=KeyStorePassword\n+IniKeyBlacklist=KeyPassword\n+IniKeyBlacklist=rsa.privateexp\n+IniKeyBlacklist=rsa.modulus\n+IniKeyBlacklist=rsa.publicexp\n+IniKeyBlacklist=aes.key\n+IniKeyBlacklist=SigningPublicExponent\n+IniKeyBlacklist=SigningModulus\n+IniKeyBlacklist=SigningPrivateExponent\n+IniKeyBlacklist=EncryptionKey\n+IniKeyBlacklist=IniKeyBlacklist\n+IniKeyBlacklist=IniSectionBlacklist\n+MapsToCook=(FilePath=\"/Game/Maps/AutoEntry\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapBreakingBad\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapLargeScale\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapIntercept\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapPreyPredator\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapWaterdrop\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapAttackPost\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapCarrier\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapReproduce\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapFormation\")\n+MapsToCook=(FilePath=\"/Game/Maps/UhmapFlagCapture\")\n\n"
  },
  {
    "path": "Config/DefaultInput.ini",
    "content": "[/Script/Engine.InputSettings]\n-AxisConfig=(AxisKeyName=\"Gamepad_LeftX\",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))\n-AxisConfig=(AxisKeyName=\"Gamepad_LeftY\",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))\n-AxisConfig=(AxisKeyName=\"Gamepad_RightX\",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))\n-AxisConfig=(AxisKeyName=\"Gamepad_RightY\",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))\n-AxisConfig=(AxisKeyName=\"MouseX\",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))\n-AxisConfig=(AxisKeyName=\"MouseY\",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))\n-AxisConfig=(AxisKeyName=\"Mouse2D\",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))\n+AxisConfig=(AxisKeyName=\"Gamepad_LeftX\",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_LeftY\",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_RightX\",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_RightY\",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MouseX\",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MouseY\",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Mouse2D\",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MouseWheelAxis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_LeftTriggerAxis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_RightTriggerAxis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_Special_Left_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Gamepad_Special_Left_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Daydream_Left_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Daydream_Left_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Daydream_Right_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Daydream_Right_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Left_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Left_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Left_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Right_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Right_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"Vive_Right_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Left_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Left_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Left_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Left_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Left_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Right_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Right_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Right_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Right_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"MixedReality_Right_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Left_Grip_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Left_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Left_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Left_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Right_Grip_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Right_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Right_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"OculusTouch_Right_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Grip_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Grip_Force\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Trackpad_Force\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Left_Trackpad_Touch\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Grip_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Grip_Force\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Trigger_Axis\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Thumbstick_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Thumbstick_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Trackpad_X\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Trackpad_Y\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\n+AxisConfig=(AxisKeyName=\"ValveIndex_Right_Trackpad_Force\",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))\nbAltEnterTogglesFullscreen=True\nbF11TogglesFullscreen=True\nbUseMouseForTouch=False\nbEnableMouseSmoothing=True\nbEnableFOVScaling=True\nbCaptureMouseOnLaunch=True\nbAlwaysShowTouchInterface=False\nbShowConsoleOnFourFingerTap=True\nbEnableGestureRecognizer=False\nbUseAutocorrect=False\nDefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown\nDefaultViewportMouseLockMode=LockOnCapture\nFOVScale=0.011110\nDoubleClickTime=0.200000\n+ActionMappings=(ActionName=\"ResumControl\",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Tab)\n+ActionMappings=(ActionName=\"OnMouseSelect\",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton)\n+ActionMappings=(ActionName=\"OnRightMouseSelect\",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=RightMouseButton)\n+ActionMappings=(ActionName=\"OnMidMousePressed\",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MiddleMouseButton)\n+ActionMappings=(ActionName=\"On=Pressed\",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Equals)\nDefaultPlayerInputClass=/Script/Engine.PlayerInput\nDefaultInputComponentClass=/Script/Engine.InputComponent\nDefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks\n-ConsoleKeys=Tilde\n+ConsoleKeys=Tilde\n\n"
  },
  {
    "path": "Docs/git_coop.md",
    "content": "# 如何将修改同步到远程\n### 0. 首先关闭虚幻引擎！\n\n### 1. 打开vscode，切换到git页面\n<div align=\"center\">\n<img src=\"Docs/2022-10-17-17-14-13.png\" width=\"300\" >\n</div>\n\n### 2. 填写message，然后点击commit\n### 3. 再点击sync changes，然后出现下面界面（也有可能直接顺利完成，忽略以下步骤即可）\n<div align=\"center\">\n<img src=\"Docs/2022-10-17-17-30-42.png\" width=\"300\" >\n</div>\n<div align=\"center\">\n<img src=\"Docs/2022-10-17-17-16-09.png\" width=\"300\" >\n</div>\n\n### 4. 如果没有“Merge Changes”，再次点击提交即可，但一般都会有若干项“Merge Changes”，即和其他人发生冲突的文件，需要进行处理\n\n### 5. 如有“Merge Changes”，打开终端\n\n#### 5-1 （确实对该文件做出过有意义的修改）如果想保存本地，无视远程文件，强行把本地文件汇入远程：\n终端命令：\n```\n   git checkout --ours /path/to/file\n   git add /path/to/file\n```\n在此例子中，\n```\n   git checkout --ours Content\\Assets\\DefAction\\ParseAction.uasset\n   git add Content\\Assets\\DefAction\\ParseAction.uasset\n```\n#### 5-2 （无意修改此文件，或该文件属于其他人的管辖范围）如果想覆盖本地文件，采纳远程文件：\n终端命令：\n```\n   git checkout --theirs /path/to/file\n   git add /path/to/file\n```\n在此例子中，\n```\n   git checkout --theirs Content\\Assets\\DefAction\\ParseAction.uasset\n   git add Content\\Assets\\DefAction\\ParseAction.uasset\n```\n\n\n### 6. 再次点击Commit，完成流程\n"
  },
  {
    "path": "Docs/old_install_method/README.md",
    "content": "# UHMAP\n\nDeveloped with Unreal Engine 4\n\n# How to install | 如何安装自定义版本的虚幻引擎\n另见视频：\nhttps://ageasga-my.sharepoint.com/:v:/g/personal/fuqingxu_yiteam_tech/EawfqsV2jF5Nsv3KF7X1-woBH-VTvELL6FSRX4cIgUboLg?e=Vmp67E\n\n## 1. 下载 Visual Studio Community\n\nhttps://visualstudio.microsoft.com/zh-hans/\n\n## 2. 安装 Visual Studio Community\n\n<div align=\"center\">\n<img src=\"Docs/vscode_install.jpg\" width=\"600\" >\n</div>\n<div align=\"center\">\n<img src=\"Docs/vs_install2.png\" width=\"600\" >\n</div>\n需要至少安装C++的桌面开发和C++的游戏开发两部分，安装版本见截图；另需安装.NET框架，版本为4.6.2\n\n## 3. 下载虚幻引擎源代码 (非官方，修改过源码)\n\nhttps://ageasga-my.sharepoint.com/:u:/g/personal/fuqingxu_yiteam_tech/Ee3lQrUjKNFMjPITm5G-hEgBbeEN6dMOPtKP9ssgONKJcA?e=BavOoJ\n\n## 4. 编译虚幻引擎\n1. 解压源代码（到至少150GB空间的磁盘）\n\n1. Open your source folder in Explorer and run **Setup.bat**. \n   This will download binary content for the engine, as well as installing prerequisites and setting up Unreal file associations. \n   On Windows 8, a warning from SmartScreen may appear.  Click \"More info\", then \"Run anyway\" to continue. 运行**Setup.bat**\n   \n   A clean download of the engine binaries is currently 3-4gb, which may take some time to complete.\n   Subsequent checkouts only require incremental downloads and will be much quicker. 需要一段时间\n \n1. Run **GenerateProjectFiles.bat** to create project files for the engine. It should take less than a minute to complete.  运行 **GenerateProjectFiles.bat**\n\n1. Load the project into Visual Studio by double-clicking on the **UE4.sln** file. Set your solution configuration to **Development Editor** and your solution\n   platform to **Win64**, then right click on the **UE4** target and select **Build**. It may take anywhere between 10 and 40 minutes to finish compiling, depending on your system specs. 打开**UE4.sln**，界面顶部的项目配置为**Development Editor** 和 **Win64**， 右击界面右侧菜单的**UE4**，点击**Build**，需要20分钟~1小时时间编译(配置好后，点选界面上面栏目中的生成Build，点击生成UE4)\n\n\n\n1. 右键点击项目工程文件夹中的UHMP.uproject，按照图示选择虚幻引擎版本为4.27.2-release，进行初步生成项目的操作；之后打开UHMP.sln，将界面顶部的项目配置为**Development Editor** 和 **Win64**，点选右侧资源管理器中的UHMP，再点击界面顶部的生成-->生成UHMP；完成后双击UHMP.uproject打开工程项目\n\n<div align=\"center\">\n<img src=\"Docs/UHMP_1.png\" width=\"600\" >\n</div>\n\n6. 成功打开的虚幻编辑器加载界面应当如下图所示\n\n<div align=\"center\">\n<img src=\"Docs/UHMP_window.jpg\" width=\"600\" >\n</div>"
  },
  {
    "path": "Please_Run_This_First_To_Fetch_Big_Files.py",
    "content": "import os, commentjson, shutil, subprocess, tqdm, shutil\nimport zipfile\nfrom modelscope import snapshot_download\ntry: os.makedirs('./TEMP')\nexcept: pass\n\nversion = 'unreal-map-v3.4'\nmodel_dir = snapshot_download(f'BinaryHusky/{version}')\nzip_file_path = f'./TEMP/{version}.zip'\n\ndef combine_file(model_dir, output_file_path, num_parts):\n    with open(output_file_path, 'wb') as output_file:\n        for i in range(0, num_parts):\n            part_file_path = os.path.join(model_dir, \"tensor\", f\"safetensor_{i+1}.pt\")\n            with open(part_file_path, 'rb') as part_file:\n                output_file.write(part_file.read())\n\nextract_to_path = './'\ncombine_file(model_dir, output_file_path=zip_file_path, num_parts=5)\n\n# 打开 ZIP 文件\nwith zipfile.ZipFile(zip_file_path, 'r') as zip_ref:\n    # 解压所有文件到指定目录\n    zip_ref.extractall(extract_to_path)\n    print(f\"files unzipped {extract_to_path}\")\n\nprint(\"everything is ready!\")\n"
  },
  {
    "path": "PythonExample/README.md",
    "content": "This demo program can connect to developing/compiled U-MAP environment to debug your simulation.\n\nThis program is a copy of another resp: https://github.com/binary-husky/hmp2g\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/.gitattributes",
    "content": "*.js linguist-detectable=false\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/.gitignore",
    "content": "# Build and Release Folders\nbin-debug/\nbin-release/\n\n*/__pycache__\n[Oo]bj/\n[Bb]in/\n\n# Other files and folders\n.settings/\n\n# Executables\n*.swf\n*.air\n*.ipa\n*.apk\n*.so\n*.pyc\n*.pyd\n*.so\n# gpu lock\n*.glock\n*.mp3\n*.png\n\n# pytorch model\n*.pt\ncore\n!MISSION/collective_assult/malib/core\nTODO\n__pycache__/\n./build/\nALGORITHM/Starcraft/result/\nALGORITHM/Starcraft/model/\nZipResults/\nVISUALIZE/train no aWiseAttn\nVISUALIZE/train-half-death-reward\nZipResults/Starcraft/\nresult\n# .vscode/\nforattack-train/\nfull-cargo/\nT*/\ncheckpoint/\n\nPROFILE/\nRECYCLE/\nTEMP/\ntest_only_*.py\ntest_only_log\ntest_only_profile.txt\ntest_only_profilex.txt\ndebug_change_self_n_agent.json.profile.txt\ndebug2-2500pt-test_only_profile.txt\ntest_only_logdebug_change_self_n_agent.json.log\ntest_only_profiledebug_change_self_n_agent.json.txt\nxx_profile_n_agents2.py\nxx_profile_n_agents3.py\nxx_profile_n_agents4.py\nUTIL/keys.py\nprivate*\nfqx*.jsonc\nmy_*.jsonc\nresult.prof\nignore\nbvrAI.log\nZHECKPOINT/*\n\nTHIRDPARTY/pymarl2/test\n\n!ZHECKPOINT/test-50+50\nZHECKPOINT/test-50+50/*\n!ZHECKPOINT/test-50+50/model.pt\n!ZHECKPOINT/test-50+50/experiment.json\n!ZHECKPOINT/test-50+50/test-50+50.jsonc\n!ZHECKPOINT/test-50+50/test50.gif\n\n!ZHECKPOINT/test-100+100\nZHECKPOINT/test-100+100/*\n!ZHECKPOINT/test-100+100/model.pt\n!ZHECKPOINT/test-100+100/experiment.json\n!ZHECKPOINT/test-100+100/test-100+100.jsonc\n\n!ZHECKPOINT/50RL-55opp\nZHECKPOINT/50RL-55opp/*\n!ZHECKPOINT/50RL-55opp/test-50RL-55opp.jsonc\n!ZHECKPOINT/50RL-55opp/model.pt\nZHECKPOINT/50RL-55opp/experiment.json\n\n!ZHECKPOINT/test-cargo50\nZHECKPOINT/test-cargo50/*\n!ZHECKPOINT/test-cargo50/model.pt\n!ZHECKPOINT/test-cargo50/experiment.json\n!ZHECKPOINT/test-cargo50/test-cargo50.jsonc\n!ZHECKPOINT/test-cargo50/cargo50.jpg\n\n!ZHECKPOINT/test-cargo50/history_cpt\nZHECKPOINT/test-cargo50/history_cpt/*\n!ZHECKPOINT/test-cargo50/history_cpt/init.pkl\n\n!ZHECKPOINT/test-50+50/butterfly.webp\n\n!ZHECKPOINT/test-aii515\nZHECKPOINT/test-aii515/*\n!ZHECKPOINT/test-aii515/model.pt\n!ZHECKPOINT/test-aii515/experiment.json\n!ZHECKPOINT/test-aii515/test-aii515.jsonc\n!ZHECKPOINT/test-aii515/aii.jpg\n\n!ZHECKPOINT/test-aii515/history_cpt\nZHECKPOINT/test-aii515/history_cpt/*\n!ZHECKPOINT/test-aii515/history_cpt/init.pkl\n\n!ZHECKPOINT/basic-ma-40-demo\nZHECKPOINT/basic-ma-40-demo/*\n!ZHECKPOINT/basic-ma-40-demo/trained_model.pt\n!ZHECKPOINT/basic-ma-40-demo/train.json\n!ZHECKPOINT/basic-ma-40-demo/test.json\n\n!ZHECKPOINT/adca-demo\nZHECKPOINT/adca-demo/*\n!ZHECKPOINT/adca-demo/model_trained.pt\n!ZHECKPOINT/adca-demo/train.json\n!ZHECKPOINT/adca-demo/test.json\n\n!ZHECKPOINT/uhmap_hete10vs10\nZHECKPOINT/uhmap_hete10vs10/backup_files\nZHECKPOINT/uhmap_hete10vs10/logger\n!ZHECKPOINT/uhmap_hete10vs10/model_trained.pt\nZHECKPOINT/uhmap_hete10vs10/experiment.jsonc\n\n\ncmd_io.txt\nrec.jpg\ndetail_reward.jpg\nz_*\nALGORITHM/conc_4hist_divtree3\nALGORITHM/conc_4hist_divtree2\nexample_dca_cs*\nPersonalityDevelop.pdf\n\n6vs7Pr-continue-train.jsonc\n6vs7Pr.jsonc\n6vs7PrTry2-Link.jsonc\n6vs7PrTry2.jsonc\n7vs7Pr.json\n7vs7Pr.jsonc\nbatch_experiment_backup.py\nmcom_buffer_0____starting_session.txt\ntemp.jpg\ntemp.jpg.jpg\nx.txt\ndebug*.jsonc\nHLT_eval.py\nqplex-pad.jsonc\nraw_exp.jsonc\ninfo.json\nUTIL/mem_watcher.py\n\n\nALGORITHM/mirror*"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/.gitmodules",
    "content": "[submodule \"THIRDPARTY/pymarl2/pymarl2src\"]\n\tpath = THIRDPARTY/pymarl2/pymarl2src\n\turl = https://github.com/binary-husky/pymarl-hmap-compat.git\n\tbranch = master\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/alg_base.py",
    "content": "import os, time, torch, traceback\nimport numpy as np\nfrom config import GlobalConfig\nfrom UTIL.colorful import *\n\nclass AlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_thread = n_thread\n        self.n_agent = n_agent\n        self.team = team\n        self.act_space = space['act_space']\n        self.obs_space = space['obs_space']\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.mcv = mcv\n        self.device = GlobalConfig.device\n\n    def interact_with_env(self, team_intel):\n        raise NotImplementedError\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/attention.py",
    "content": "import math\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.distributions.categorical import Categorical\nfrom torch.distributions.multivariate_normal import MultivariateNormal\nfrom UTIL.tensor_ops import my_view\n\nclass MultiHeadAttention(nn.Module):\n    # taken from https://github.com/wouterkool/attention-tsp/blob/master/graph_encoder.py\n    def __init__(\n            self,\n            n_heads,\n            input_dim,\n            embed_dim=None,\n            val_dim=None,\n            key_dim=None\n    ):\n        super(MultiHeadAttention, self).__init__()\n\n        if val_dim is None:\n            assert embed_dim is not None, \"Provide either embed_dim or val_dim\"\n            val_dim = embed_dim // n_heads\n        if key_dim is None:\n            key_dim = val_dim\n\n        self.n_heads = n_heads\n        self.input_dim = input_dim\n        self.embed_dim = embed_dim\n        self.val_dim = val_dim\n        self.key_dim = key_dim\n\n        self.norm_factor = 1 / math.sqrt(key_dim)  # See Attention is all you need\n\n        self.W_query = nn.Parameter(torch.Tensor(n_heads, input_dim, key_dim))\n        self.W_key = nn.Parameter(torch.Tensor(n_heads, input_dim, key_dim))\n        self.W_val = nn.Parameter(torch.Tensor(n_heads, input_dim, val_dim))\n\n        if embed_dim is not None:\n            self.W_out = nn.Parameter(torch.Tensor(n_heads, key_dim, embed_dim))\n\n        self.init_parameters()\n\n    def init_parameters(self):\n        for param in self.parameters():\n            stdv = 1. / math.sqrt(param.size(-1))\n            param.data.uniform_(-stdv, stdv)\n\n    def forward(self, q, k=None, v=None, mask=None, return_attn=False, return_attn_weight=False):\n        if q.dim()<=3: \n            out = self.forward_(q, k, v, mask, return_attn, return_attn_weight)\n            if return_attn:\n                out, attn = out\n                assert attn.shape[0]==1\n                attn = attn.squeeze(0)\n                return out, attn\n            return out\n\n\n        hyper_dim = q.shape[:-2]\n        q = my_view(q, [-1, *q.shape[-2:]])\n        if k is not None: \n            k = my_view(k, [-1, *k.shape[-2:]])\n        if v is not None: \n            v = my_view(v, [-1, *v.shape[-2:]])\n        if mask is not None: mask = my_view(mask, [-1, *mask.shape[-2:]])\n        out = self.forward_(q, k, v, mask, return_attn, return_attn_weight)\n        if return_attn:\n            out, attn = out\n            if hyper_dim is not None:\n                out = out.view(*hyper_dim, *out.shape[-2:])\n                attn = attn.view(*hyper_dim, *attn.shape[-2:]) #??\n            return out, attn\n        else:\n            if hyper_dim is not None:\n                out = out.view(*hyper_dim, *q.shape[-2:])\n            return out\n\n    def forward_(self, q, k=None, v=None, mask=None, return_attn=False, return_attn_weight=False):\n        \"\"\"\n        :param q: queries (batch_size, n_query, input_dim)\n        :param k: data (batch_size, n_key/graph_size, input_dim)\n        :param mask: mask (batch_size, n_query, graph_size) or viewable as that (i.e. can be 2 dim if n_query == 1)\n        Mask should contain 1 if attention is not possible (i.e. mask is negative adjacency)\n        :return:\n        \"\"\"\n        if k is None:\n            k = q  # compute self-attention\n        if v is None:\n            v = k\n        # k should be (batch_size, graph_size, input_dim)\n        batch_size, graph_size, input_dim = k.size()\n        n_query = q.size(1)\n        assert q.size(0) == batch_size\n        assert q.size(2) == input_dim\n        assert input_dim == self.input_dim, \"Wrong embedding dimension of input\"\n\n        kflat = k.contiguous().view(-1, input_dim)\n        qflat = q.contiguous().view(-1, input_dim)\n        vflat = v.contiguous().view(-1, input_dim)\n\n        # last dimension can be different for keys and values\n        shp = (self.n_heads, batch_size, graph_size, -1)\n        shp_q = (self.n_heads, batch_size, n_query, -1)\n\n        # Calculate queries, (n_heads, n_query, graph_size, key/val_size)\n        Q = torch.matmul(qflat, self.W_query).view(shp_q)\n        # Calculate keys and values (n_heads, batch_size, graph_size, key/val_size)\n        K = torch.matmul(kflat, self.W_key).view(shp)\n        V = torch.matmul(vflat, self.W_val).view(shp)\n\n        # Calculate compatibility (n_heads, batch_size, n_query, graph_size)\n        compatibility = self.norm_factor * torch.matmul(Q, K.transpose(2, 3))\n        if return_attn_weight:\n            assert self.n_heads == 1\n            if mask is not None:\n                mask = mask.view(1, batch_size, n_query, graph_size).expand_as(compatibility)\n                compatibility[mask.bool()] = -math.inf\n            return compatibility.squeeze(0)\n\n        # Optionally apply mask to prevent attention\n        if mask is not None:    # expand to n_heads\n            mask = mask.view(1, batch_size, n_query, graph_size).expand_as(compatibility)\n            compatibility[mask.bool()] = -math.inf\n\n        attn = F.softmax(compatibility, dim=-1)\n\n        # If there are nodes with no neighbours then softmax returns nan so we fix them to 0\n        if mask is not None:\n            attnc = attn.clone()\n            attnc[mask.bool()] = 0\n            attn = attnc\n\n        # 为了在这里解决 0*nan = nan 的问题，输入必须将V中的nan转化为0\n        heads = torch.matmul(attn, V)\n\n        out = torch.mm(\n            heads.permute(1, 2, 0, 3).contiguous().view(-1, self.n_heads * self.val_dim),\n            self.W_out.view(-1, self.embed_dim)\n        ).view(batch_size, n_query, self.embed_dim)\n\n        if return_attn:\n            return out, attn\n        return out\n\n\n\n\nclass SimpleAttention(nn.Module):\n    def __init__(self, h_dim):\n        super().__init__()\n        self.W_query = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.W_key = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.W_val = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.init_parameters()\n\n    def init_parameters(self):\n        for param in self.parameters():\n            stdv = 1. / math.sqrt(param.size(-1))\n            param.data.uniform_(-stdv, stdv)\n\n    def forward(self, k, q, v, mask=None):\n        Q = torch.matmul(q, self.W_query) \n        K = torch.matmul(k, self.W_key) \n        V = torch.matmul(v, self.W_val)\n\n        norm_factor = 1 / math.sqrt(Q.shape[-1])\n        compat = norm_factor * torch.matmul(Q, K.transpose(-1, -2)) \n        if mask is not None: compat[mask.bool()] = -math.inf\n        # 为了在这里解决 0*nan = nan 的问题，输入必须将V中的nan转化为0\n        score = torch.nan_to_num(F.softmax(compat, dim=-1), 0)\n        return torch.matmul(score, V) \n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/conc.py",
    "content": "import math\nimport torch,time,random\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom UTIL.tensor_ops import my_view, __hash__, __hashn__, pad_at_dim, gather_righthand\n\nclass Concentration(nn.Module):\n    def __init__(self, n_focus_on, h_dim, skip_connect=False, skip_connect_dim=0, adopt_selfattn=False):\n        super().__init__()\n        self.n_focus_on = n_focus_on\n        self.skip_connect = skip_connect\n        self.skip_dim = h_dim+skip_connect_dim\n        self.CT_W_query = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.CT_W_key = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.CT_W_val = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.CT_motivate_mlp = nn.Sequential(nn.Linear(h_dim * 2, h_dim), nn.ReLU(inplace=True))\n        self.AT_forward_mlp = nn.Sequential(nn.Linear((n_focus_on+1)*self.skip_dim, h_dim), nn.ReLU(inplace=True))\n        self.adopt_selfattn = adopt_selfattn\n        if self.adopt_selfattn:\n            assert False, ('no longer support')\n        self.init_parameters()\n\n    def init_parameters(self):\n        for param in self.parameters():\n            stdv = 1. / math.sqrt(param.size(-1))\n            param.data.uniform_(-stdv, stdv)\n\n    def forward(self, vs, ve, ve_dead, skip_connect_ze=None, skip_connect_zs=None):\n        mask = ve_dead\n        Q = torch.matmul(vs, self.CT_W_query) \n        K = torch.matmul(ve, self.CT_W_key) \n\n        norm_factor = 1 / math.sqrt(Q.shape[-1])\n        compat = norm_factor * torch.matmul(Q, K.transpose(2, 3)) \n        assert compat.shape[-2] == 1\n        compat = compat.squeeze(-2)\n        compat[mask.bool()] = -math.inf\n        score = F.softmax(compat, dim=-1)\n        # nodes with no neighbours were softmax into nan, fix them to 0\n        score = torch.nan_to_num(score, 0)\n        # ----------- motivational brach -------------\n        Va = torch.matmul(score.unsqueeze(-2), torch.matmul(ve, self.CT_W_val)) \n        v_M = torch.cat((vs, Va), -1).squeeze(-2) \n        v_M_final = self.CT_motivate_mlp(v_M)\n        # -----------   forward branch   -------------\n        score_sort_index = torch.argsort(score, dim=-1, descending=True)\n        score_sort_drop_index = score_sort_index[..., :self.n_focus_on]\n        if self.skip_connect:\n            ve = torch.cat((ve, skip_connect_ze), -1)\n            vs = torch.cat((vs, skip_connect_zs), -1)\n        ve_C = gather_righthand(src=ve,  index=score_sort_drop_index, check=False)\n        need_padding = (score_sort_drop_index.shape[-1] != self.n_focus_on)\n        if need_padding:\n            print('the n_focus param is large than input, advise: pad observation instead of pad here')\n            ve_C = pad_at_dim(ve_C, dim=-2, n=self.n_focus_on)\n        v_C_stack = torch.cat((vs, ve_C), dim=-2)\n        if self.adopt_selfattn:\n            v_C_stack = self.AT_Attention(v_C_stack, mask=None)\n\n        v_C_flat = my_view(v_C_stack, [0, 0, -1]); assert v_C_stack.dim()==4\n        v_C_final = self.AT_forward_mlp(v_C_flat)\n        return v_C_final, v_M_final\n\n\n\nclass ConcentrationHete(nn.Module):\n    def __init__(self, n_focus_on, h_dim, skip_connect=False, skip_connect_dim=0, adopt_selfattn=False):\n        super().__init__()\n        self.n_focus_on = n_focus_on\n        self.skip_connect = skip_connect\n        self.skip_dim = h_dim+skip_connect_dim\n        self.AT_W_query = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.AT_W_key = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.AT_W_val = nn.Parameter(torch.Tensor(h_dim, h_dim))\n        self.AT_motivate_mlp = nn.Sequential(nn.Linear(h_dim * 2, h_dim), nn.ReLU(inplace=True))\n        self.AT_forward_mlp = nn.Sequential(nn.Linear((n_focus_on+1)*self.skip_dim, h_dim), nn.ReLU(inplace=True))\n        self.adopt_selfattn = adopt_selfattn\n        if self.adopt_selfattn:\n            assert False, ('no longer support')\n        self.init_parameters()\n\n    def init_parameters(self):\n        for param in self.parameters():\n            stdv = 1. / math.sqrt(param.size(-1))\n            param.data.uniform_(-stdv, stdv)\n\n    def forward(self, vs, ve, ve_dead, skip_connect_ze=None, skip_connect_zs=None):\n        mask = ve_dead\n        Q = torch.matmul(vs, self.AT_W_query) \n        K = torch.matmul(ve, self.AT_W_key) \n\n        norm_factor = 1 / math.sqrt(Q.shape[-1])\n        compat = norm_factor * torch.matmul(Q, K.transpose(2, 3)) \n        assert compat.shape[-2] == 1\n        compat = compat.squeeze(-2)\n        compat[mask.bool()] = -math.inf\n        score = F.softmax(compat, dim=-1)\n        # nodes with no neighbours were softmax into nan, fix them to 0\n        score = torch.nan_to_num(score, 0)\n        # ----------- motivational brach -------------\n        Va = torch.matmul(score.unsqueeze(-2), torch.matmul(ve, self.AT_W_val)) \n        v_M = torch.cat((vs, Va), -1).squeeze(-2) \n        v_M_final = self.AT_motivate_mlp(v_M)\n        # -----------   forward branch   -------------\n        score_sort_index = torch.argsort(score, dim=-1, descending=True)\n        score_sort_drop_index = score_sort_index[..., :self.n_focus_on]\n        if self.skip_connect:\n            ve = torch.cat((ve, skip_connect_ze), -1)\n            vs = torch.cat((vs, skip_connect_zs), -1)\n        ve_C = gather_righthand(src=ve,  index=score_sort_drop_index, check=False)\n        need_padding = (score_sort_drop_index.shape[-1] != self.n_focus_on)\n        if need_padding:\n            print('the n_focus param is large than input, advise: pad observation instead of pad here')\n            ve_C = pad_at_dim(ve_C, dim=-2, n=self.n_focus_on)\n        v_C_stack = torch.cat((vs, ve_C), dim=-2)\n        if self.adopt_selfattn:\n            v_C_stack = self.AT_Attention(v_C_stack, mask=None)\n\n        v_C_flat = my_view(v_C_stack, [0, 0, -1]); assert v_C_stack.dim()==4\n        v_C_final = self.AT_forward_mlp(v_C_flat)\n        return v_C_final, v_M_final\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/dl_pool.py",
    "content": "\"\"\"\n    Author: Fu Qingxu,CASIA\n    Description: deep learning sample manager\n\n\"\"\"\nimport torch\nimport numpy as np\n\nclass DeepLearningPool(object):\n    def __init__(self, pool_size, batch_size) -> None:\n        super().__init__()\n        self.x_batch = None\n        self.y_batch = None\n        self.size = pool_size\n        self.batch_size = batch_size\n\n\n\n    def add_and_sample(self, x, y):\n        n_sample = x.shape[0]\n        assert n_sample > 0\n        if self.x_batch is None:\n            self.x_batch = np.zeros(shape=(self.size, *x.shape[1:]), dtype=x.dtype)\n            self.y_batch = np.zeros(shape=(self.size, *y.shape[1:]), dtype=y.dtype)\n            self.current_idx = 0\n            self.current_size = 0\n        idx = self._get_storage_idx(n_sample)\n        self.x_batch[idx] = x\n        self.y_batch[idx] = y\n        return self._sample()\n\n\n    def _get_storage_idx(self, inc=None):\n        inc = inc or 1\n        if self.current_idx + inc <= self.size:\n            idx = np.arange(self.current_idx, self.current_idx + inc)\n            self.current_idx += inc\n        elif self.current_idx < self.size:\n            overflow = inc - (self.size - self.current_idx)\n            idx_a = np.arange(self.current_idx, self.size)\n            idx_b = np.arange(0, overflow)\n            idx = np.concatenate([idx_a, idx_b])\n            self.current_idx = overflow\n        else:\n            idx = np.arange(0, inc)\n            self.current_idx = inc\n        self.current_size = min(self.size, self.current_size + inc)\n        if inc == 1:\n            idx = idx[0]\n        return idx\n\n    def _sample(self):\n        idx = np.random.randint(0, self.current_size, self.batch_size)\n        return self.x_batch[idx], self.y_batch[idx]\n\n\nif __name__ == '__main__':\n    dlp = DeepLearningPool(10, 7)\n    res = dlp.add_and_sample(x=np.random.rand(2,2,3),y=np.array([1,2]))\n    print(dlp.y_batch,'res',res[1])\n    res = dlp.add_and_sample(x=np.random.rand(4,2,3),y=np.array([3,4,5,6]))\n    print(dlp.y_batch,'res',res[1])\n    res = dlp.add_and_sample(x=np.random.rand(3,2,3),y=np.array([7,8,9]))\n    print(dlp.y_batch,'res',res[1])\n    res = dlp.add_and_sample(x=np.random.rand(3,2,3),y=np.array([10,11,12]))\n    print(dlp.y_batch,'res',res[1])\n    res = dlp.add_and_sample(x=np.random.rand(3,2,3),y=np.array([13,14,15]))\n    print(dlp.y_batch,'res',res[1])\n    res = dlp.add_and_sample(x=np.random.rand(3,2,3),y=np.array([16,17,18]))\n    print(dlp.y_batch,'res',res[1])\n    print('end of test')\n\n\n        \n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/his.py",
    "content": "import matplotlib.pyplot as plt\nimport numpy as np\nimport matplotlib\n\n# 设置matplotlib正常显示中文和负号\nmatplotlib.rcParams['font.sans-serif']=['SimHei']   # 用黑体显示中文\nmatplotlib.rcParams['axes.unicode_minus']=False     # 正常显示负号\n# 随机生成（10000,）服从正态分布的数据\ndata = np.random.randn(10000)\n\"\"\"\n绘制直方图\ndata:必选参数，绘图数据\nbins:直方图的长条形数目，可选项，默认为10\nnormed:是否将得到的直方图向量归一化，可选项，默认为0，代表不归一化，显示频数。normed=1，表示归一化，显示频率。\nfacecolor:长条形的颜色\nedgecolor:长条形边框的颜色\nalpha:透明度\n\"\"\"\nplt.hist(data, bins=40, facecolor=\"blue\", edgecolor=\"black\", alpha=0.7)\n# 显示横轴标签\nplt.xlabel(\"区间\")\n# 显示纵轴标签\nplt.ylabel(\"频数/频率\")\n# 显示图标题\nplt.title(\"频数/频率分布直方图\")\nplt.show()\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/hyper_net.py",
    "content": "from re import X\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom UTIL.tensor_ops import my_view\n\nclass HyperNet(nn.Module):\n    def __init__(self, **kwargs):\n        super(HyperNet, self).__init__()\n\n        self.x_input_dim = kwargs['x_input_dim']\n        self.embed_dim = kwargs['embed_dim']\n        self.hyper_input_dim = kwargs['hyper_input_dim']\n        \n        # hyper w1 b1\n        self.hyper_w1 = nn.Sequential( nn.Linear(self.hyper_input_dim, self.embed_dim),\n                                        nn.ReLU(inplace=True),\n                                        nn.Linear(self.embed_dim, self.x_input_dim * self.embed_dim))\n        \n        self.hyper_b1 = nn.Sequential(nn.Linear(self.hyper_input_dim, self.embed_dim))\n        \n        # hyper w2 b2\n        self.hyper_w2 = nn.Sequential(\n                            nn.Linear(self.hyper_input_dim, self.embed_dim),\n                            nn.ReLU(inplace=True),\n                            nn.Linear(self.embed_dim, self.embed_dim * self.embed_dim))\n        self.hyper_b2 = nn.Sequential(nn.Linear(self.hyper_input_dim, self.embed_dim),\n                            nn.ReLU(inplace=True),\n                            nn.Linear(self.embed_dim, 1))\n\n    def forward(self, x, hyper_x):\n        # x shape (thread/batch, agent, core)\n        # hyper_x shape (thread/batch, core)\n        assert hyper_x.dim() == 3\n        # reshape w1 into # (..., x_input_dim, embed_dim)\n        w1 = my_view(self.hyper_w1(hyper_x), [0, 0, self.x_input_dim, self.embed_dim])\n        b1 = self.hyper_b1(hyper_x).unsqueeze(-2) # b1 (thread/batch, core=embed_dim)\n        \n        # Second layer\n        w2 = my_view(self.hyper_w2(hyper_x), [0, 0, self.embed_dim, self.embed_dim])\n        b2 = self.hyper_b2(hyper_x).unsqueeze(-2)\n        \n        ## x shape = (..., x_input_dim)\n        ## w1 shape = (..., x_input_dim, embed_dim)\n        \n        # x reshape = (..., 1, x_input_dim)\n        x = x.unsqueeze(-2)\n        hidden = F.elu(torch.matmul(x, w1) + b1) # b * t, 1, emb\n        # Forward (batch, 1, 32) * w2(batch, 32, 1) => y(batch, 1)\n        y = torch.matmul(hidden, w2) + b2 # b * t, 1, 1\n        \n        return y.squeeze(-2)\n\n\n\nclass MyHyperNet(nn.Module):\n    def __init__(self, x_in_dim, hyber_in_dim, layer_out_dims, hyber_hid_dim):\n        super(MyHyperNet, self).__init__()\n\n        self.x_in_dim = x_in_dim\n        self.layer_out_dims = layer_out_dims\n        self.hyber_in_dim = hyber_in_dim\n        self.hyber_hid_dim = hyber_hid_dim\n        self.n_layer = len(self.layer_out_dims)\n        self.layer_dim_dict = [(x_in_dim, layer_out_dims[0])] + [(d_in, d_out) for d_in, d_out in zip(layer_out_dims[:-1], layer_out_dims[1:])]\n\n\n        self.weight_each_layer = nn.ModuleList([\n            nn.Sequential(nn.Linear(self.hyber_in_dim, self.hyber_hid_dim), nn.ReLU(inplace=True), nn.Linear(self.hyber_hid_dim, d_in * d_out))\n            for d_in, d_out in self.layer_dim_dict\n        ])\n\n        self.bias_each_layer = nn.ModuleList([\n            nn.Sequential(nn.Linear(self.hyber_in_dim, self.hyber_hid_dim), nn.ReLU(inplace=True), nn.Linear(self.hyber_hid_dim, d_out))\n            for d_in, d_out in self.layer_dim_dict\n        ])\n\n\n    def forward(self, x, hyper_x):\n        # x shape (thread/batch, agent, core)\n        # hyper_x shape (thread/batch, core)\n        assert hyper_x.dim() == 3\n        x = x.unsqueeze(-2)\n\n        for i in range(self.n_layer):\n            d_in, d_out = self.layer_dim_dict[i]\n            w = my_view(self.weight_each_layer[i](hyper_x), [0, 0, d_in, d_out])\n            b = self.bias_each_layer[i](hyper_x).unsqueeze(-2)\n            x = torch.matmul(x, w) + b\n\n            is_last_layer = (i==(self.n_layer-1))\n            if is_last_layer:\n                # do NOT use relu at last layer\n                pass\n            else:\n                x = F.relu(x, inplace=True)\n\n        return x.squeeze(-2)"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/logit2act.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.distributions.categorical import Categorical\nfrom UTIL.tensor_ops import my_view, Args2tensor_Return2numpy, Args2tensor\nfrom UTIL.tensor_ops import pt_inf\n\n\n\n\"\"\"\n    network initialize\n\"\"\"\nclass Logit2Act(nn.Module):\n    def __init__(self, *args, **kwargs):\n        super().__init__()\n\n    def _logit2act_rsn(self, logits_agent_cluster, eval_mode, greedy, eval_actions=None, avail_act=None, eprsn=None):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = self.ccategorical.feed_logits(logits_agent_cluster)\n        \n        if not greedy:    act = self.ccategorical.sample(act_dist, eprsn) if not eval_mode else eval_actions\n        else:             act = torch.argmax(act_dist.probs, axis=2)\n        # the policy gradient loss will feedback from here\n        actLogProbs = self._get_act_log_probs(act_dist, act) \n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    def _logit2act(self, logits_agent_cluster, eval_mode, greedy, eval_actions=None, avail_act=None, **kwargs):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = Categorical(logits = logits_agent_cluster)\n        if not greedy:     act = act_dist.sample() if not eval_mode else eval_actions\n        else:              act = torch.argmax(act_dist.probs, axis=2)\n        actLogProbs = self._get_act_log_probs(act_dist, act) # the policy gradient loss will feedback from here\n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    @staticmethod\n    def _get_act_log_probs(distribution, action):\n        return distribution.log_prob(action.squeeze(-1)).unsqueeze(-1)\n        \n\n    @Args2tensor_Return2numpy\n    def act(self, *args, **kargs):\n        return self._act(*args, **kargs)\n\n    @Args2tensor\n    def evaluate_actions(self, *args, **kargs):\n        return self._act(*args, **kargs, eval_mode=True)\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/mlp.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom .norm import DynamicNorm\n\nclass SimpleMLP(nn.Module):\n    def __init__(self, in_dim, out_dim, hidden_dim=128, use_normalization=False):\n        super().__init__()\n        activation_func = nn.ReLU\n        h_dim = hidden_dim\n        if use_normalization:\n            print('test DynamicNorm')\n            self.mlp = nn.Sequential(\n                DynamicNorm(in_dim, only_for_last_dim=True, exclude_one_hot=True),\n                nn.Linear(in_dim, h_dim),\n                activation_func(inplace=True),\n                nn.Linear(h_dim, out_dim)\n            )\n        else:\n            self.mlp = nn.Sequential(\n                nn.Linear(in_dim, h_dim),\n                activation_func(inplace=True),\n                nn.Linear(h_dim, out_dim)\n            )\n    def forward(self,x):\n        return self.mlp(x)\n\nclass ResLinear(nn.Module):\n    def __init__(self, io_dim, h_dim, need_input_tf=False, input_tf_dim=None, inplace_relu=True) -> None:\n        super(ResLinear, self).__init__()\n        self.need_input_tf = need_input_tf\n        if need_input_tf:\n            self.f0 = nn.Linear(input_tf_dim, io_dim)\n        self.f1 = nn.Linear(io_dim, h_dim)\n        self.lkrelu = nn.ReLU(inplace=True) if inplace_relu else nn.ReLU(inplace=False)\n        self.f2 = nn.Linear(h_dim, io_dim)\n\n    def forward(self, xo):\n        if self.need_input_tf:\n            xo = self.f0(xo)\n        x = self.lkrelu(self.f1(xo))\n        x = self.f2(x) + xo\n        x = self.lkrelu(x)\n        return x\n        \nclass LinearFinal(nn.Module):\n\n    __constants__ = ['in_features', 'out_features']\n    in_features: int\n    out_features: int\n    weight: torch.Tensor\n\n    def __init__(self, in_features: int, out_features: int, bias: bool = True) -> None:\n        super(LinearFinal, self).__init__()\n        self.in_features = in_features\n        self.out_features = out_features\n        self.weight = nn.Parameter(torch.Tensor(out_features, in_features))\n        if bias:\n            self.bias = nn.Parameter(torch.Tensor(out_features))\n        else:\n            self.register_parameter('bias', None)\n\n    def forward(self, input: torch.Tensor) -> torch.Tensor:\n        return F.linear(input, self.weight, self.bias)\n\n    def extra_repr(self) -> str:\n        return 'in_features={}, out_features={}, bias={}'.format(\n            self.in_features, self.out_features, self.bias is not None\n        )"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/net_manifest.py",
    "content": "\nimport torch.nn as nn\ndef weights_init(m):\n    def init_Linear(m, final_layer=False):\n        nn.init.orthogonal_(m.weight.data)\n        if final_layer:nn.init.orthogonal_(m.weight.data, gain=0.01)\n        if m.bias is not None: nn.init.uniform_(m.bias.data, a=-0.02, b=0.02)\n\n    initial_fn_dict = {\n        'Net': None,\n        'NetCentralCritic': None,\n        'DataParallel':None,\n        'BatchNorm1d':None,\n        'Concentration':None,\n        'ConcentrationHete':None,\n        'Pnet':None,\n        'Sequential':None,\n        'DataParallel':None,\n        'Tanh':None,\n        'ModuleList':None,\n        'ModuleDict':None,\n        'MultiHeadAttention':None,\n        'SimpleMLP':None,\n        'SimpleAttention':None,\n        'SelfAttention_Module':None,\n        'ReLU':None,\n        'Softmax':None,\n        'DynamicNorm':None,\n        'DynamicNormFix':None,\n        'EXTRACT':None,\n        'LinearFinal':lambda m:init_Linear(m, final_layer=True),\n        'Linear':init_Linear,\n        'ResLinear':None,\n        'LeakyReLU':None,\n        'HyperNet':None,\n        'MyHyperNet':None,\n        'DivTree':None,\n    }\n\n    classname = m.__class__.__name__\n    assert classname in initial_fn_dict.keys(), ('how to handle the initialization of this class? ', classname)\n    init_fn = initial_fn_dict[classname]\n    if init_fn is None: return\n    init_fn(m)\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/norm.py",
    "content": "\"\"\"\n    CASIA, fuqingxu\n    live vector normalization using pytorch, \n    therefore the parameter of normalization (mean and var) \n    can be save together with network parameters\n    light up exclude_one_hot=True to prevent onehot component being normalized\n\"\"\"\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.distributions.categorical import Categorical\nfrom torch.distributions.multivariate_normal import MultivariateNormal\nfrom UTIL.tensor_ops import my_view\nfrom UTIL.tensor_ops import Args2tensor_Return2numpy\n\nclass DynamicNorm(nn.Module):\n    # ! warning! this module will mess with multi-gpu setting!!\n    def __init__(self, input_size, only_for_last_dim, exclude_one_hot=True, exclude_nan=False):\n        super().__init__()\n        assert only_for_last_dim\n        self.exclude_one_hot = exclude_one_hot\n        self.mean = nn.Parameter(torch.zeros(input_size, requires_grad=False), requires_grad=False)\n        self.var = nn.Parameter(torch.ones(input_size, requires_grad=False), requires_grad=False)\n        self.n_sample = nn.Parameter(torch.zeros(1, requires_grad=False, dtype=torch.long), requires_grad=False)\n        if self.exclude_one_hot: \n            self.one_hot_filter = nn.Parameter(torch.ones(input_size, requires_grad=False, dtype=torch.bool), requires_grad=False)\n        self.input_size = input_size\n        self.exclude_nan = exclude_nan\n        self.patience = 1000\n\n    def forward(self, x, get_mu_var=False):\n        assert self.input_size == x.shape[-1], ('self.input_size',self.input_size,'x.shape[-1]',x.shape[-1])\n        _2dx = x.detach().reshape(-1, self.input_size)\n        if self.exclude_nan: _2dx = _2dx[~torch.isnan(_2dx).any(axis=-1)]\n        this_batch_size = _2dx.shape[0]\n        # assert this_batch_size>=1\n        if this_batch_size<=0:  \n            print('Warning! An empty batch just being normalized')\n            x = torch.clip_((x - self.mean) / torch.sqrt_(self.var + 1e-8), -10, 10)\n            return x\n            \n        if self.training:\n            with torch.no_grad():\n                this_batch_mean = torch.mean(_2dx, dim=0)\n                this_batch_var = torch.var(_2dx, dim=0, unbiased=False)\n                if torch.isnan(this_batch_var).any():\n                    assert False, ('nan value detected in normalization! but you can turn on exclude_nan')\n                assert _2dx.dim() == 2\n                delta = this_batch_mean - self.mean\n                tot_count = self.n_sample + this_batch_size\n                new_mean = self.mean + delta * this_batch_size / tot_count\n                m_a = self.var * (self.n_sample)\n                m_b = this_batch_var * (this_batch_size)\n                M2 = m_a + m_b + torch.square_(delta) * self.n_sample * this_batch_size / (self.n_sample + this_batch_size)\n                new_var = M2 / (self.n_sample + this_batch_size)\n                if self.exclude_one_hot:    # 滤除-1,0和1的点位\n                    self.one_hot_filter.data &= ~(((_2dx != 0) & (_2dx != 1) & (_2dx != -1)).any(dim=0))\n                self.mean.data = torch.where(self.one_hot_filter, self.mean, new_mean) if self.exclude_one_hot else new_mean # new_mean\n                new_var_clip = torch.clamp(new_var, min=0.01, max=1000)\n                self.var.data = torch.where(self.one_hot_filter, self.var, new_var_clip) if self.exclude_one_hot else new_var_clip\n                self.n_sample.data = tot_count\n        if get_mu_var:\n            return self.mean, self.var\n        \n        x = torch.clip_((x - self.mean) / torch.sqrt_(self.var + 1e-8), -10, 10)\n        return x\n\n\n    # @Args2tensor_Return2numpy\n    # def get_mean_var(self, x):\n        # return self.forward(x, get_mu_var=True)\n\n\nclass DynamicNormFix(nn.Module):\n    # ! warning! this module will mess with multi-gpu setting!!\n    def __init__(self, input_size, only_for_last_dim, exclude_one_hot=True, exclude_nan=False):\n        super().__init__()\n        assert only_for_last_dim\n        self.exclude_one_hot = exclude_one_hot\n        self.mean = nn.Parameter(torch.zeros(input_size, requires_grad=False), requires_grad=False)\n        self.var = nn.Parameter(torch.ones(input_size, requires_grad=False), requires_grad=False)\n        self.var_fix = nn.Parameter(torch.ones(input_size, requires_grad=False), requires_grad=False)\n        self.min = nn.Parameter(torch.ones(input_size, requires_grad=False)+float('inf'), requires_grad=False)\n        self.max = nn.Parameter(torch.ones(input_size, requires_grad=False)-float('inf'), requires_grad=False)\n        self.n_sample = nn.Parameter(torch.zeros(1, requires_grad=False, dtype=torch.long), requires_grad=False)\n        if self.exclude_one_hot: \n            self.one_hot_filter = nn.Parameter(torch.ones(input_size, requires_grad=False, dtype=torch.bool), requires_grad=False)\n        self.input_size = input_size\n        self.exclude_nan = exclude_nan\n        self.patience = 1000\n        self.var_fix_wait = 1000\n        \n        # var fixing, T2 is maximum x abs value after normalization\n        self.T1 = 5\n        self.T2 = 10\n        self.TD = (self.T2**2 - self.T1**2)/self.T2**2\n        self.first_run = True\n        self.debug = True\n\n    # 兼容np\n    @Args2tensor_Return2numpy\n    def np_forward(self, x, freeze=False, get_mu_var=False):\n        return self.forward(x, freeze, get_mu_var)\n    \n    def forward(self, x, freeze=False, get_mu_var=False):\n        assert self.input_size == x.shape[-1], ('self.input_size',self.input_size,'x.shape[-1]',x.shape[-1])\n        _2dx = x.detach().reshape(-1, self.input_size)\n        if self.exclude_nan: _2dx = _2dx[~torch.isnan(_2dx).any(axis=-1)]\n        _2dx_view = my_view(_2dx, [-1, 0])\n        this_batch_size = _2dx.shape[0]\n        # assert this_batch_size>=1\n        if this_batch_size<=0:  \n            print('Warning! An empty batch just being normalized')\n            x = torch.clip_((x - self.mean) / torch.sqrt_(self.var_fix + 1e-8), -10, 10)\n            return x\n            \n        if self.training and (not freeze):\n            with torch.no_grad():\n                this_batch_mean = torch.mean(_2dx, dim=0)\n                this_batch_var = torch.var(_2dx, dim=0, unbiased=False)\n                if torch.isnan(this_batch_var).any():\n                    assert False, ('nan value detected in normalization! but you can turn on exclude_nan')\n                assert _2dx.dim() == 2\n                delta = this_batch_mean - self.mean\n                tot_count = self.n_sample + this_batch_size\n                new_mean = self.mean + delta * this_batch_size / tot_count\n                m_a = self.var * (self.n_sample)\n                m_b = this_batch_var * (this_batch_size)\n                M2 = m_a + m_b + torch.square_(delta) * self.n_sample * this_batch_size / (self.n_sample + this_batch_size)\n                new_var = M2 / (self.n_sample + this_batch_size)\n                if self.exclude_one_hot:    # 滤除-1,0和1的点位\n                    self.one_hot_filter.data &= ~(((_2dx != 0) & (_2dx != 1) & (_2dx != -1)).any(dim=0))\n                self.mean.data = torch.where(self.one_hot_filter, self.mean, new_mean) if self.exclude_one_hot else new_mean # new_mean\n                # if self.patience > 0: self.check_errors(_2dx, new_var)\n                self.var.data = torch.where(self.one_hot_filter, self.var, new_var) if self.exclude_one_hot else new_var\n\n                # begin fix variance\n                max_tmp, _ = _2dx_view.max(0)\n                min_tmp, _ = _2dx_view.min(0)\n\n                # if self.first_run:\n                if self.patience > 0:\n                    self.patience -= 1\n                    self.first_run = False\n                    self.max.data = torch.maximum(max_tmp, self.max)\n                    self.min.data = torch.minimum(min_tmp, self.min)\n\n                else:\n                    # self.max.data = torch.maximum(max_tmp, self.max)\n                    # self.min.data = torch.minimum(min_tmp, self.min)\n                    self.max.data = self.max + (torch.maximum(max_tmp, self.max)-self.max) * this_batch_size / tot_count\n                    self.min.data = self.min + (torch.minimum(min_tmp, self.min)-self.min) * this_batch_size / tot_count\n                # # if self.debug: self.mcv.rec(max_tmp.squeeze().item(), 'batch max')\n                # # if self.debug: self.mcv.rec(min_tmp.squeeze().item(), 'batch min')\n                # # if self.debug: self.mcv.rec(torch.maximum(max_tmp, self.max).squeeze().item(), 'hist max')\n                # # if self.debug: self.mcv.rec(torch.minimum(min_tmp, self.min).squeeze().item(), 'hist min')\n                # if self.debug: self.mcv.rec(self.max.data, 'fixed max')\n                # if self.debug: self.mcv.rec(self.min.data, 'fixed min')\n                # if self.debug: self.mcv.rec_show()\n                \n                dm = torch.maximum((self.max - self.mean), (self.mean - self.min))\n                # std_th_1 = dm / self.T1\n                std_threshold_2 = dm / self.T2\n                # var1 = std_th_1**2\n                var2 = std_threshold_2**2\n                leak = self.TD * self.var + var2    # leak = (var1 - var2)/(var1) *self.var + var2\n                new_var_fix = torch.maximum(self.var, leak)\n                self.var_fix.data = torch.where(self.one_hot_filter, self.var_fix, new_var_fix) if self.exclude_one_hot else new_var_fix\n                \n                # if self.debug: self.mcv.rec(self.var.data, 'var')\n                # if self.debug: self.mcv.rec(self.var_fix.data, 'var fix')\n                # if self.debug: self.mcv.rec(self.var_fix.data-self.var.data, 'delta var')\n                # if self.debug: self.mcv.rec((1 - self.mean) / torch.sqrt_(self.var_fix + 1e-8), 'base line +1')\n                # if self.debug: self.mcv.rec((-1 - self.mean) / torch.sqrt_(self.var_fix + 1e-8), 'base line -1')\n                # if self.debug: self.mcv.rec((10 - self.mean) / torch.sqrt_(self.var_fix + 1e-8), 'base line +10')\n                # if self.debug: self.mcv.rec((-10 - self.mean) / torch.sqrt_(self.var_fix + 1e-8), 'base line -10')\n                \n                \n                \n                # !!! qq = self.var_fix.data-self.var.data\n                # !!! if self.patience > 0 and self.patience < 800 and (not (qq==0).all()):\n                # !!!     print('[norm.py] Input issue: cannot be well expressed by normal distribution', torch.where(qq!=0))\n                    \n                    \n                self.n_sample.data = tot_count\n                \n                \n        # t = (_2dx_view - self.mean) / torch.sqrt_(self.var_fix + 1e-8)\n        if get_mu_var:\n            return self.mean, self.var_fix\n        return (x - self.mean) / torch.sqrt_(self.var_fix + 1e-8)\n\n    # def check_errors(self, _2dx, new_var):\n    #     self.patience -= 1\n\n\n\n'''\ntest script\n\nimport torch, time\nfrom ALGORITHM.common.norm import DynamicNormFix\n\ninput_size = 1\nonly_for_last_dim = True\ndynamic_norm = DynamicNormFix(input_size, only_for_last_dim, exclude_one_hot=True, exclude_nan=False)\n\nfor _ in range(101100):\n    \n    # mask = (torch.randn(60, 1, out=None) > 0)\n    # x = torch.where(mask,\n    #                 torch.randn(60, 1, out=None)*10,\n    #                 torch.randn(60, 1, out=None)*5,\n    #                 )\n    # 左边\n    std = 0.01; offset = -0.01;  num = 5\n    x3 = torch.randn(num, 1, out=None) * std + offset\n  \n    # 中间\n    std = 0.01; offset = 0;    num = 500\n    x2 = torch.randn(num, 1, out=None) * std + offset\n\n    # 右边\n    std = 0.01; offset = 1;   num = 5\n    x1 = torch.randn(num, 1, out=None) * std + offset\n        \n        \n    # # 左边\n    # std = 1; offset = -10;  num = 5\n    # x3 = torch.randn(num, 1, out=None) * std + offset\n  \n    # # 中间\n    # std = 1; offset = 5;    num = 500\n    # x2 = torch.randn(num, 1, out=None) * std + offset\n\n    # # 右边\n    # std = 1; offset = 5;   num = 5\n    # x1 = torch.randn(num, 1, out=None) * std + offset\n    \n    x = torch.cat((x1,x2,x3), 0)\n    y = dynamic_norm(x)\n    \nprint(y)\ntime.sleep(60)\n\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/pca.py",
    "content": "import numpy as np\n\ndef pca(samples, target_dim):\n    assert len(samples.shape) == 2\n    data = samples - np.mean(samples,axis=0)  # mean at batch dim\n    covMat = np.cov(data,rowvar=0)\n    fValue,fVector = np.linalg.eig(covMat)\n    fValueSort = np.argsort(-fValue)\n    fValueTopN = fValueSort[:target_dim]\n    fvectormat = fVector[:,fValueTopN]\n    down_dim_data = np.dot(data, fvectormat)\n    return down_dim_data"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/ppo_sampler.py",
    "content": "import torch, math\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport numpy as np\nfrom random import randint, sample\nfrom torch.utils.data.sampler import BatchSampler, SubsetRandomSampler\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import _2tensor, __hash__, repeat_at, _2cpu2numpy\nfrom UTIL.tensor_ops import my_view, scatter_with_nan, sample_balance\nfrom config import GlobalConfig as cfg\nfrom UTIL.gpu_share import GpuShareUnit\n\nclass TrajPoolSampler():\n    def __init__(self, n_div, traj_pool, flag, req_dict, req_dict_rename, prevent_batchsize_oom=False, mcv=None):\n        self.n_pieces_batch_division = n_div\n        self.prevent_batchsize_oom = prevent_batchsize_oom    \n        self.mcv = mcv\n        if self.prevent_batchsize_oom:\n            assert self.n_pieces_batch_division==1, 'self.n_pieces_batch_division should be 1'\n\n        self.num_batch = None\n        self.container = {}\n        self.warned = False\n        assert flag=='train'\n        # req_dict =        ['obs', 'state', 'action', 'actionLogProb', 'return', 'reward', 'threat', 'value']\n        # req_dict_rename = ['obs', 'state', 'action', 'actionLogProb', 'return', 'reward', 'threat', 'state_value']\n        if cfg.ScenarioConfig.AvailActProvided:\n            req_dict.append('avail_act')\n            req_dict_rename.append('avail_act')\n        return_rename = \"return\"\n        value_rename =  \"state_value\"\n        advantage_rename = \"advantage\"\n        # replace 'obs' to 'obs > xxxx'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name):\n                real_key_list = [real_key for real_key in traj_pool[0].__dict__ if (key_name+'>' in real_key)]\n                assert len(real_key_list) > 0, ('check variable provided!', key,key_index)\n                for real_key in real_key_list:\n                    mainkey, subkey = real_key.split('>')\n                    req_dict.append(real_key)\n                    req_dict_rename.append(key_rename+'>'+subkey)\n        self.big_batch_size = -1  # vector should have same length, check it!\n        \n        # load traj into a 'container'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name): continue\n            set_item = np.concatenate([getattr(traj, key_name) for traj in traj_pool], axis=0)\n            if not (self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0)):\n                print('error')\n            assert self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0), (key,key_index)\n            self.big_batch_size = set_item.shape[0]\n            self.container[key_rename] = set_item    # assign value to key_rename\n\n        # normalize advantage inside the batch\n        self.container[advantage_rename] = self.container[return_rename] - self.container[value_rename]\n        self.container[advantage_rename] = ( self.container[advantage_rename] - self.container[advantage_rename].mean() ) / (self.container[advantage_rename].std() + 1e-5)\n        # size of minibatch for each agent\n        self.mini_batch_size = math.ceil(self.big_batch_size / self.n_pieces_batch_division)  \n\n        # do once\n        self.do_once_fin = False\n\n    def __len__(self):\n        return self.n_pieces_batch_division\n\n    def reminder(self, n_sample):\n        if not self.do_once_fin:\n            self.do_once_fin = True\n            drop_percent = (self.big_batch_size-n_sample) / self.big_batch_size*100\n            if self.mcv is not None: self.mcv.rec(drop_percent, 'drop percent')\n            if drop_percent > 20: \n                print_ = print亮红\n                print_('droping %.1f percent samples..'%(drop_percent))\n                assert False, \"GPU OOM!\"\n            else:\n                print_ = print\n                print_('droping %.1f percent samples..'%(drop_percent))\n\n    def get_sampler(self):\n        if not self.prevent_batchsize_oom:\n            # \n            sampler = BatchSampler(SubsetRandomSampler(range(self.big_batch_size)), self.mini_batch_size, drop_last=False)\n        else:\n            max_n_sample = self.determine_max_n_sample()\n            n_sample = min(self.big_batch_size, max_n_sample)\n            self.reminder(n_sample)\n            sampler = BatchSampler(SubsetRandomSampler(range(n_sample)), n_sample, drop_last=False)\n        return sampler\n\n    def reset_and_get_iter(self):\n        self.sampler = self.get_sampler()\n        for indices in self.sampler:\n            selected = {}\n            for key in self.container:\n                selected[key] = self.container[key][indices]\n            for key in [key for key in selected if '>' in key]:\n                # re-combine child key with its parent\n                mainkey, subkey = key.split('>')\n                if not mainkey in selected: selected[mainkey] = {}\n                selected[mainkey][subkey] = selected[key]\n                del selected[key]\n            yield selected\n\n\n    def determine_max_n_sample(self):\n        assert self.prevent_batchsize_oom\n        if not hasattr(TrajPoolSampler,'MaxSampleNum'):\n            # initialization\n            TrajPoolSampler.MaxSampleNum =  [int(self.big_batch_size*(i+1)/50) for i in range(50)]\n            max_n_sample = self.big_batch_size\n        elif TrajPoolSampler.MaxSampleNum[-1] > 0:  \n            # meaning that oom never happen, at least not yet\n            # only update when the batch size increases\n            if self.big_batch_size > TrajPoolSampler.MaxSampleNum[-1]:\n                TrajPoolSampler.MaxSampleNum.append(self.big_batch_size)\n            max_n_sample = self.big_batch_size\n        else:\n            # meaning that oom already happened, choose TrajPoolSampler.MaxSampleNum[-2] to be the limit\n            assert TrajPoolSampler.MaxSampleNum[-2] > 0\n            max_n_sample = TrajPoolSampler.MaxSampleNum[-2]\n        return max_n_sample\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/rl_alg_base.py",
    "content": "import time\nfrom UTIL.tensor_ops import __hash__, repeat_at\nfrom UTIL.colorful import *\nfrom .alg_base import AlgorithmBase\n\n# model IO\nclass RLAlgorithmBase(AlgorithmBase):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        super().__init__(n_agent, n_thread, space, mcv, team)\n        \n        # data integraty check\n        self._unfi_frag_ = None\n\n        # Skip currupt data integraty check after this patience is exhausted\n        self.patience = 1000\n\n\n\n    def interact_with_env(self, team_intel):\n        raise NotImplementedError\n\n    def save_model(self, update_cnt, info=None):\n        raise NotImplementedError\n\n    def process_framedata(self, traj_framedata):\n        raise NotImplementedError\n\n    # Rollout Processor 准备提交Rollout，以下划线开头和结尾的键值需要对齐(self.n_thread, ...)\n    # note that keys starting with _ must have shape (self.n_thread, ...), details see fn:mask_paused_env()\n    def process_framedata(self, traj_framedata):\n        ''' \n            hook is called when reward and next moment observation is ready,\n            now feed them into trajectory manager.\n            Rollout Processor | 准备提交Rollout, 以下划线开头和结尾的键值需要对齐(self.n_thread, ...)\n            note that keys starting with _ must have shape (self.n_thread, ...), details see fn:mask_paused_env()\n        '''\n        # strip info, since it is not array\n        items_to_pop = ['info', 'Latest-Obs']\n        for k in items_to_pop:\n            if k in traj_framedata:\n                traj_framedata.pop(k)\n        # the agent-wise reward is supposed to be the same, so averge them\n        if self.ScenarioConfig.RewardAsUnity:\n            traj_framedata['reward'] = repeat_at(traj_framedata['reward'], insert_dim=-1, n_times=self.n_agent)\n        # change the name of done to be recognised (by trajectory manager)\n        traj_framedata['_DONE_'] = traj_framedata.pop('done')\n        traj_framedata['_TOBS_'] = traj_framedata.pop(\n            'Terminal-Obs-Echo') if 'Terminal-Obs-Echo' in traj_framedata else None\n        # mask out pause thread\n        traj_framedata = self.mask_paused_env(traj_framedata)\n        # put the frag into memory\n        self.batch_traj_manager.feed_traj(traj_framedata)\n\n\n    def check_reward_type(self, AlgorithmConfig):\n        if self.ScenarioConfig.RewardAsUnity != AlgorithmConfig.TakeRewardAsUnity:\n            assert self.ScenarioConfig.RewardAsUnity\n            assert not AlgorithmConfig.TakeRewardAsUnity\n            print亮紫(\n                'Warning, the scenario (MISSION) provide `RewardAsUnity`, but AlgorithmConfig does not `TakeRewardAsUnity` !')\n            print亮紫(\n                'If you continue, team reward will be duplicated to serve as individual rewards, wait 3s to proceed...')\n            time.sleep(3)\n\n    def mask_paused_env(self, frag):\n        running = ~frag['_SKIP_']\n        if running.all():\n            return frag\n        for key in frag:\n            if not key.startswith('_') and hasattr(frag[key], '__len__') and len(frag[key]) == self.n_thread:\n                frag[key] = frag[key][running]\n        return frag\n\n\n    '''\n        Get event from hmp task runner, called when each test rotinue is complete.\n    '''\n    def on_notify(self, message, **kargs):\n        self.save_model(\n            update_cnt=self.traj_manager.update_cnt,\n            info=str(kargs)\n        )\n\n\n\n\n    ''' \n        function to be called when reward is received\n    '''\n    def commit_traj_frag(self, unfi_frag, req_hook=True):\n        assert self._unfi_frag_ is None\n        self._unfi_frag_ = unfi_frag\n        self._check_data_hash()  # check data integraty\n        if req_hook:\n            # leave a hook\n            return self.traj_waiting_hook\n        else:\n            return None\n\n\n\n\n\n    def traj_waiting_hook(self, new_frag):\n        ''' \n            This function will be called from <multi_team.py::deal_with_hook()>\n            hook is called when reward and next moment observation is ready\n        '''\n        # do data curruption check at beginning, this is important!\n        self._check_data_curruption()\n        # finish the frame data with new data feedin\n        fi_frag = self._unfi_frag_\n        fi_frag.update(new_frag)\n        # call upper level function to deal with frame data\n        self.process_framedata(traj_framedata=fi_frag)\n        # delete data reference\n        self._unfi_frag_ = None\n\n\n    def _no_hook(self, new_frag):\n        return\n\n\n\n    # protect data from overwriting\n    def _check_data_hash(self):\n        if self.patience > 0:\n            self.patience -= 1\n            self.hash_db = {}\n            # for debugging, to detect write protection error\n            for key in self._unfi_frag_:\n                item = self._unfi_frag_[key]\n                if isinstance(item, dict):\n                    self.hash_db[key] = {}\n                    for subkey in item:\n                        subitem = item[subkey]\n                        self.hash_db[key][subkey] = __hash__(subitem)\n                else:\n                    self.hash_db[key] = __hash__(item)\n\n    # protect data from overwriting\n    def _check_data_curruption(self):\n        if self.patience > 0:\n            self.patience -= 1\n            assert self._unfi_frag_ is not None\n            assert self.hash_db is not None\n            for key in self._unfi_frag_:\n                item = self._unfi_frag_[key]\n                if isinstance(item, dict):\n                    for subkey in item:\n                        subitem = item[subkey]\n                        assert self.hash_db[key][subkey] == __hash__(subitem), ('Currupted data!')\n                else:\n                    assert self.hash_db[key] == __hash__(item), ('Currupted data!')\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/traj.py",
    "content": "# cython: language_level=3\nimport numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import __hash__\n\nclass TRAJ_BASE():\n    key_data_type = {}\n    key_data_shape = {}\n    max_mem_length = -1\n\n    def __init__(self, traj_limit, env_id):\n        self.traj_limit = traj_limit\n        self.env_id = env_id\n        self.readonly_lock = False\n        self.key_dict = []\n        self.time_pointer = 0\n        self.need_reward_bootstrap = False\n        self.deprecated_flag = False\n\n    # remember something in a time step, add it to trajectory\n    def remember(self, key, content):\n        assert not self.readonly_lock\n        if not (key in self.key_dict) and (content is not None):\n            self.init_track(key=key, first_content=content)\n            getattr(self, key)[self.time_pointer] = content\n        elif not (key in self.key_dict) and (content is None):\n            self.init_track_none(key=key)\n        elif (key in self.key_dict) and (content is not None):\n            getattr(self, key)[self.time_pointer] = content\n        else:\n            pass\n    \n    # duplicate/rename a trajectory\n    def copy_track(self, origin_key, new_key):\n        if hasattr(self, origin_key):\n            origin_handle = getattr(self, origin_key)\n            setattr(self, new_key, origin_handle.copy())\n            new_handle = getattr(self, new_key)\n            self.key_dict.append(new_key)\n            #return origin_handle, new_handle\n        else:\n            real_key_list = [real_key for real_key in self.__dict__ if (origin_key+'>' in real_key)]\n            assert len(real_key_list)>0, ('this key does not exist (yet), check:', origin_key)\n            for real_key in real_key_list:\n                mainkey, subkey = real_key.split('>')\n                self.copy_track(real_key, (new_key+'>'+subkey))\n            #return\n\n    # make sure dtype is ok\n    def check_type_shape(self, key, first_content=None):\n        if first_content is not None:\n            content_type = first_content.dtype\n            content_shape = first_content.shape\n            if key in TRAJ_BASE.key_data_type: \n                assert TRAJ_BASE.key_data_type[key] == content_type\n            else: \n                TRAJ_BASE.key_data_type[key] = content_type\n                TRAJ_BASE.key_data_shape[key] = content_shape\n            return content_type, content_shape\n        assert key in TRAJ_BASE.key_data_type\n        return TRAJ_BASE.key_data_type[key], TRAJ_BASE.key_data_shape[key]\n\n    # create track, executed used when a key showing up for the first time in 'self.remember'\n    def init_track(self, key, first_content):\n        content = first_content\n        self.check_type_shape(key, first_content)\n        assert isinstance(content, np.ndarray) or isinstance(content, float), (key, content.__class__)\n        tensor_size = ((self.traj_limit,) + tuple(content.shape))\n        set_item = np.zeros(shape=tensor_size, dtype=content.dtype)\n        set_item[:] = np.nan  if np.issubdtype(content.dtype, np.floating) else 0\n        setattr(self, key, set_item)\n        self.key_dict.append(key)\n\n    # key pop up yet content is None, \n    # read dtype from history dtype dictionary to fill the hole\n    def init_track_none(self, key):\n        content_dtype, content_shape = self.check_type_shape(key)\n        tensor_size = ((self.traj_limit,) + tuple(content_shape))\n        set_item = np.zeros(shape=tensor_size, dtype=content_dtype)\n        set_item[:] = np.nan  if np.issubdtype(content_dtype, np.floating) else 0\n        setattr(self, key, set_item)\n        self.key_dict.append(key)\n\n    # push the time pointer forward, before you call 'self.remember' again to fill t+1 data\n    def time_shift(self):\n        assert self.time_pointer < self.traj_limit\n        self.time_pointer += 1\n\n    # cut trajectory tail, when the number of episode time step < traj_limit\n    def cut_tail(self): \n        TJ = lambda key: getattr(self, key)\n        self.readonly_lock = True\n        n_frame = self.time_pointer\n        # check is buffer size too big\n        if n_frame > TRAJ_BASE.max_mem_length: \n            TRAJ_BASE.max_mem_length = n_frame\n            print('max_mem_length：%d, traj_limit:%d'%(TRAJ_BASE.max_mem_length, self.traj_limit))\n        # clip tail\n        for key in self.key_dict: setattr(self, key, TJ(key)[:n_frame])"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/traj_gae.py",
    "content": "# cython: language_level=3\nimport numpy as np\nfrom ALGORITHM.common.traj import TRAJ_BASE\nimport copy\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, repeat_at, gather_righthand\n\nclass trajectory(TRAJ_BASE):\n    dead_mask_check = True  # confirm mask ok\n    def __init__(self, traj_limit, env_id, alg_cfg):\n        super().__init__(traj_limit, env_id)\n        self.agent_alive_reference = 'alive'\n        self.alg_cfg = alg_cfg\n\n    def early_finalize(self):\n        assert not self.readonly_lock   # unfinished traj\n        self.need_reward_bootstrap = True\n\n    def set_terminal_obs(self, tobs):\n        self.tobs = copy.deepcopy(tobs)\n\n    def cut_tail(self):\n        # 删去多余的预留空间\n        super().cut_tail()\n        TJ = lambda key: getattr(self, key)\n        # 进一步地， 根据这个轨迹上的NaN，删除所有无效时间点\n        agent_alive = getattr(self, self.agent_alive_reference)\n        assert len(agent_alive.shape) == 2, \"shoud be 2D (time, agent)/dead_or_alive\"\n        if self.need_reward_bootstrap:\n            assert False, ('it should not go here if everything goes as expected')\n        # deprecated if nothing in it\n        p_valid = agent_alive.any(axis=-1)\n        p_invalid = ~p_valid\n        is_fully_valid_traj = (p_valid[-1] == True)\n        # assert p_valid[-1] == True, 如果有三只队伍，很有可能出现一只队伍全体阵亡，但游戏仍未结束的情况\n        if p_invalid.all(): #invalid traj\n            self.deprecated_flag = True\n            return\n        if not is_fully_valid_traj:\n            # adjust reward position if not fully valid\n            reward = TJ('reward')\n            for i in reversed(range(self.time_pointer)):\n                if p_invalid[i] and i != 0: # invalid, push reward forward\n                    reward[i-1] += reward[i]; reward[i] = np.nan\n            setattr(self, 'reward', reward)\n        # clip NaN\n        for key in self.key_dict: setattr(self, key, TJ(key)[p_valid])\n        if not is_fully_valid_traj:\n            # reset time pointer\n            self.time_pointer = p_valid.sum()\n        # all done\n        return\n\n    def reward_push_forward(self, dead_mask):\n        # self.new_reward = self.reward.copy()\n        if self.alg_cfg.gamma_in_reward_forwarding:\n            gamma = self.alg_cfg.gamma_in_reward_forwarding_value \n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i]*gamma, 0)  # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])        # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n\n        else:\n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i], 0)        # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])        # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n        return\n\n\n    # new finalize\n    def finalize(self):\n        self.readonly_lock = True\n        assert not self.deprecated_flag\n        TJ = lambda key: getattr(self, key) \n        assert not np.isnan(TJ('reward')).any()\n        # deadmask\n        agent_alive = getattr(self, self.agent_alive_reference)\n        dead_mask = ~agent_alive\n\n        if trajectory.dead_mask_check:\n            trajectory.dead_mask_check = False\n            if not dead_mask.any(): \n                assert False, \"Are you sure agents cannot die? If so, delete this check.\"\n\n        self.reward_push_forward(dead_mask) # push terminal reward forward 38 42 54\n        threat = np.zeros(shape=dead_mask.shape) - 1\n        assert dead_mask.shape[0] == self.time_pointer\n        for i in reversed(range(self.time_pointer)):\n            # threat[:(i+1)] 不包含threat[(i+1)]\n            if i+1 < self.time_pointer:\n                threat[:(i+1)] += (~(dead_mask[i+1]&dead_mask[i])).astype(np.int)\n            elif i+1 == self.time_pointer:\n                threat[:] += (~dead_mask[i]).astype(np.int)\n\n        SAFE_LIMIT = 8\n        threat = np.clip(threat, -1, SAFE_LIMIT)\n        setattr(self, 'threat', np.expand_dims(threat, -1))\n\n        # ! Use GAE to calculate return\n        if self.alg_cfg.use_policy_resonance:\n            self.gae_finalize_return_pr(reward_key='reward', value_key='BAL_value_all_level', new_return_name='BAL_return_all_level')\n        else:\n            self.gae_finalize_return(reward_key='reward', value_key='value', new_return_name='return')\n        return\n\n    def gae_finalize_return(self, reward_key, value_key, new_return_name):\n        # ------- gae parameters -------\n        gamma = self.alg_cfg.gamma \n        tau = self.alg_cfg.tau\n        # ------- -------------- -------\n        rewards = getattr(self, reward_key)\n        value = getattr(self, value_key)\n        # ------- -------------- -------\n        length = rewards.shape[0]\n        assert rewards.shape[0]==value.shape[0]\n        # if dimension not aligned\n        if rewards.ndim == value.ndim-1: rewards = np.expand_dims(rewards, -1)\n        # initalize two more tracks\n        setattr(self, new_return_name, np.zeros_like(value))\n        self.key_dict.append(new_return_name)\n        # ------- -------------- -------\n        returns = getattr(self, new_return_name)\n        boot_strap = 0 if not self.need_reward_bootstrap else self.boot_strap_value['bootstrap_'+value_key]\n\n        for step in reversed(range(length)):\n            if step==(length-1): # 最后一帧\n                value_preds_delta = rewards[step] + gamma * boot_strap      - value[step]\n                gae = value_preds_delta\n            else:\n                value_preds_delta = rewards[step] + gamma * value[step + 1] - value[step]\n                gae = value_preds_delta + gamma * tau * gae\n            returns[step] = gae + value[step]\n\n    def gae_finalize_return_pr(self, reward_key, value_key, new_return_name):\n        # ------- gae parameters -------\n        gamma = self.alg_cfg.gamma \n        tau = self.alg_cfg.tau\n        # ------- -------------- -------\n        BAL_value_all_level = copy.deepcopy(getattr(self, value_key))\n        # reshape to (batch, agent*distribution_precision, 1)\n        value = my_view(BAL_value_all_level, [0, -1, 1])\n        # ------- ------- reshape reward ------- -------\n        rewards_cp = copy.deepcopy(getattr(self, reward_key))\n        # if dimension not aligned\n        if rewards_cp.ndim == value.ndim-1: rewards_cp = np.expand_dims(rewards_cp, -1)\n        assert rewards_cp.shape[-1] == 1\n        n_agent = rewards_cp.shape[-2]\n        assert BAL_value_all_level.shape[-2] == n_agent\n        assert BAL_value_all_level.shape[-1] == self.alg_cfg.distribution_precision\n        rewards_cp = repeat_at(rewards_cp.squeeze(-1), -1, self.alg_cfg.distribution_precision)\n        rewards_cp = my_view(rewards_cp, [0, -1, 1])\n        # ------- -------------- -------\n        length = rewards_cp.shape[0]\n        assert rewards_cp.shape[0]==value.shape[0]\n        # ------- -------------- -------\n        returns = np.zeros_like(value)\n        boot_strap = 0 if not self.need_reward_bootstrap else self.boot_strap_value['bootstrap_'+value_key]\n        for step in reversed(range(length)):\n            if step==(length-1): # 最后一帧\n                value_preds_delta = rewards_cp[step] + gamma * boot_strap      - value[step]\n                gae = value_preds_delta\n            else:\n                value_preds_delta = rewards_cp[step] + gamma * value[step + 1] - value[step]\n                gae = value_preds_delta + gamma * tau * gae\n            returns[step] = gae + value[step]\n        # ------- -------------- -------\n        returns = my_view(returns, [0, n_agent, self.alg_cfg.distribution_precision])   # BAL_return_all_level\n        setattr(self, new_return_name, returns)\n        self.key_dict.append(new_return_name)\n\n        \n        def select_value_level(BAL_all_level, randl):\n            n_agent = BAL_all_level.shape[1]\n            tmp_index = np.expand_dims(repeat_at(randl, -1, n_agent), -1)\n\n            return gather_righthand(src=BAL_all_level, index=tmp_index, check=False)\n\n        self.value_selected = select_value_level(BAL_all_level=self.BAL_value_all_level, randl=self.randl)\n        self.return_selected = select_value_level(BAL_all_level=self.BAL_return_all_level, randl=self.randl)\n\n\n'''\n    轨迹池管理\n'''\n\nclass TrajManagerBase(object):\n    def __init__(self, n_env, traj_limit, alg_cfg):\n        self.alg_cfg = alg_cfg\n        self.n_env = n_env\n        self.traj_limit = traj_limit\n        self.update_cnt = 0\n        self.traj_pool = []\n        self.registered_keys = []\n        self.live_trajs = [trajectory(self.traj_limit, env_id=i, alg_cfg=self.alg_cfg) for i in range(self.n_env)]\n        self.live_traj_frame = [0 for _ in range(self.n_env)]\n        self._traj_lock_buf = None\n        self.patience = 1000\n        pass\n    \n    def __check_integraty(self, traj_frag):\n        if self.patience < 0: \n            return # stop wasting time checking this\n        self.patience -= 1\n        for key in traj_frag:\n            if key not in self.registered_keys and (not key.startswith('_')):\n                self.registered_keys.append(key)\n        for key in self.registered_keys:\n            assert key in traj_frag, ('this key sometimes disappears from the traj_frag:', key)\n\n    def batch_update(self, traj_frag):\n        self.__check_integraty(traj_frag)\n        done = traj_frag['_DONE_']; traj_frag.pop('_DONE_') # done flag\n        skip = traj_frag['_SKIP_']; traj_frag.pop('_SKIP_') # skip/frozen flag\n        tobs = traj_frag['_TOBS_']; traj_frag.pop('_TOBS_') # terminal obs\n        # single bool to list bool\n        if isinstance(done, bool): done = [done for _ in range(self.n_env)]\n        if isinstance(skip, bool): skip = [skip for _ in range(self.n_env)]\n        n_active = sum(~skip)\n        # feed\n        cnt = 0\n        for env_i in range(self.n_env):\n            if skip[env_i]: continue\n            # otherwise\n            frag_index = cnt; cnt += 1\n            env_index = env_i\n            traj_handle = self.live_trajs[env_index]\n            for key in traj_frag:\n                self.traj_remember(traj_handle, key=key, content=traj_frag[key],frag_index=frag_index, n_active=n_active)\n            self.live_traj_frame[env_index] += 1\n            traj_handle.time_shift()\n            if done[env_i]:\n                assert tobs[env_i] is not None # get the final obs\n                traj_handle.set_terminal_obs(tobs[env_i])\n                self.traj_pool.append(traj_handle)\n                self.live_trajs[env_index] = trajectory(self.traj_limit, env_id=env_index, alg_cfg=self.alg_cfg)\n                self.live_traj_frame[env_index] = 0\n\n    def traj_remember(self, traj, key, content, frag_index, n_active):\n        if content is None: traj.remember(key, None)\n        elif isinstance(content, dict):\n            for sub_key in content: \n                self.traj_remember(traj, \"\".join((key , \">\" , sub_key)), content=content[sub_key], frag_index=frag_index, n_active=n_active)\n        else:\n            assert n_active == len(content), ('length error')\n            traj.remember(key, content[frag_index]) # *\n\n\nclass BatchTrajManager(TrajManagerBase):\n    def __init__(self, n_env, traj_limit, trainer_hook, alg_cfg):\n        super().__init__(n_env, traj_limit, alg_cfg)\n        self.trainer_hook = trainer_hook\n        self.traj_limit = traj_limit\n\n    # 函数入口\n    def feed_traj(self, traj_frag, require_hook=False):\n        if require_hook:  raise ModuleNotFoundError(\"not supported anymore\")\n        assert self._traj_lock_buf is None\n        assert '_DONE_' in traj_frag\n        assert '_SKIP_' in traj_frag\n        self.batch_update(traj_frag=traj_frag)  # call parent's batch_update()\n        return\n\n    def train_and_clear_traj_pool(self):\n        print('do update %d'%self.update_cnt)\n        for traj_handle in self.traj_pool:\n            traj_handle.cut_tail()\n        self.traj_pool = list(filter(lambda traj: not traj.deprecated_flag, self.traj_pool))\n        for traj_handle in self.traj_pool: traj_handle.finalize()\n        self.trainer_hook(self.traj_pool, 'train')\n        self.traj_pool = []\n        self.update_cnt += 1\n        return self.update_cnt\n\n    def can_exec_training(self):\n        num_traj_needed = self.alg_cfg.train_traj_needed\n        if len(self.traj_pool) >= num_traj_needed:  \n            return True\n        else:  \n            return False\n "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/common/traj_manager.py",
    "content": ""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/example_foundation.py",
    "content": "import numpy as np\nimport copy\nimport math\nimport random\n\nclass ExampleFoundation():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_thread = n_thread\n        self.n_agent = n_agent\n        self.handler = [None for _  in range(self.n_thread)]\n\n    def interact_with_env(self, team_intel):\n        info = team_intel['Latest-Team-Info']\n        done = team_intel['Env-Suffered-Reset']\n        step_cnt = team_intel['Current-Obs-Step']\n        action_list = np.zeros(shape=(self.n_agent, self.n_thread, 1))\n        return action_list, team_intel\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/ccategorical.py",
    "content": "from torch.distributions.categorical import Categorical\nimport torch\nfrom .foundation import AlgorithmConfig\nfrom UTIL.tensor_ops import repeat_at, _2tensor\nfrom torch.distributions import kl_divergence\nEPS = 1e-9\n# yita = p_hit = 0.14\n\ndef random_process(probs, rsn_flag):\n    yita = AlgorithmConfig.yita\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(-1, max_place, 1).bool()\n        pmax = probs[mask_max]\n        if rsn_flag:\n            assert max_place.shape[-1] == 1\n            return max_place.squeeze(-1)\n        else:\n            # forbit max prob being chosen, pmax = probs.max(axis=-1)\n            p_hat = pmax + (pmax-1)/(1/yita-1)\n            k = 1/(1-yita)\n            #!!! write\n            probs *= k  \n            #!!! write\n            probs[mask_max] = p_hat \n            # print(probs)\n            dist = Categorical(probs=probs)\n            samp = dist.sample()\n            assert samp.shape[-1] != 1\n            return samp\n\ndef random_process_allow_big_yita(probs, rsn_flag):\n    yita = AlgorithmConfig.yita\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(-1, max_place, 1).bool()\n        pmax = probs[mask_max].reshape(max_place.shape) #probs[max_place].clone()\n        if rsn_flag:\n            assert max_place.shape[-1] == 1\n            return max_place.squeeze(-1)\n        else:\n            # forbit max prob being chosen\n            # pmax = probs.max(axis=-1) #probs[max_place].clone()\n            yita_arr = torch.ones_like(pmax)*yita\n            yita_arr_clip = torch.minimum(pmax, yita_arr)\n            # p_hat = pmax + (pmax-1) / (1/yita_arr_clip-1) + 1e-10\n            p_hat = (pmax-yita_arr_clip)/(1-yita_arr_clip)\n            k = 1/(1-yita_arr_clip)\n            probs *= k\n            probs[mask_max] = p_hat.reshape(-1)\n\n            # print(probs)\n            dist = Categorical(probs=probs)\n            samp = dist.sample()\n            assert samp.shape[-1] != 1\n            return samp #.squeeze(-1)\n\n\n\ndef random_process_with_clamp3(probs, yita, yita_min_prob, rsn_flag):\n\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(dim=-1, index=max_place, value=1).bool()\n        pmax = probs[mask_max].reshape(max_place.shape)\n        # act max\n        assert max_place.shape[-1] == 1\n        act_max = max_place.squeeze(-1)\n        # act samp\n        yita_arr = torch.ones_like(pmax)*yita\n        # p_hat = pmax + (pmax-1) / (1/yita_arr_clip-1) + 1e-10\n        p_hat = (pmax-yita_arr)/((1-yita_arr)+EPS)\n        p_hat = p_hat.clamp(min=yita_min_prob)\n        k = (1-p_hat)/((1-pmax)+EPS)\n        probs *= k\n        probs[mask_max] = p_hat.reshape(-1)\n        dist = Categorical(probs=probs)\n        act_samp = dist.sample()\n        # assert act_samp.shape[-1] != 1\n        hit_e = _2tensor(rsn_flag)\n        return torch.where(hit_e, act_max, act_samp)\n\n\nclass CCategorical():\n    def __init__(self, planner):\n        self.planner = planner\n        \n        pass\n\n    def sample(self, dist, eprsn):\n        probs = dist.probs.clone()\n        return random_process_with_clamp3(probs, self.planner.yita, self.planner.yita_min_prob, eprsn)\n\n    def register_rsn(self, rsn_flag):\n        self.rsn_flag = rsn_flag\n\n    def feed_logits(self, logits):\n        try:\n            return Categorical(logits=logits)\n        except:\n            print('error')\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/cython_func.pyx",
    "content": "import numpy as np\ncimport numpy as np\ncimport cython\nfrom cython.parallel import prange\nnp.import_array()\nctypedef fused DTYPE_t:\n    np.float32_t\n    np.float64_t\n\nctypedef fused DTYPE_intlong_t:\n    np.int64_t\n    np.int32_t  # to compat Windows\n    \nctypedef np.uint8_t DTYPE_bool_t\n\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\n@cython.nonecheck(False)\ndef roll_hisory( DTYPE_t[:,:,:,:] obs_feed_new, \n                DTYPE_t[:,:,:,:] prev_obs_feed, \n                DTYPE_bool_t[:,:,:] valid_mask, \n                DTYPE_intlong_t[:,:] N_valid, \n                DTYPE_t[:,:,:,:] next_his_pool):\n    # how many threads\n    cdef Py_ssize_t vmax = N_valid.shape[0]\n    # how many agents\n    cdef Py_ssize_t wmax = N_valid.shape[1]\n    # how many entity subjects (including self @0)\n    cdef Py_ssize_t max_obs_entity = obs_feed_new.shape[2]\n    cdef int n_v, th, a, t, k, pointer\n    for th in prange(vmax, nogil=True):\n        # for each thread range -> prange\n        for a in prange(wmax):\n            # for each agent\n            pointer = 0\n            # step 1 fill next_his_pool[0 ~ (nv-1)] with obs_feed_new[0 ~ max_obs_entity-1]\n            for k in range(max_obs_entity):\n                if valid_mask[th,a,k]:\n                    next_his_pool[th, a, pointer] = obs_feed_new[th,a,k]\n                    pointer = pointer + 1\n\n            # step 2 fill next_his_pool[nv ~ (max_obs_entity-1)] with prev_obs_feed[0 ~ (max_obs_entity-1-nv)]\n            n_v = N_valid[th,a]\n            for k in range(n_v, max_obs_entity):\n                next_his_pool[th,a,k] = prev_obs_feed[th,a,k-n_v]\n    return np.asarray(next_his_pool)"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/div_tree.py",
    "content": "import torch\nimport torch.nn as nn\nimport numpy as np\nfrom ALGORITHM.common.mlp import LinearFinal\nfrom UTIL.tensor_ops import add_onehot_id_at_last_dim, add_onehot_id_at_last_dim_fixlen, repeat_at, _2tensor, gather_righthand, scatter_righthand\n\n\n    \nclass DivTree(nn.Module): # merge by MLP version\n    def __init__(self, input_dim, h_dim, n_action):\n        super().__init__()\n\n        # to design a division tree, I need to get the total number of agents\n        from .foundation import AlgorithmConfig\n        self.n_agent = AlgorithmConfig.n_agent\n        self.div_tree = get_division_tree(self.n_agent)\n        self.n_level = len(self.div_tree)\n        self.max_level = len(self.div_tree) - 1\n        self.current_level = 0\n        self.init_level = AlgorithmConfig.div_tree_init_level\n        if self.init_level < 0:\n            self.init_level = self.max_level\n        self.current_level_floating = 0.0\n\n        get_net = lambda: nn.Sequential(\n            nn.Linear(h_dim+self.n_agent, h_dim), \n            nn.ReLU(inplace=True),\n            LinearFinal(h_dim, n_action)\n        )\n        # Note: this is NOT net defining for each agent\n        # Instead, all agents starts from self.nets[0]\n        self.nets = torch.nn.ModuleList(modules=[\n            get_net() for i in range(self.n_agent)  \n        ])\n\n    def set_to_init_level(self, auto_transfer=True):\n        if self.init_level!=self.current_level:\n            for i in range(self.current_level, self.init_level):\n                self.change_div_tree_level(i+1, auto_transfer)\n\n\n\n    def change_div_tree_level(self, level, auto_transfer=True):\n        print('performing div tree level change (%d -> %d/%d) \\n'%(self.current_level, level, self.max_level))\n        self.current_level = level\n        self.current_level_floating = level\n        assert len(self.div_tree) > self.current_level, ('Reach max level already!')\n        if not auto_transfer: return\n        transfer_list = []\n        for i in range(self.n_agent):\n            previous_net_index = self.div_tree[self.current_level-1, i]\n            post_net_index = self.div_tree[self.current_level, i]\n            if post_net_index!=previous_net_index:\n                transfer = (previous_net_index, post_net_index)\n                if transfer not in transfer_list:\n                    transfer_list.append(transfer)\n        for transfer in transfer_list:\n            from_which_net = transfer[0]\n            to_which_net = transfer[1]\n            self.nets[to_which_net].load_state_dict(self.nets[from_which_net].state_dict())\n            print('transfering model parameters from %d-th net to %d-th net'%(from_which_net, to_which_net))\n        return \n\n    def forward(self, x_in, agent_ids):  # x0: shape = (?,...,?, n_agent, core_dim)\n        if self.current_level == 0:\n            x0 = add_onehot_id_at_last_dim_fixlen(x_in, fixlen=self.n_agent, agent_ids=agent_ids)\n            x2 = self.nets[0](x0)\n            return x2, None\n        else:\n            x0 = add_onehot_id_at_last_dim_fixlen(x_in, fixlen=self.n_agent, agent_ids=agent_ids)\n            res = []\n            for i in range(self.n_agent):\n                use_which_net = self.div_tree[self.current_level, i]\n                res.append(self.nets[use_which_net](x0[..., i, :]))\n            x2 = torch.stack(res, -2)\n            # x22 = self.nets[0](x1)\n            \n            return x2, None\n\n    # def forward_try_parallel(self, x0):  # x0: shape = (?,...,?, n_agent, core_dim)\n    #     x1 = self.shared_net(x0)\n    #     stream = []\n    #     res = []\n    #     for i in range(self.n_agent):\n    #         stream.append(torch.cuda.Stream())\n        \n    #     torch.cuda.synchronize()\n    #     for i in range(self.n_agent):\n    #         use_which_net = self.div_tree[self.current_level, i]\n    #         with torch.cuda.stream(stream[i]):\n    #             res.append(self.nets[use_which_net](x1[..., i, :]))\n    #             print(res[i])\n\n    #     # s1 = torch.cuda.Stream()\n    #     # s2 = torch.cuda.Stream()\n    #     # # Wait for the above tensors to initialise.\n    #     # torch.cuda.synchronize()\n    #     # with torch.cuda.stream(s1):\n    #     #     C = torch.mm(A, A)\n    #     # with torch.cuda.stream(s2):\n    #     #     D = torch.mm(B, B)\n    #     # Wait for C and D to be computed.\n    #     torch.cuda.synchronize()\n    #     # Do stuff with C and D.\n\n    #     x2 = torch.stack(res, -2)\n\n    #     return x2\n\n\n\ndef _2div(arr):\n    arr_res = arr.copy()\n    arr_pieces = []\n    pa = 0\n    st = 0\n    needdivcnt = 0\n    for i, a in enumerate(arr):\n        if a!=pa:\n            arr_pieces.append([st, i])\n            if (i-st)!=1: needdivcnt+=1\n            pa = a\n            st = i\n\n    arr_pieces.append([st, len(arr)])\n    if (len(arr)-st)!=1: needdivcnt+=1\n\n    offset = range(len(arr_pieces), len(arr_pieces)+needdivcnt)\n    p=0\n    for arr_p in arr_pieces:\n        length = arr_p[1] - arr_p[0]\n        if length == 1: continue\n        half_len = int(np.ceil(length / 2))\n        for j in range(arr_p[0]+half_len, arr_p[1]):\n            try:\n                arr_res[j] = offset[p]\n            except:\n                print('wtf')\n        p+=1\n    return arr_res\n\ndef get_division_tree(n_agents):\n    agent2divitreeindex = np.arange(n_agents)\n    np.random.shuffle(agent2divitreeindex)\n    max_div = np.ceil(np.log2(n_agents)).astype(int)\n    levels = np.zeros(shape=(max_div+1, n_agents), dtype=int)\n    tree_of_agent = []*(max_div+1)\n    for ith, level in enumerate(levels):\n        if ith == 0: continue\n        res = _2div(levels[ith-1,:])\n        levels[ith,:] = res\n    res_levels = levels.copy()\n    for i, div_tree_index in enumerate(agent2divitreeindex):\n        res_levels[:, i] = levels[:, div_tree_index]\n    return res_levels"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/foundation.py",
    "content": "import os, time, torch, traceback, shutil, pickle, io\nimport numpy as np\nfrom UTIL.colorful import *\nfrom config import GlobalConfig\nfrom UTIL.tensor_ops import repeat_at, _2tensor\nfrom ALGORITHM.common.rl_alg_base import RLAlgorithmBase\nclass AlgorithmConfig:\n    '''\n        AlgorithmConfig: This config class will be 'injected' with new settings from json.\n        (E.g., override configs with ```python main.py --cfg example.jsonc```)\n        (please see UTIL.config_args to find out how this advanced trick works out.)\n    '''\n    # configuration, open to jsonc modification\n    gamma = 0.99\n    tau = 0.95\n    train_traj_needed = 512\n    hete_n_alive_frontend = 1\n    TakeRewardAsUnity = False\n    use_normalization = True\n    wait_norm_stable = True\n    add_prob_loss = False\n    n_focus_on = 2\n    n_entity_placeholder = 11\n\n    load_checkpoint = False\n    load_specific_checkpoint = ''\n\n    # PPO part\n    clip_param = 0.2\n    ppo_epoch = 16\n    n_pieces_batch_division = 1\n    value_loss_coef = 0.1\n    entropy_coef = 0.05\n    max_grad_norm = 0.5\n    clip_param = 0.2\n    lr = 1e-4\n\n    # prevent GPU OOM\n    prevent_batchsize_oom = False\n    gamma_in_reward_forwarding = False\n    gamma_in_reward_forwarding_value = 0.99\n\n    net_hdim = 24\n    \n    dual_conc = True\n\n    n_agent = 'auto load, do not change'\n\n    ConfigOnTheFly = True\n\n\n    hete_n_net_placeholder = 5\n    hete_thread_align = False\n    hete_same_prob = 0.25\n    hete_lasted_n = 100\n    \n    policy_resonance = False\n\n    use_avail_act = True\n    \n    debug = False\n    ignore_test = False\n    \n    type_agent_diff_lr = False\n    hete_exclude_zero_wr = False\n    policy_matrix_testing = False\n    test_which_cpk = 1\n    type_sel_override = False\n    type_sel_override_list = []\n    allow_fast_test = True\n\ndef str_array_to_num(str_arr):\n    out_arr = []\n    buffer = {}\n    for str in str_arr:\n        if str not in buffer:\n            buffer[str] = len(buffer)\n        out_arr.append(buffer[str])\n    return out_arr\n\ndef itemgetter(*items):\n    # same with operator.itemgetter\n    def g(obj): return tuple(obj[item] if item in obj else None for item in items)\n    return g\nclass CPU_Unpickler(pickle.Unpickler):\n    def find_class(self, module, name):\n        if module == 'torch.storage' and name == '_load_from_bytes':\n            return lambda b: torch.load(io.BytesIO(b), map_location='cpu')\n        else:\n            return super().find_class(module, name)\nclass ReinforceAlgorithmFoundation(RLAlgorithmBase):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .shell_env import ShellEnvWrapper, ActionConvertLegacy\n        from .hete_net import HeteNet\n        super().__init__(n_agent, n_thread, space, mcv, team)\n        AlgorithmConfig.n_agent = n_agent\n        self.action_converter = ActionConvertLegacy(\n                SELF_TEAM_ASSUME=team, \n                OPP_TEAM_ASSUME=(1-team), \n                OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        n_actions = len(self.action_converter.dictionary_args)\n        # change obs format, e.g., converting dead agent obs into NaN\n        self.shell_env = ShellEnvWrapper(n_agent, n_thread, space, mcv, self, AlgorithmConfig, GlobalConfig.ScenarioConfig, self.team)\n        if self.ScenarioConfig.EntityOriented: rawob_dim = self.ScenarioConfig.obs_vec_length\n        else: rawob_dim = space['obs_space']['obs_shape']\n\n        # self.StagePlanner, for policy resonance\n        from .stage_planner import StagePlanner\n        self.stage_planner = StagePlanner(mcv=mcv)\n\n        # heterogeneous agent types\n        agent_type_list = [a['type'] for a in GlobalConfig.ScenarioConfig.SubTaskConfig.agent_list]\n        self.HeteAgentType = str_array_to_num(agent_type_list)\n        hete_type = np.array(self.HeteAgentType)[self.ScenarioConfig.AGENT_ID_EACH_TEAM[team]]\n        \n        # initialize policy\n        self.policy = HeteNet(rawob_dim=rawob_dim, n_action=n_actions, hete_type=hete_type, stage_planner=self.stage_planner)\n        self.policy = self.policy.to(self.device)\n\n        # initialize optimizer and trajectory (batch) manager\n        from .ppo import PPO\n        from .trajectory import BatchTrajManager\n        self.trainer = PPO(self.policy, ppo_config=AlgorithmConfig, mcv=mcv)\n        self.traj_manager = BatchTrajManager(\n            n_env=n_thread, traj_limit=int(GlobalConfig.ScenarioConfig.MaxEpisodeStep),\n            trainer_hook=self.trainer.train_on_traj)\n        self.stage_planner.trainer = self.trainer\n\n        # confirm that reward method is correct\n        self.check_reward_type(AlgorithmConfig)\n\n        # load checkpoints if needed\n        self.load_model(AlgorithmConfig)\n\n        # enable config_on_the_fly ability\n        if AlgorithmConfig.ConfigOnTheFly:\n            self._create_config_fly()\n\n        if AlgorithmConfig.policy_matrix_testing:\n            self.threads_test_reward_sum = np.zeros(shape=(n_thread,), dtype=float)\n            # self.threads_test_reward = []\n            self.recent_test_rewards = []\n            self.recent_test_wins = []\n            self._unfi_frag_matrix_ = None\n            self.recent_test_hete_gp_summary = []\n            self.current_hete_gp_summary = None\n            from VISUALIZE.mcom import mcom\n            self.mcv_matrix = mcom( \n                            path='%s/logger/matrix/'%GlobalConfig.logdir,\n                            image_path='%s/matrix.jpg'%GlobalConfig.logdir,\n                            draw_mode='Img',\n                            tag='[ppo.py]' )\n            self.mcv_matrix.rec_init(color='r')\n\n\n    def action_making(self, StateRecall, test_mode):\n        # make sure hook is cleared\n        assert ('_hook_' not in StateRecall)\n        \n        # read obs et.al.\n        obs, threads_active_flag, avail_act, hete_pick, hete_type, gp_sel_summary, eprsn = \\\n            itemgetter('obs', 'threads_active_flag', 'avail_act', '_hete_pick_', '_hete_type_', '_gp_pick_', '_EpRsn_')(StateRecall)\n            \n        \n        # make sure obs shape is correct\n        assert obs is not None, ('Make sure obs is ok')\n        assert len(obs) == sum(threads_active_flag), ('check batch size')\n        \n        # make sure avail_act is correct\n        if AlgorithmConfig.use_avail_act: assert avail_act is not None\n        \n        # policy resonance flag reshape\n        eprsn = repeat_at(eprsn, -1, self.n_agent)\n        thread_index = np.arange(self.n_thread)[threads_active_flag]\n        \n        # make decision\n        with torch.no_grad():\n            action, value, action_log_prob = self.policy.act(obs=obs,\n                                                             test_mode=test_mode,\n                                                             avail_act=avail_act,\n                                                             hete_pick=hete_pick,\n                                                             hete_type=hete_type,\n                                                             gp_sel_summary=gp_sel_summary,\n                                                             thread_index=thread_index,\n                                                             eprsn=eprsn,\n                                                             )\n\n        # commit obs to buffer, vars named like _x_ are aligned, others are not!\n        traj_framefrag = {\n            \"_SKIP_\":        ~threads_active_flag,\n            \"value\":         value,\n            \"hete_pick\":     hete_pick,\n            \"hete_type\":     hete_type,\n            \"gp_sel_summary\": gp_sel_summary,\n            \"avail_act\":     avail_act,\n            \"actionLogProb\": action_log_prob,\n            \"obs\":           obs,\n            \"action\":        action,\n        }\n        if avail_act is not None: traj_framefrag.update({'avail_act':  avail_act})\n        \n        # deal with rollout later when the reward is ready, leave a hook as a callback here\n        if not test_mode: \n            StateRecall['_hook_'] = self.commit_traj_frag(traj_framefrag, req_hook = True)\n        else:\n            if test_mode and AlgorithmConfig.policy_matrix_testing: \n                StateRecall['_hook_'] = self.matrix_callback_special(traj_framefrag)\n        return action.copy(), StateRecall\n\n\n\n    ''' \n        function to be called when reward is received\n    '''\n    def matrix_callback_special(self, framefrag):\n        assert self._unfi_frag_matrix_ is None\n        self._unfi_frag_matrix_ = framefrag\n        return self.matrix_callback_special_callback\n\n    def matrix_callback_special_callback(self, new_frag):\n        \n        fi_frag = self._unfi_frag_matrix_\n        self._unfi_frag_matrix_ = None\n\n        reward = new_frag['reward'].copy()\n        done = new_frag['done'].copy()\n        # self.threads_test_reward.append(reward)\n        self.threads_test_reward_sum += reward * ~fi_frag['_SKIP_']\n        \n        if not any(fi_frag['_SKIP_']):\n            self.current_hete_gp_summary = fi_frag[\"gp_sel_summary\"]\n\n        if done.all():\n            self.recent_test_rewards.extend(self.threads_test_reward_sum)\n            self.recent_test_wins.extend([q['team_ranking'][self.team]==0 for q in new_frag['info']]) # 0 means rank first\n            self.recent_test_hete_gp_summary.extend(self.current_hete_gp_summary)\n            self.threads_test_reward_sum *= 0\n            self.current_hete_gp_summary = None\n\n        return None\n\n\n    def interact_with_env(self, StateRecall):\n        '''\n            Interfacing with marl, standard method that you must implement\n            (redirect to shell_env to help with history rolling)\n        '''\n        return self.shell_env.interact_with_env(StateRecall)\n\n\n    def interact_with_env_genuine(self, StateRecall):\n        '''\n            When shell_env finish the preparation, interact_with_env_genuine is called\n            (Determine whether or not to do a training routinue)\n        '''\n        # if not StateRecall['Test-Flag']: self.train()  # when needed, train!\n        return self.action_making(StateRecall, StateRecall['Test-Flag'])\n\n    def train(self):\n        '''\n            Get event from hmp task runner, save model now!\n        '''\n        if self.traj_manager.can_exec_training():\n            if self.stage_planner.can_exec_trainning():\n                self.traj_manager.train_and_clear_traj_pool()\n            else:\n                self.traj_manager.clear_traj_pool()\n                \n            # read configuration\n            if AlgorithmConfig.ConfigOnTheFly: self._config_on_fly()\n            \n            # \n            self.stage_planner.update_plan()\n\n    # override parent function\n    def on_notify(self, message, **kargs):\n        win_rate = kargs['win_rate']\n        mean_reward = kargs['mean_reward']\n        \n        path = self.save_model(\n            update_cnt=self.traj_manager.update_cnt,\n            info=str(kargs)\n        )\n        # print('[random win rate] ! ! ! ! !')\n        # win_rate = np.random.rand()\n        self.policy.register_ckp(win_rate, path, mean_reward)\n        \n        if AlgorithmConfig.policy_matrix_testing:\n            from UTIL.data_struct import UniqueList\n            # self.recent_test_hete_gp_summary_str = self.recent_test_hete_gp_summary # [str(q.tolist()) for q in self.recent_test_hete_gp_summary]\n            recent_test_hete_gp_summary_ls = [q.tolist() for q in self.recent_test_hete_gp_summary]\n            ulist = UniqueList(recent_test_hete_gp_summary_ls)\n            for u in ulist:\n                feature = self.policy.ph_to_feature[u].squeeze().cpu().numpy().tolist()\n                feature = \"[%.2f,%.2f,%.2f]\"%tuple(feature)\n                mask = [u==uu  for uu in recent_test_hete_gp_summary_ls]\n                r = np.array(self.recent_test_rewards)[mask].mean()\n                wr = np.array(self.recent_test_wins)[mask].mean()\n\n                self.mcv_matrix.rec(self.policy.ckpg_input_cnt, 'time')\n                self.mcv_matrix.rec(r,  'r of=%s'%feature)\n                self.mcv_matrix.rec(wr, 'w of=%s'%feature)\n                self.mcv_matrix.rec(sum(mask), 'n of=%s'%feature)\n\n            self.mcv_matrix.rec_show()\n            \n            self.recent_test_rewards = []\n            self.recent_test_wins = []\n            self.recent_test_hete_gp_summary = []\n\n\n\n\n    def save_model(self, update_cnt, info=None):\n        '''\n            save model now!\n            save if triggered when:\n            1. Update_cnt = 50, 100, ...\n            2. Given info, indicating a hmp command\n            3. A flag file is detected, indicating a save command from human\n        '''\n        if not os.path.exists('%s/history_cpt/' % GlobalConfig.logdir): \n            os.makedirs('%s/history_cpt/' % GlobalConfig.logdir)\n\n        # dir 1\n        pt_path = '%s/model.pt' % GlobalConfig.logdir\n        print绿('saving model to %s' % pt_path)\n        torch.save({\n            'policy': self.policy.state_dict(),\n            'optimizer': self.trainer.optimizer.state_dict(),\n        }, pt_path)\n\n        # dir 2\n        info = str(update_cnt) if info is None else ''.join([str(update_cnt), '_', info])\n        pt_path2 = '%s/history_cpt/model_%s.pt' % (GlobalConfig.logdir, info)\n        shutil.copyfile(pt_path, pt_path2)\n\n        # save ckpg_info\n        with open('%s/history_cpt/ckpg_info.pkl'%GlobalConfig.logdir, 'wb') as f:pickle.dump((self.policy.ckpg_info, self.policy.ckpg_input_cnt, [(n.feature, n.static, n.ready_to_go) for n in self.policy._nets_flat_placeholder_]),f)\n        print绿('save_model fin')\n        return pt_path2\n\n    def find_ckp(self, feature):\n        import glob\n        list_ckp = glob.glob('%s/history_cpt/*.pt'%GlobalConfig.logdir)\n        ckp_dir = [ckp for ckp in list_ckp if str(feature[0]) in ckp][0]\n        cuda_n = 'cpu' if 'cpu' in self.device else self.device\n        cpt = torch.load(ckp_dir, map_location=cuda_n)\n        # get previous frontier network\n        return {k.replace('_nets_flat_placeholder_.0.',''):v for k, v in cpt['policy'].items() if '_nets_flat_placeholder_.0.' in k}\n\n\n    def load_model(self, AlgorithmConfig):\n        '''\n            load model now\n        '''\n\n        if AlgorithmConfig.load_checkpoint:\n            manual_dir = AlgorithmConfig.load_specific_checkpoint\n            ckpt_dir = '%s/model.pt' % GlobalConfig.logdir if manual_dir == '' else '%s/%s' % (GlobalConfig.logdir, manual_dir)\n            cuda_n = 'cpu' if 'cpu' in self.device else self.device\n            strict = True\n            if not platform.system()==\"Linux\": assert ':' not in ckpt_dir, ('Windows OS does not allow : in file name')\n            cpt = torch.load(ckpt_dir, map_location=cuda_n)\n            self.policy.load_state_dict(cpt['policy'], strict=strict)\n            # https://github.com/pytorch/pytorch/issues/3852\n            self.trainer.optimizer.load_state_dict(cpt['optimizer'])\n\n            print黄('loaded checkpoint:', ckpt_dir)\n            if os.path.exists('%s/history_cpt/ckpg_info.pkl'%GlobalConfig.logdir):\n                with open('%s/history_cpt/ckpg_info.pkl'%GlobalConfig.logdir, 'rb') as f:\n                    self.policy.ckpg_info, self.policy.ckpg_input_cnt, n_flags = CPU_Unpickler(f).load()\n                for (n, flags) in zip(self.policy._nets_flat_placeholder_, n_flags):\n                    n.feature = flags[0]\n                    n.static = flags[1]\n                    n.ready_to_go = flags[2]\n                    if n.feature!=1: \n                        n.load_state_dict(self.find_ckp(n.feature), strict=True)\n                self.policy.ph_to_feature = _2tensor(np.array([n.feature for n in self.policy._nets_flat_placeholder_]))\n                print黄('loaded ckpg_info')\n            else:\n                print('Warning, past policy missing !!')\n\n\n    def process_framedata(self, traj_framedata):\n        ''' \n            hook is called when reward and next moment observation is ready,\n            now feed them into trajectory manager.\n            Rollout Processor | 准备提交Rollout, 以下划线开头和结尾的键值需要对齐(self.n_thread, ...)\n            note that keys starting with _ must have shape (self.n_thread, ...), details see fn:mask_paused_env()\n        '''\n        # strip info, since it is not array\n        items_to_pop = ['info', 'Latest-Obs']\n        for k in items_to_pop:\n            if k in traj_framedata:\n                traj_framedata.pop(k)\n        # the agent-wise reward is supposed to be the same, so averge them\n        if self.ScenarioConfig.RewardAsUnity:\n            traj_framedata['reward'] = repeat_at(traj_framedata['reward'], insert_dim=-1, n_times=self.n_agent)\n        # change the name of done to be recognised (by trajectory manager)\n        traj_framedata['_DONE_'] = traj_framedata.pop('done')\n        traj_framedata['_TOBS_'] = traj_framedata.pop(\n            'Terminal-Obs-Echo') if 'Terminal-Obs-Echo' in traj_framedata else None\n        # mask out pause thread\n        traj_framedata = self.mask_paused_env(traj_framedata)\n        # put the frag into memory\n        self.traj_manager.feed_traj_framedata(traj_framedata)\n\n    def mask_paused_env(self, frag):\n        running = ~frag['_SKIP_']\n        if running.all():\n            return frag\n        for key in frag:\n            if not key.startswith('_') and hasattr(frag[key], '__len__') and len(frag[key]) == self.n_thread:\n                frag[key] = frag[key][running]\n        return frag\n\n\n    def _create_config_fly(self):\n        logdir = GlobalConfig.logdir\n        self.input_file_dir = '%s/cmd_io.txt' % logdir\n        if not os.path.exists(self.input_file_dir):\n            with open(self.input_file_dir, 'w+', encoding='utf8') as f: f.writelines([\"# Write cmd at next line: \", \"\"])\n\n    def _config_on_fly(self):\n        if not os.path.exists(self.input_file_dir): return\n\n        with open(self.input_file_dir, 'r', encoding='utf8') as f:\n            cmdlines = f.readlines()\n\n        cmdlines_writeback = []\n        any_change = False\n\n        for cmdline in cmdlines:\n            if cmdline.startswith('#') or cmdline==\"\\n\" or cmdline==\" \\n\":\n                cmdlines_writeback.append(cmdline)\n            else:\n                any_change = True\n                try:\n                    print亮绿('[foundation.py] ------- executing: %s ------'%cmdline)\n                    exec(cmdline)\n                    cmdlines_writeback.append('# [execute successfully]\\t'+cmdline)\n                except:\n                    print红(traceback.format_exc())\n                    cmdlines_writeback.append('# [execute failed]\\t'+cmdline)\n\n        if any_change:\n            with open(self.input_file_dir, 'w+', encoding='utf8') as f:\n                f.writelines(cmdlines_writeback)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/hete_assignment.py",
    "content": "import copy\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, __hash__, repeat_at, gather_righthand\nfrom .foundation import AlgorithmConfig\n\n\n\ndef random_group(random_select_fn, n_thread, hete_type, n_hete_types, n_group, selected_tps, testing):\n    n_agent = hete_type.shape[-1]\n    group_sel_arr = np.zeros(shape=(n_thread, n_agent), dtype=int)\n    gp_sel_summary = []\n    for i in range(n_thread):\n        group_assignment = np.array([ \n            random_select_fn(testing) \n            if type not in selected_tps[i] else 0\n            for type in range(n_hete_types)\n        ])\n        assert (group_assignment[selected_tps[i]]==0).all()\n        gp_sel_summary.append(copy.deepcopy(group_assignment))\n        for ht, group in enumerate(group_assignment):\n            mask = (hete_type == ht)\n            group_sel_arr[i,mask] = group\n    return group_sel_arr, np.stack(gp_sel_summary).astype(np.int64)\n\n\n\n\ndef select_nets_for_shellenv(n_types, policy, hete_type_list, n_thread, n_gp, testing):\n    if (not testing) or (AlgorithmConfig.policy_matrix_testing):\n        n_alive_frontend = AlgorithmConfig.hete_n_alive_frontend\n        tmp = np.arange(n_types)\n        # select types to use frontier\n        if not AlgorithmConfig.type_sel_override:\n            selected_types = np.stack([\n                np.random.choice(\n                    a=tmp,\n                    size=(n_alive_frontend),\n                    replace=False,\n                    p=None)\n                for _ in range(n_thread)\n            ])\n        else:\n            selected_types = np.stack([\n                AlgorithmConfig.type_sel_override_list\n                for _ in range(n_thread)\n            ]) \n    else:\n        # testing but not policy_matrix_testing: select all types to use frontier\n        selected_types = np.stack([np.arange(n_types) for _ in range(n_thread)])\n    \n    # generate a random group selection array\n    if not AlgorithmConfig.policy_matrix_testing:\n        random_select_fn = policy.random_select \n    else:\n        random_select_fn = policy.random_select_matrix_test\n\n    group_sel_arr, gp_sel_summary = random_group(\n        random_select_fn=random_select_fn, n_thread=n_thread, hete_type=hete_type_list, \n        n_hete_types=n_types, n_group=n_gp, selected_tps=selected_types, testing=testing)\n    # group to net index\n    n_tp = n_types\n    get_placeholder = lambda type, group: group*n_tp + type\n    hete_type_arr = repeat_at(hete_type_list, 0, n_thread)\n    selected_nets = get_placeholder(type=hete_type_arr, group=group_sel_arr)\n    \n    return selected_nets, gp_sel_summary\n\n\n\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/hete_net.py",
    "content": "import torch, math, copy, pickle\nimport numpy as np\nimport torch.nn as nn\nfrom config import GlobalConfig as cfg\nfrom torch.distributions.categorical import Categorical\nfrom UTIL.colorful import print亮绿\nfrom UTIL.tensor_ops import Args2tensor_Return2numpy, Args2tensor, __hashn__, cat_last_dim, __hash__, one_hot_with_nan, repeat_at, scatter_righthand, gather_righthand, _2cpu2numpy, my_view\nfrom .foundation import AlgorithmConfig\nfrom ALGORITHM.common.pca import pca\nfrom ALGORITHM.common.net_manifest import weights_init\nfrom .net import Net, NetCentralCritic\n\ndef popgetter(*items):\n    def g(obj): return tuple(obj.pop(item) if item in obj else None for item in items)\n    return g\n\nclass no_context():\n    def __enter__(self):\n        return None\n    def __exit__(self, exc_type, exc_value, traceback):\n        return False\n\ndef _count_list_type(x):\n    type_cnt = {}\n    for xx in x:\n        if xx not in type_cnt: type_cnt[xx] = 0\n        type_cnt[xx] += 1\n    return len(type_cnt)\n\n\ndef _create_tensor_ph_or_fill_(ref, pt, offset, *args):\n    n_threads, n_agents, mask = args\n    if pt[offset] is None: \n        pt[offset] = torch.zeros(size=(n_threads*n_agents, *ref.shape[2:]), device=ref.device, dtype=ref.dtype)\n    pt[offset][mask] = ref.squeeze(0)\n    \ndef _tensor_expand_thread_dim_v2_(ref, pt, offset, *args):\n    # undo dim collapse\n    n_threads, n_agents = args\n    v = pt[offset]\n    pt[offset] = v.view(n_threads, n_agents, *v.shape[1:])\n\n\n\ndef dfs_create_and_fn(ref, pt, offset, fn, *args):\n    '''\n        ref: target to sync\n        pt: mutable list\n        offset: mutable list index\n        fn: function to be executed at leaf nodes\n        args: anything needed\n    '''\n    if ref is None: # there is nothing to sync, instead, do something at leaf node only\n        ref = pt[offset]\n\n    if ref == 'vph':\n        pt[offset] = 'vph'\n        return\n    elif isinstance(ref, tuple) or isinstance(ref, list):\n        if pt[offset] is None: pt[offset] = [None for item in ref]\n        for i, item in enumerate(ref):\n            dfs_create_and_fn(item, pt[offset], i, fn, *args)\n    elif isinstance(ref, dict):\n        if pt[offset] is None: pt[offset] = {key:None for key in ref}\n        for key in ref:\n            dfs_create_and_fn(ref[key], pt[offset], key, fn, *args)\n    elif isinstance(ref, torch.Tensor):\n        fn(ref, pt, offset, *args)\n    else:\n        assert False\n\n\ndef _deal_single_in(x, mask_flatten):\n    if isinstance(x, torch.Tensor):\n        # collapse first two dims\n        return x.view(-1, *x.shape[2:])[mask_flatten].unsqueeze(0)\n    else:\n        return x\n\n# todo: https://pytorch.org/tutorials/advanced/torch-script-parallelism.html?highlight=parallel\ndef distribute_compute(fn_arr, mask_arr, **kwargs):\n    \"\"\"compute on each network\n\n    Args:\n        fn_arr : a list of forwarding networks\n        mask_arr : mask of kwargs\n\n    Returns:\n        tuple tensors: the result of networks\n    \"\"\"\n    # python don't have pointers, \n    # however, a list is a mutable type in python, that's what we need\n    g_out = [None]\n    \n    n_threads = mask_arr[0].shape[0]\n    n_agents = mask_arr[0].shape[1]\n    \n    # calculated result will be gathered into ret_tuple_gather\n    ret_tuple_gather = []\n    \n    # one by one we compute the result\n    for fn, mask in zip(fn_arr, mask_arr):\n        assert mask.dim()==2\n        mask_flatten = mask.flatten()\n        \n        agent_ids = torch.where(mask)[1]\n        agent_ids = agent_ids.unsqueeze(0) # fake an extral dimension\n        _kwargs = {key:_deal_single_in(kwargs[key], mask_flatten) for key in kwargs}\n        \n        with torch.no_grad() if fn.static else no_context() as gs:  # no_grad is already declared outside in act mode\n            ret_tuple = fn._act(agent_ids=agent_ids, **_kwargs)\n            ret_tuple_gather.append(ret_tuple)\n            \n    # stack ret_tuple_gather into g_out\n    for ret_tuple, fn, mask in zip(ret_tuple_gather, fn_arr, mask_arr):\n        mask_flatten = mask.flatten()\n        dfs_create_and_fn(ret_tuple, g_out, 0, _create_tensor_ph_or_fill_, n_threads, n_agents, mask_flatten)\n        \n    # reshape the tensor\n    dfs_create_and_fn(None, g_out, 0, _tensor_expand_thread_dim_v2_, n_threads, n_agents)\n    return tuple(g_out[0])\n\n\n\nclass HeteNet(nn.Module):\n    def __init__(self, rawob_dim, n_action, hete_type, **kwargs):\n        super().__init__()\n        self.rawob_dim = rawob_dim\n        self.n_action = n_action\n        self.hete_type = hete_type\n        self.n_hete_types = _count_list_type(self.hete_type)\n        self.hete_n_net_placeholder = AlgorithmConfig.hete_n_net_placeholder\n        self.use_normalization = AlgorithmConfig.use_normalization\n\n        self.n_tp = self.n_hete_types\n        self.n_gp = self.hete_n_net_placeholder\n        self.n_agent_each_tp = [sum(self.hete_type==i) for i in range(self.n_hete_types)]\n        self.n_agents = len(self.hete_type)\n        # convertion between placeholder index and type-group index\n        self.tpgp_2_ph = lambda type, group: group*self.n_tp + type\n        self.ph_2_tpgp = lambda ph: (ph%self.n_hete_types, ph//self.n_hete_types)\n        self.ph_2_gp = lambda ph: ph//self.n_hete_types\n\n        # initialize net placeholders\n        self._nets_flat_placeholder_ = torch.nn.ModuleList(modules=[\n            Net(rawob_dim, n_action, **kwargs) for _ in range(\n                self.n_gp\n            )\n        ])\n        # initialize critic\n        self._critic_central = NetCentralCritic(rawob_dim, n_action, **kwargs)\n        # reshape the handle of networks\n        self.nets = [  [ self._nets_flat_placeholder_[gp]  ] for gp in range(self.n_gp)]\n        # the frontier nets\n        self.frontend_nets = self.nets[0]\n        # the static nets\n        self.static_nets = self.nets[1:]\n        # heterogeneous feature dimension\n        self.hete_feature_dim = 1\n        # add flags to each nets\n        for gp, n_arr in enumerate(self.nets): \n            for _, n in enumerate(n_arr):\n                ph_index = gp\n                n.gp = gp\n                # n.lr_div = self.n_agent_each_tp[tp] / self.n_agents\n                if gp!=0: \n                    # lock static nets: the static nets are not loaded yet\n                    n.feature = np.zeros(self.hete_feature_dim)\n                    n.ready_to_go = False\n                    self.lock_net(ph_index)\n                else:\n                    # unlock frontier nets: the frontier nets are ready\n                    n.feature = np.ones(self.hete_feature_dim)\n                    n.ready_to_go = True\n                    self.unlock_net(ph_index)\n                    \n        # a list to trace the vital checkpoints\n        self.ckpg_info = []\n        # track the number of checkpoints commited\n        self.ckpg_input_cnt = 0\n        # feature array, arranged according to placeholders\n        self.ph_to_feature = torch.tensor(np.array([n.feature for n in self._nets_flat_placeholder_]), dtype=torch.float, device=cfg.device)\n        # \n        # from UTIL.sync_exp import SynWorker\n        # self.syn_worker = SynWorker('follow')\n\n    def lock_net(self, i):\n        n = self._nets_flat_placeholder_[i]\n        n.static = True\n        n.eval()\n\n    def unlock_net(self, i):\n        n = self._nets_flat_placeholder_[i]\n        n.static = False\n        n.train()\n\n    def register_ckp(self, win_rate, cpk_path, mean_reward):\n        # deal with new checkpoint\n        self.ckpg_input_cnt += 1\n        # get previous win rates\n        prev_win_rate = [self.ckpg_info[i]['win_rate'] for i in range(len(self.ckpg_info))]\n        # if the winrate is not a breakthough, give up\n        if len(prev_win_rate)>0 and win_rate <= max(prev_win_rate): \n            return\n        if AlgorithmConfig.hete_exclude_zero_wr and win_rate==0:\n            return\n        \n        # list the infomation about this checkpoint\n        self.ckpg_info.append({\n            'win_rate': win_rate, \n            'mean_reward': mean_reward,\n            'ckpg_cnt': self.ckpg_input_cnt,\n            'cpk_path': cpk_path,\n            'model': copy.deepcopy(self.frontend_nets[0].state_dict()),\n            'feature': [\n                win_rate\n            ],\n        })\n        \n        # sort according to win rate\n        self.ckpg_info.sort(key=lambda x:x['win_rate'])\n        # remove a checkpoint that is too close to its neighbor\n        self.trim_ckp()\n        \n        print('ckp register change!')\n        print([self.ckpg_info[i]['win_rate'] for i in range(len(self.ckpg_info))])\n        print([self.ckpg_info[i]['ckpg_cnt'] for i in range(len(self.ckpg_info))])\n        \n        # reload parameters\n        for i, static_nets in enumerate(self.static_nets):\n            # some net cannot be loaded with parameters yet, because ckpg_info has not collect enough samples\n            if i >= len(self.ckpg_info): continue\n            for _, net in enumerate(static_nets):\n                # load parameters\n                net.load_state_dict(self.ckpg_info[i]['model'], strict=True)\n                # the net must be static\n                assert net.static\n                # now the net is ready\n                net.ready_to_go = True\n                net.feature = self.ckpg_info[i]['feature']\n\n        # reload the net features\n        self.ph_to_feature = torch.tensor(np.array([n.feature for n in self._nets_flat_placeholder_]), dtype=torch.float, device=cfg.device)\n        \n\n\n        print('parameters reloaded')\n\n    def random_select(self, testing, *args, **kwargs):\n        \"\"\"randomly select a group index\n\n        Args:\n            AlgorithmConfig.hete_same_prob: a probability about choosing the frontier net as the teammate\n\n        Returns:\n            int: a group index\n        \"\"\"\n        assert not testing\n        if np.random.rand() < AlgorithmConfig.hete_same_prob:\n            return 0\n \n        # choose randomly among existing nets\n        n_option = len(self.ckpg_info)\n        if n_option > 0:\n            if n_option > AlgorithmConfig.hete_lasted_n:\n                assert AlgorithmConfig.hete_lasted_n != 0\n                rand_sel = np.random.randint(low=n_option+1-AlgorithmConfig.hete_lasted_n, high=n_option+1)\n            else:\n                rand_sel = np.random.randint(low=1, high=n_option+1)\n            return rand_sel\n        else:\n            return 0\n\n    if AlgorithmConfig.policy_matrix_testing: \n        def random_select_matrix_test(self, testing, *args, **kwargs):\n    \n            if testing:\n                hete_frontier_prob = 0 # 1 / (AlgorithmConfig.hete_lasted_n+1)\n                # print('manual selection')\n                n_option = len(self.ckpg_info)\n                LAST = AlgorithmConfig.test_which_cpk\n                # return 0\n                return (n_option+1) - LAST\n            else:\n                hete_frontier_prob = AlgorithmConfig.hete_same_prob\n\n            if np.random.rand() < hete_frontier_prob:\n                return 0\n                \n            # choose randomly among existing nets\n            n_option = len(self.ckpg_info)\n            if n_option > 0:\n                if AlgorithmConfig.hete_lasted_n == 0:\n                    return 0\n                if n_option > AlgorithmConfig.hete_lasted_n:\n                    assert AlgorithmConfig.hete_lasted_n != 0\n                    rand_sel = np.random.randint(low=n_option+1-AlgorithmConfig.hete_lasted_n, high=n_option+1)\n                else:\n                    rand_sel = np.random.randint(low=1, high=n_option+1)\n                return rand_sel\n            else:\n                return 0\n\n\n\n\n    # called after training update\n    def on_update(self, update_cnt):\n        return\n\n    def redirect_to_frontend(self, i):\n        return i%self.n_tp\n\n    def acquire_net(self, i):\n        tp, gp = self.ph_2_tpgp(i)\n        return self._nets_flat_placeholder_[gp]\n                \n    def exe(self, hete_pick=None, **kargs):\n        # shape\n        n_thread = hete_pick.shape[0]\n        n_agents = hete_pick.shape[1]\n        \n        # pop items from kargs\n        gp_sel_summary, thread_indices, hete_type = popgetter('gp_sel_summary', 'thread_index', 'hete_type')(kargs)\n        \n        # get ph_feature\n        # _012345 = torch.arange(self.n_tp, device=kargs['obs'].device, dtype=torch.int64)\n        ph_sel = gp_sel_summary # *self.n_tp + repeat_at(_012345, 0, n_thread)   # group * self.n_tp + tp\n        ph_feature = self.ph_to_feature[ph_sel]  # my_view(, [0, -1])\n        ph_feature_cp_raw = repeat_at(ph_feature, 1, n_agents)\n        agent2tp_onehot = torch.nn.functional.one_hot(hete_type.long(), num_classes=self.n_tp).unsqueeze(-1)\n\n        type_gp_mat = repeat_at(gp_sel_summary, -1, self.n_tp)\n        same_gp = (type_gp_mat == type_gp_mat.transpose(-1,-2)).long()\n        agent_self_type_mask2 = gather_righthand(same_gp,  index=hete_type, check=False).unsqueeze(-1)\n\n        assert ph_feature_cp_raw.dim() == 4\n\n        ph_feature_cp2 = (ph_feature_cp_raw*(1-agent_self_type_mask2) + agent_self_type_mask2)\n        ph_feature_cp_obs_ = torch.cat((ph_feature_cp2, agent2tp_onehot), 2)\n        ph_feature_cp_critic_ = torch.cat((ph_feature_cp_raw, agent2tp_onehot), 2)\n        ph_feature_cp_obs = my_view(ph_feature_cp_obs_, [0,0,-1]) # ph_feature_cp_obs.shape = torch.Size([n_thread=16, n_agents=10, core_dim=12])\n        ph_feature_cp_critic = my_view(ph_feature_cp_critic_, [0,0,-1]) # ph_feature_cp_obs.shape = torch.Size([n_thread=16, n_agents=10, core_dim=12])\n        \n        # add ph_feature to kwargs\n        kargs['obs_hfeature'] = ph_feature_cp_obs\n        # get a manifest of running nets\n        # invo_hete_types = [i for i in range(self.n_tp*self.n_gp) if (i in hete_pick)]\n        invo_gps = [i for i in range(self.n_gp) if (i in gp_sel_summary)]\n        running_nets = [self.nets[gp][0] for gp in invo_gps]\n        \n        # make sure all nets under testing is frontend / frontier\n        if 'test_mode' in kargs and kargs['test_mode']: \n            for net in running_nets: \n                if not AlgorithmConfig.policy_matrix_testing: assert not net.static\n\n\n        # run actor policy networks\n        actor_result = distribute_compute(\n            fn_arr = running_nets,\n            mask_arr = [(self.ph_2_gp(hete_pick) == gp) for gp in invo_gps],\n            **kargs\n        )\n\n\n        # run critic network\n        kargs.pop('obs_hfeature')   # replace h_feature\n        kargs['obs_hfeature_critic'] = ph_feature_cp_critic\n        critic_result = self._critic_central.estimate_state(**kargs)\n        \n        # combine actor_result and critic_result\n        actor_result = list(actor_result)\n        for i, item in enumerate(actor_result):\n            if item=='vph': actor_result[i] = critic_result\n        \n        # done !\n        return tuple(actor_result)\n\n\n    @Args2tensor_Return2numpy\n    def act(self, **kargs):\n        return self.exe(**kargs)\n\n    @Args2tensor\n    def evaluate_actions(self, **kargs):\n        return self.exe(**kargs, eval_mode=True)\n\n    def trim_ckp(self):\n        RemoveNew = True\n        max_static_gp = self.n_gp - 1\n        if len(self.ckpg_info) <= max_static_gp:\n            return\n        else:\n            assert len(self.ckpg_info) == max_static_gp+1\n            # find two ckp with nearest \n            winrate_list = np.array([self.ckpg_info[i]['win_rate'] for i in range(len(self.ckpg_info))])\n            winrate_list = np.abs(winrate_list[1:] - winrate_list[:-1])\n            index = np.argmin(winrate_list)\n            old_index = index\n            new_index = index + 1\n            if self.ckpg_info[new_index]['ckpg_cnt'] < self.ckpg_info[old_index]['ckpg_cnt']:\n                new_index, old_index = old_index, new_index\n            \n            if RemoveNew:\n                self.ckpg_info.pop(new_index)\n            else:\n                self.ckpg_info.pop(old_index)\n            assert len(self.ckpg_info) == max_static_gp\n                \n        pass\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/net.py",
    "content": "import torch, math, copy\nimport numpy as np\nimport torch.nn as nn\nfrom torch.distributions.categorical import Categorical\nfrom UTIL.colorful import print亮绿\nfrom UTIL.tensor_ops import Args2tensor_Return2numpy, Args2tensor, __hashn__, my_view\nfrom UTIL.tensor_ops import pt_inf\nfrom UTIL.exp_helper import changed\nfrom .ccategorical import CCategorical\nfrom .foundation import AlgorithmConfig\nfrom ALGORITHM.common.attention import SimpleAttention\nfrom ALGORITHM.common.norm import DynamicNormFix\nfrom ALGORITHM.common.net_manifest import weights_init\nfrom ALGORITHM.common.hyper_net import HyperNet\n\n\n\n\"\"\"\n    network initialize\n\"\"\"\nclass Net(nn.Module):\n    def __init__(self, rawob_dim, n_action, **kwargs):\n        super().__init__()\n        self.update_cnt = nn.Parameter(\n            torch.zeros(1, requires_grad=False, dtype=torch.long), requires_grad=False)\n        self.use_normalization = AlgorithmConfig.use_normalization\n        self.use_policy_resonance = AlgorithmConfig.policy_resonance\n        self.n_action = n_action\n        \n        \n        if self.use_policy_resonance:\n            self.ccategorical = CCategorical(kwargs['stage_planner'])\n            self.is_resonance_active = lambda: kwargs['stage_planner'].is_resonance_active()\n\n        h_dim = AlgorithmConfig.net_hdim\n\n        # observation normalization\n        if self.use_normalization:\n            self._batch_norm = DynamicNormFix(rawob_dim, only_for_last_dim=True, exclude_one_hot=True, exclude_nan=True)\n\n        n_entity = AlgorithmConfig.n_entity_placeholder\n        \n        # # # # # # # # # #  actor-critic share # # # # # # # # # # # #\n        self.obs_encoder = nn.Sequential(nn.Linear(rawob_dim, h_dim), nn.ReLU(inplace=True), nn.Linear(h_dim, h_dim))\n        self.attention_layer = SimpleAttention(h_dim=h_dim)\n        # # # # # # # # # #        actor        # # # # # # # # # # # #\n        _size = n_entity * h_dim\n        self.hyper_net = HyperNet(embed_dim=h_dim, hyper_input_dim=6, x_input_dim=_size)\n        self.policy_head = nn.Sequential(\n            nn.Linear(h_dim, h_dim), nn.ReLU(inplace=True),\n            nn.Linear(h_dim, self.n_action))\n\n        self.is_recurrent = False\n        self.apply(weights_init)\n        return\n\n    def act(self, *args, **kargs):\n        return self._act(*args, **kargs)\n\n    def evaluate_actions(self, *args, **kargs):\n        return self._act(*args, **kargs, eval_mode=True)\n\n    def _act(self, obs=None, test_mode=None, eval_mode=False, eval_actions=None, avail_act=None, agent_ids=None, eprsn=None, obs_hfeature=None):\n        assert (self.ready_to_go)\n        mask_dead = torch.isnan(obs).any(-1)    # find dead agents\n        \n        # if not (obs[..., -3+self.tp][~mask_dead] == -1).all().item():\n        #     assert False\n        \n        if self.static:\n            assert self.gp >=1\n            \n        # if not test_mode: assert not self.ready_to_go\n        eval_act = eval_actions if eval_mode else None\n        others = {}\n        if self.use_normalization:\n            if torch.isnan(obs).all(): pass\n            else: obs = self._batch_norm(obs, freeze=(eval_mode or test_mode or self.static))\n            obs_hfeature_norm = obs_hfeature\n\n        mask_dead = torch.isnan(obs).any(-1)\n        obs = torch.nan_to_num_(obs, 0)         # replace dead agents' obs, from NaN to 0\n        \n        # # # # # # # # # # actor-critic share # # # # # # # # # # # #\n        baec = self.obs_encoder(obs)\n        baec = self.attention_layer(k=baec,q=baec,v=baec, mask=mask_dead)\n\n        # # # # # # # # # # actor # # # # # # # # # # # #\n        at_bac = my_view(baec,[0,0,-1])\n        \n        at_bac_hn = self.hyper_net(at_bac, hyper_x=obs_hfeature_norm)\n        \n        logits = self.policy_head(at_bac_hn)\n        \n        # choose action selector\n        logit2act = self._logit2act_rsn if self.use_policy_resonance and self.is_resonance_active() else self._logit2act\n        \n        # apply action selector\n        act, actLogProbs, distEntropy, probs = logit2act( logits, \n                                                          eval_mode=eval_mode,\n                                                          greedy=(test_mode or self.static), \n                                                          eval_actions=eval_act, \n                                                          avail_act=avail_act,\n                                                          eprsn=eprsn )\n\n        if not eval_mode: return act, 'vph', actLogProbs\n        else:             return 'vph', actLogProbs, distEntropy, probs, others\n\n    def _logit2act_rsn(self, logits_agent_cluster, eval_mode, greedy, eval_actions=None, avail_act=None, eprsn=None):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = self.ccategorical.feed_logits(logits_agent_cluster)\n        \n        if not greedy:    act = self.ccategorical.sample(act_dist, eprsn) if not eval_mode else eval_actions\n        else:             act = torch.argmax(act_dist.probs, axis=2)\n        # the policy gradient loss will feedback from here\n        actLogProbs = self._get_act_log_probs(act_dist, act) \n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    def _logit2act(self, logits_agent_cluster, eval_mode, greedy, eval_actions=None, avail_act=None, **kwargs):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = Categorical(logits = logits_agent_cluster)\n        if not greedy:     act = act_dist.sample() if not eval_mode else eval_actions\n        else:              act = torch.argmax(act_dist.probs, axis=2)\n        actLogProbs = self._get_act_log_probs(act_dist, act) # the policy gradient loss will feedback from here\n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    @staticmethod\n    def _get_act_log_probs(distribution, action):\n        return distribution.log_prob(action.squeeze(-1)).unsqueeze(-1)\n\n\n\n\nclass NetCentralCritic(nn.Module):\n    def __init__(self, rawob_dim, n_action, **kwargs):\n        super().__init__()\n        self.update_cnt = nn.Parameter(\n            torch.zeros(1, requires_grad=False, dtype=torch.long), requires_grad=False)\n        self.use_normalization = AlgorithmConfig.use_normalization\n        self.use_policy_resonance = AlgorithmConfig.policy_resonance\n        self.n_action = n_action\n        \n        \n        if self.use_policy_resonance:\n            self.ccategorical = CCategorical(kwargs['stage_planner'])\n            self.is_resonance_active = lambda: kwargs['stage_planner'].is_resonance_active()\n\n        h_dim = AlgorithmConfig.net_hdim\n\n        # observation normalization\n        if self.use_normalization:\n            self._batch_norm = DynamicNormFix(rawob_dim, only_for_last_dim=True, exclude_one_hot=True, exclude_nan=True)\n\n        n_entity = AlgorithmConfig.n_entity_placeholder\n        \n        # # # # # # # # # #  actor-critic share # # # # # # # # # # # #\n        self.obs_encoder = nn.Sequential(nn.Linear(rawob_dim, h_dim), nn.ReLU(inplace=True), nn.Linear(h_dim, h_dim))\n        self.attention_layer = SimpleAttention(h_dim=h_dim)\n\n        # # # # # # # # # # critic # # # # # # # # # # # #\n        \n        _size = n_entity * h_dim\n        self.hyper_net = HyperNet(embed_dim=h_dim, hyper_input_dim=6, x_input_dim=_size)\n        \n        self.ct_encoder = nn.Sequential(nn.Linear(h_dim, h_dim), nn.ReLU(inplace=True), nn.Linear(h_dim, h_dim))\n        self.ct_attention_layer = SimpleAttention(h_dim=h_dim)\n        self.get_value = nn.Sequential(nn.Linear(h_dim, h_dim), nn.ReLU(inplace=True),nn.Linear(h_dim, 1))\n\n        self.is_recurrent = False\n        self.apply(weights_init)\n        return\n\n\n\n    def estimate_state(self, obs=None, test_mode=None, eval_mode=False, eval_actions=None, avail_act=None, agent_ids=None, eprsn=None, obs_hfeature_critic=None):\n        if self.use_normalization:\n            if torch.isnan(obs).all(): pass\n            else: obs = self._batch_norm(obs, freeze=(eval_mode or test_mode))\n            obs_hfeature_norm = obs_hfeature_critic\n\n        mask_dead = torch.isnan(obs).any(-1)\n        obs = torch.nan_to_num_(obs, 0)         # replace dead agents' obs, from NaN to 0\n        \n        # # # # # # # # # # actor-critic share # # # # # # # # # # # #\n        baec = self.obs_encoder(obs)\n        baec = self.attention_layer(k=baec,q=baec,v=baec, mask=mask_dead)\n\n        # # # # # # # # # # critic # # # # # # # # # # # #\n        ct_bac = my_view(baec,[0,0,-1])\n        ct_bac_hn = self.hyper_net(ct_bac, hyper_x=obs_hfeature_norm)\n        ct_bac = self.ct_encoder(ct_bac_hn)\n        ct_bac = self.ct_attention_layer(k=ct_bac,q=ct_bac,v=ct_bac)\n        value = self.get_value(ct_bac)\n        return value\n        \n\n\n\n    \n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/ppo.py",
    "content": "import torch, math, traceback\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport numpy as np\nfrom random import randint, sample\nfrom torch.utils.data.sampler import BatchSampler, SubsetRandomSampler\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import _2tensor, __hash__, __hashn__\nfrom config import GlobalConfig as cfg\nfrom UTIL.gpu_share import GpuShareUnit\nfrom .ppo_sampler import TrajPoolSampler\nfrom VISUALIZE.mcom import mcom\n\nclass PPO():\n    def __init__(self, policy_and_critic, ppo_config, mcv=None):\n        self.policy_and_critic = policy_and_critic\n        self.clip_param = ppo_config.clip_param\n        self.ppo_epoch = ppo_config.ppo_epoch\n        self.use_avail_act = ppo_config.ppo_epoch\n        self.n_pieces_batch_division = ppo_config.n_pieces_batch_division\n        self.value_loss_coef = ppo_config.value_loss_coef\n        self.entropy_coef = ppo_config.entropy_coef\n        self.max_grad_norm = ppo_config.max_grad_norm\n        self.add_prob_loss = ppo_config.add_prob_loss\n        self.prevent_batchsize_oom = ppo_config.prevent_batchsize_oom\n        # self.freeze_body = ppo_config.freeze_body\n        self.lr = ppo_config.lr\n        self.all_parameter = list(policy_and_critic.named_parameters())\n        self.parameter = [p for p_name, p in self.all_parameter]    # 535\n\n        # set learning rate differently?\n        if ppo_config.type_agent_diff_lr:\n            others_parameters = [v for k,v in self.all_parameter if '_nets_flat' not in k]\n            adam_lr_list = [\n                {'params': list(n.parameters()), 'lr':self.lr*n.lr_div} for n in policy_and_critic._nets_flat_placeholder_\n            ] + [{'params': list(policy_and_critic._critic_central.parameters()), 'lr':self.lr}]   # 33*3*5 + 40 = 535\n            assert sum([len(list(d['params'])) for d in adam_lr_list]) == len(self.all_parameter)\n            self.optimizer = optim.Adam(adam_lr_list, lr=self.lr)\n        else:\n            self.optimizer = optim.Adam(self.parameter, lr=self.lr)\n\n        self.g_update_delayer = 0\n        self.g_initial_value_loss = 0\n        \n        # 轮流训练式\n        self.mcv = mcv\n        self.ppo_update_cnt = 0\n        self.batch_size_reminder = True\n        self.trivial_dict = {}\n\n        assert self.n_pieces_batch_division == 1\n        self.gpu_share_unit = GpuShareUnit(cfg.device, gpu_party=cfg.gpu_party)\n\n        self.mcv2 = mcom( path='%s/logger/ppo/'%cfg.logdir,\n                          image_path='%s/detail_reward.jpg'%cfg.logdir,\n                          rapid_flush=True,\n                          draw_mode=cfg.draw_mode,\n                          tag='[ppo.py]' )\n        self.mcv2.rec_init(color='g')\n\n    def freeze_body(self):\n        self.freeze_body = True\n        self.at_parameter = [p for p_name, p in self.all_parameter if 'AT_policy_head' in p_name]\n        self.at_optimizer = optim.Adam(self.at_parameter, lr=self.lr)\n        self.ct_parameter = [p for p_name, p in self.all_parameter if 'CT_' in p_name]\n        self.ct_optimizer = optim.Adam(self.ct_parameter, lr=self.lr*10.0) #(self.lr)\n        print('change train object')\n\n    def train_on_traj(self, traj_pool, task):\n        while True:\n            try:\n                with self.gpu_share_unit:\n                    self.train_on_traj_(traj_pool, task) \n                break # 运行到这说明显存充足\n            except RuntimeError as err:\n                print(traceback.format_exc())\n                if self.prevent_batchsize_oom:\n                    # in some cases, reversing MaxSampleNum a single time is not enough\n                    if TrajPoolSampler.MaxSampleNum[-1] < 0: TrajPoolSampler.MaxSampleNum.pop(-1)\n                    assert TrajPoolSampler.MaxSampleNum[-1] > 0\n                    TrajPoolSampler.MaxSampleNum[-1] = -1\n                    print亮红('Insufficient gpu memory, using previous sample size !')\n                else:\n                    assert False\n            torch.cuda.empty_cache()\n\n    def log_reward_rich(self, traj_pool, mcv2):\n        tags = {}\n        for traj in traj_pool:\n            traj.reward_sum = sum(traj.reward[:,0])\n            gp_list = traj.gp_sel_summary[0] \n            if (gp_list==0).all():\n                tag = 'frontend'\n                if tag not in tags: tags[tag] = []\n                tags[tag].append(traj.reward_sum)\n            else:\n                gp = max(gp_list)\n                wr = self.policy_and_critic.ckpg_info[gp-1]['win_rate']\n                tp = np.argmax(gp_list)\n                tag = 'tp:%d wr:%.2f'%(tp, wr)\n                if tag not in tags: tags[tag] = []\n                tags[tag].append(traj.reward_sum)\n        tags = dict(sorted(tags.items()))\n        for k in tags:\n            mcv2.rec(np.array(tags[k]).mean(), k)\n        mcv2.rec_show()\n\n    def train_on_traj_(self, traj_pool, task):\n        self.log_reward_rich(traj_pool, self.mcv2)\n        ppo_valid_percent_list = []\n        sampler = TrajPoolSampler(n_div=1, traj_pool=traj_pool, flag=task, prevent_batchsize_oom=self.prevent_batchsize_oom, mcv=self.mcv)\n        # before_training_hash = [__hashn__(t.parameters()) for t in (self.policy_and_critic._nets_flat_placeholder_)]\n        for e in range(self.ppo_epoch):\n            sample_iter = sampler.reset_and_get_iter()\n            self.optimizer.zero_grad()\n            # ! get traj fragment\n            sample = next(sample_iter)\n            # ! build graph, then update network\n            loss_final, others = self.establish_pytorch_graph(task, sample, e)\n            loss_final = loss_final*0.5\n            if e==0: print('[PPO.py] Memory Allocated %.2f GB'%(torch.cuda.memory_allocated()/1073741824))\n            loss_final.backward()\n            # log\n            ppo_valid_percent_list.append(others.pop('PPO valid percent').item())\n            self.log_trivial(dictionary=others); others = None\n            nn.utils.clip_grad_norm_(self.parameter, self.max_grad_norm)\n            self.optimizer.step()\n            \n            if ppo_valid_percent_list[-1] < 0.70: \n                print亮黄('policy change too much, epoch terminate early'); break\n        pass # finish all epoch update\n\n        print亮黄(np.array(ppo_valid_percent_list))\n        self.log_trivial_finalize()\n        \n        net_updated = [any([p.grad is not None for p in t.parameters()]) for t in (self.policy_and_critic._nets_flat_placeholder_)]\n        self.optimizer.zero_grad(set_to_none=True)\n        self.ppo_update_cnt += 1\n        for updated, net in zip(net_updated, self.policy_and_critic._nets_flat_placeholder_):\n            if updated:\n                net.update_cnt.data[0] = self.ppo_update_cnt\n        self.policy_and_critic.on_update(self.ppo_update_cnt)\n        \n        torch.cuda.empty_cache()\n        return self.ppo_update_cnt\n\n    def freeze_body(self):\n        assert False, \"function forbidden\"\n        self.freeze_body = True\n        self.parameter_pv = [p_name for p_name, p in self.all_parameter if not any(p_name.startswith(kw)  for kw in ('obs_encoder', 'attention_layer'))]\n        self.parameter = [p for p_name, p in self.all_parameter if not any(p_name.startswith(kw)  for kw in ('obs_encoder', 'attention_layer'))]\n        self.optimizer = optim.Adam(self.parameter, lr=self.lr)\n        print('change train object')\n\n    def log_trivial(self, dictionary):\n        for key in dictionary:\n            if key not in self.trivial_dict: self.trivial_dict[key] = []\n            item = dictionary[key].item() if hasattr(dictionary[key], 'item') else dictionary[key]\n            self.trivial_dict[key].append(item)\n\n    def log_trivial_finalize(self, print=True):\n        for key in self.trivial_dict:\n            self.trivial_dict[key] = np.array(self.trivial_dict[key])\n        \n        print_buf = ['[ppo.py] ']\n        for key in self.trivial_dict:\n            self.trivial_dict[key] = self.trivial_dict[key].mean()\n            print_buf.append(' %s:%.3f, '%(key, self.trivial_dict[key]))\n            if self.mcv is not None:  self.mcv.rec(self.trivial_dict[key], key)\n        if print: print紫(''.join(print_buf))\n        if self.mcv is not None:\n            self.mcv.rec_show()\n        self.trivial_dict = {}\n\n\n    def establish_pytorch_graph(self, flag, sample, n):\n        obs = _2tensor(sample['obs'])\n        advantage = _2tensor(sample['advantage'])\n        action = _2tensor(sample['action'])\n        oldPi_actionLogProb = _2tensor(sample['actionLogProb'])\n        real_value = _2tensor(sample['return'])\n        hete_pick = _2tensor(sample['hete_pick'])\n        hete_type = _2tensor(sample['hete_type'])\n        gp_sel_summary = _2tensor(sample['gp_sel_summary'])\n        avail_act = _2tensor(sample['avail_act']) if 'avail_act' in sample else None\n\n        # batchsize = advantage.shape[0]#; print亮紫(batchsize)\n        batch_agent_size = advantage.shape[0]*advantage.shape[1]\n\n        assert flag == 'train'\n        newPi_value, newPi_actionLogProb, entropy, probs, others = \\\n            self.policy_and_critic.evaluate_actions(\n                obs=obs, \n                eval_actions=action, \n                test_mode=False, \n                avail_act=avail_act, \n                hete_pick=hete_pick,\n                hete_type=hete_type,\n                gp_sel_summary=gp_sel_summary)\n        entropy_loss = entropy.mean()\n\n\n        n_actions = probs.shape[-1]\n        if self.add_prob_loss: assert n_actions <= 15  # \n        penalty_prob_line = (1/n_actions)*0.12\n        probs_loss = (penalty_prob_line - torch.clamp(probs, min=0, max=penalty_prob_line)).mean()\n        if not self.add_prob_loss:\n            probs_loss = torch.zeros_like(probs_loss)\n\n        # dual clip ppo core\n        E = newPi_actionLogProb - oldPi_actionLogProb\n        E_clip = torch.zeros_like(E)\n        E_clip = torch.where(advantage > 0, torch.clamp(E, max=np.log(1.0+self.clip_param)), E_clip)\n        E_clip = torch.where(advantage < 0, torch.clamp(E, min=np.log(1.0-self.clip_param), max=np.log(5) ), E_clip)\n        ratio  = torch.exp(E_clip)\n        policy_loss = -(ratio*advantage).mean()\n\n        # add all loses\n        value_loss = 0.5 * F.mse_loss(real_value, newPi_value)\n\n\n        AT_net_loss = policy_loss - entropy_loss*self.entropy_coef # + probs_loss*20\n        CT_net_loss = value_loss * 1.0\n        # AE_new_loss = ae_loss * 1.0\n\n        loss_final =  AT_net_loss + CT_net_loss  # + AE_new_loss\n\n        ppo_valid_percent = ((E_clip == E).int().sum()/batch_agent_size)\n\n        nz_mask = real_value!=0\n        value_loss_abs = (real_value[nz_mask] - newPi_value[nz_mask]).abs().mean()\n        others = {\n            'Value loss Abs':           value_loss_abs,\n            'PPO valid percent':        ppo_valid_percent,\n            'CT_net_loss':              CT_net_loss,\n            'AT_net_loss':              AT_net_loss,\n        }\n\n\n        return loss_final, others\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/ppo_sampler.py",
    "content": "import torch, math, traceback\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport numpy as np\nfrom random import randint, sample\nfrom torch.utils.data.sampler import BatchSampler, SubsetRandomSampler\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import _2tensor, __hash__, repeat_at\nfrom config import GlobalConfig as cfg\nfrom UTIL.gpu_share import GpuShareUnit\nclass TrajPoolSampler():\n    def __init__(self, n_div, traj_pool, flag, prevent_batchsize_oom=False, mcv=None):\n        self.n_pieces_batch_division = n_div\n        self.prevent_batchsize_oom = prevent_batchsize_oom    \n        self.mcv = mcv\n        if self.prevent_batchsize_oom:\n            assert self.n_pieces_batch_division==1, ('?')\n\n        self.num_batch = None\n        self.container = {}\n        self.warned = False\n        assert flag=='train'\n        req_dict =        ['hete_type', 'gp_sel_summary', 'avail_act', 'obs', 'action', 'actionLogProb', 'return', 'reward', 'hete_pick', 'value']\n        req_dict_rename = ['hete_type', 'gp_sel_summary', 'avail_act', 'obs', 'action', 'actionLogProb', 'return', 'reward', 'hete_pick', 'state_value']\n        return_rename = \"return\"\n        value_rename =  \"state_value\"\n        advantage_rename = \"advantage\"\n        # replace 'obs' to 'obs > xxxx'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name):\n                real_key_list = [real_key for real_key in traj_pool[0].__dict__ if (key_name+'>' in real_key)]\n                assert len(real_key_list) > 0, ('check variable provided!', key,key_index)\n                for real_key in real_key_list:\n                    mainkey, subkey = real_key.split('>')\n                    req_dict.append(real_key)\n                    req_dict_rename.append(key_rename+'>'+subkey)\n        self.big_batch_size = -1  # vector should have same length, check it!\n        \n        # load traj into a 'container'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name): continue\n            set_item = np.concatenate([getattr(traj, key_name) for traj in traj_pool], axis=0)\n            if not (self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0)):\n                print('error')\n            assert self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0), (key,key_index)\n            self.big_batch_size = set_item.shape[0]\n            self.container[key_rename] = set_item    # 指针赋值\n\n        # normalize advantage inside the batch\n        self.container[advantage_rename] = self.container[return_rename] - self.container[value_rename]\n        self.container[advantage_rename] = ( self.container[advantage_rename] - self.container[advantage_rename].mean() ) / (self.container[advantage_rename].std() + 1e-5)\n        # size of minibatch for each agent\n        self.mini_batch_size = math.ceil(self.big_batch_size / self.n_pieces_batch_division)  \n\n    def __len__(self):\n        return self.n_pieces_batch_division\n\n    def determine_max_n_sample(self):\n        assert self.prevent_batchsize_oom\n        if not hasattr(TrajPoolSampler,'MaxSampleNum'):\n            # initialization\n            TrajPoolSampler.MaxSampleNum =  [int(self.big_batch_size*(i+1)/50) for i in range(50)]\n            max_n_sample = self.big_batch_size\n        elif TrajPoolSampler.MaxSampleNum[-1] > 0:  \n            # meaning that oom never happen, at least not yet\n            # only update when the batch size increases\n            if self.big_batch_size > TrajPoolSampler.MaxSampleNum[-1]: TrajPoolSampler.MaxSampleNum.append(self.big_batch_size)\n            max_n_sample = self.big_batch_size\n        else:\n            # meaning that oom already happened, choose TrajPoolSampler.MaxSampleNum[-2] to be the limit\n            assert TrajPoolSampler.MaxSampleNum[-2] > 0\n            max_n_sample = TrajPoolSampler.MaxSampleNum[-2]\n        return max_n_sample\n\n    def reset_and_get_iter(self):\n        if not self.prevent_batchsize_oom:\n            self.sampler = BatchSampler(SubsetRandomSampler(range(self.big_batch_size)), self.mini_batch_size, drop_last=False)\n        else:\n            max_n_sample = self.determine_max_n_sample()\n            n_sample = min(self.big_batch_size, max_n_sample)\n            if not hasattr(self,'reminded'):\n                self.reminded = True\n                drop_percent = (self.big_batch_size-n_sample)/self.big_batch_size*100\n                if self.mcv is not None:\n                    self.mcv.rec(drop_percent, 'drop percent')\n                if drop_percent > 20: \n                    print_ = print亮红\n                    print_('droping %.1f percent samples..'%(drop_percent))\n                    assert False, \"GPU OOM!\"\n                else:\n                    print_ = print\n                    print_('droping %.1f percent samples..'%(drop_percent))\n            self.sampler = BatchSampler(SubsetRandomSampler(range(n_sample)), n_sample, drop_last=False)\n\n        for indices in self.sampler:\n            selected = {}\n            for key in self.container:\n                selected[key] = self.container[key][indices]\n            for key in [key for key in selected if '>' in key]:\n                # 重新把子母键值组合成二重字典\n                mainkey, subkey = key.split('>')\n                if not mainkey in selected: selected[mainkey] = {}\n                selected[mainkey][subkey] = selected[key]\n                del selected[key]\n            yield selected\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/shell_env.py",
    "content": "import numpy as np\nfrom config import GlobalConfig\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__, repeat_at, gather_righthand\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom .foundation import AlgorithmConfig\nfrom .cython_func import roll_hisory\nfrom .hete_assignment import select_nets_for_shellenv\n\nclass ShellEnvConfig:\n    add_avail_act = False\n    \n    \nclass ActionConvertLegacy():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            ('N/A',         'N/A',              None, None, None, None, None, None),   # 0\n            ('Idle',        'DynamicGuard',     None, None, None, None, None, None),   # 1\n            ('Idle',        'StaticAlert',      None, None, None, None, None, None),   # 2\n            ('Idle',        'AsFarAsPossible',              None, None, None, None, None, None),   # 4\n            ('Idle',        'StayWhenTargetInRange',        None, None, None, None, None, None),   # 5\n            ('SpecificMoving',      'Dir+X',    None, None, None, None, None, None),   # 7\n            ('SpecificMoving',      'Dir+Y',    None, None, None, None, None, None),   # 8\n            ('SpecificMoving',      'Dir-X',    None, None, None, None, None, None),   # 9\n            ('SpecificMoving',      'Dir-Y',    None, None, None, None, None, None),   # 10\n        ] \n        for i in range(self.OPP_NUM_ASSUME):\n            self.dictionary_args.append( ('SpecificAttacking',   'N/A',      None, None, None, None, OPP_TEAM_ASSUME, i) )\n    \n    \n\n    def convert_act_arr(self, type, a):\n        if type == 'RLA_UAV_Support':\n            args = self.dictionary_args[a]\n            # override wrong actions\n            if args[0] == 'SpecificAttacking':\n                return encode_action_as_digits('N/A',         'N/A',              None, None, None, None, None, None)\n            # override incorrect actions\n            if args[0] == 'Idle':\n                return encode_action_as_digits('Idle',        'StaticAlert',      None, None, None, None, None, None)\n            return encode_action_as_digits(*args)\n        else:\n            return encode_action_as_digits(*self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        for i in range(n_act):\n            args = self.dictionary_args[i]\n            \n            # for all kind of agents\n            if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n            \n            if type == 'RLA_UAV_Support':\n                if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n                if args[0] == 'SpecificAttacking':  ret[i] = DISABLE\n                if args[0] == 'Idle':               ret[i] = DISABLE\n                if args[1] == 'StaticAlert':        ret[i] = ENABLE\n        return ret\n    \n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \ndef count_list_type(x):\n    type_cnt = {}\n    for xx in x:\n        if xx not in type_cnt: type_cnt[xx] = 0\n        type_cnt[xx] += 1\n    return len(type_cnt)\n\n \n\nclass ShellEnvWrapper(object):\n    def __init__(self, n_agent, n_thread, space, mcv, rl_functional, alg_config, ScenarioConfig, team):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.team = team\n        self.space = space\n        self.mcv = mcv\n        self.rl_functional = rl_functional\n        if GlobalConfig.ScenarioConfig.EntityOriented:\n            self.core_dim = GlobalConfig.ScenarioConfig.obs_vec_length\n        else:\n            self.core_dim = space['obs_space']['obs_shape']\n        self.n_entity_placeholder = alg_config.n_entity_placeholder\n\n        # whether to use avail_act to block forbiden actions\n        self.AvailActProvided = False\n        if hasattr(ScenarioConfig, 'AvailActProvided'):\n            self.AvailActProvided = ScenarioConfig.AvailActProvided \n        self.action_converter = ActionConvertLegacy(\n                SELF_TEAM_ASSUME=team, \n                OPP_TEAM_ASSUME=(1-team), \n                OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        # heterogeneous agent types\n        agent_type_list = [a['type'] for a in GlobalConfig.ScenarioConfig.SubTaskConfig.agent_list]\n        opp_type_list = [a['type'] for a in GlobalConfig.ScenarioConfig.SubTaskConfig.agent_list if a['team']!=self.team]\n        self_type_list = [a['type'] for a in GlobalConfig.ScenarioConfig.SubTaskConfig.agent_list if a['team']==self.team]\n        def str_array_to_num(str_arr):\n            out_arr = []\n            buffer = {}\n            for str in str_arr:\n                if str not in buffer:\n                    buffer[str] = len(buffer)\n                out_arr.append(buffer[str])\n            return out_arr  \n        \n        self.HeteAgentType = str_array_to_num(agent_type_list)\n        self.hete_type = np.array(self.HeteAgentType)[GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[team]]\n        self.n_hete_types = count_list_type(self.hete_type)\n        \n        # check parameters\n        assert self.n_agent == len(self_type_list)\n        self.action_converter.confirm_parameters_are_correct(team, self.n_agent, len(opp_type_list))\n        self.patience = 2000\n        self.epsiode_cnt = 0\n\n    def cold_start_warmup(self, StateRecall):\n        self.agent_uid = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n        self.agent_type = [agent_meta['type'] \n            for agent_meta in StateRecall['Latest-Team-Info'][0]['dataArr']\n            if agent_meta['uId'] in self.agent_uid]\n        if ShellEnvConfig.add_avail_act:\n            self.avail_act = np.stack(tuple(self.action_converter.get_tp_avail_act(tp) for tp in self.agent_type))\n            self.avail_act = repeat_at(self.avail_act, insert_dim=0, n_times=self.n_thread)\n\n\n    def interact_with_env(self, StateRecall):\n        # warm up at first execution\n        if not hasattr(self, 'agent_type'):\n            self.cold_start_warmup(StateRecall)\n            \n        # action init to: -1\n        act = np.zeros(shape=(self.n_thread, self.n_agent), dtype=np.int) - 1\n        \n        # read and reshape observation\n        obs = StateRecall['Latest-Obs']\n        obs = my_view(obs,[0, 0, -1, self.core_dim])\n        # mask out invalid observation with NaN\n        obs[(obs==0).all(-1)] = np.nan\n\n        # stopped env mask\n        P  =  StateRecall['ENV-PAUSE']\n        # running env mask \n        R  = ~P\n        # reset env mask\n        RST = StateRecall['Env-Suffered-Reset']\n        \n        # when needed, train!\n        if not StateRecall['Test-Flag']: self.rl_functional.train()\n        \n        # if true: just experienced full reset on all episode, this is the first step of all env threads\n        if RST.all(): \n            if AlgorithmConfig.allow_fast_test and GlobalConfig.test_only and (self.epsiode_cnt > GlobalConfig.report_reward_interval):\n                import sys\n                sys.exit(0)\n            self.epsiode_cnt += self.n_thread\n            # policy resonance\n            eprsn_yita = self.rl_functional.stage_planner.yita if AlgorithmConfig.policy_resonance else 0\n            EpRsn = np.random.rand(self.n_thread) < eprsn_yita\n            StateRecall['_EpRsn_'] = EpRsn\n            # heterogeneous agent identification\n            StateRecall['_hete_type_'] = repeat_at(self.hete_type, 0, self.n_thread)\n            # select static/frontier actor network\n            StateRecall['_hete_pick_'], StateRecall['_gp_pick_'] = select_nets_for_shellenv(\n                                        n_types=self.n_hete_types, \n                                        policy=self.rl_functional.policy,\n                                        hete_type_list=self.hete_type,\n                                        n_thread = self.n_thread,\n                                        n_gp=AlgorithmConfig.hete_n_net_placeholder,\n                                        testing=StateRecall['Test-Flag']\n                                    )\n            print([(t['win_rate'], t['ckpg_cnt']) for t in self.rl_functional.policy.ckpg_info])\n\n        # prepare observation for the real RL algorithm\n        I_StateRecall = {\n            'obs':obs[R], \n            'avail_act':self.avail_act[R],\n            'Test-Flag':StateRecall['Test-Flag'], \n            '_EpRsn_':StateRecall['_EpRsn_'][R],\n            '_hete_pick_':StateRecall['_hete_pick_'][R], \n            '_hete_type_':StateRecall['_hete_type_'][R],\n            '_gp_pick_':StateRecall['_gp_pick_'][R], \n            'threads_active_flag':R, \n            'Latest-Team-Info':StateRecall['Latest-Team-Info'][R],\n        }\n        # load available act to limit action space if possible\n        if self.AvailActProvided:\n            avail_act = np.array([info['avail-act'] for info in np.array(StateRecall['Latest-Team-Info'][R], dtype=object)])\n            I_StateRecall.update({'avail_act':avail_act})\n\n        # the real RL algorithm ! !\n        act_active, internal_recall = self.rl_functional.interact_with_env_genuine(I_StateRecall)\n\n        # get decision results\n        act[R] = act_active\n        \n        # confirm actions are valid (satisfy 'avail-act')\n        if ShellEnvConfig.add_avail_act and self.patience>0:\n            self.patience -= 1\n            assert (gather_righthand(self.avail_act, repeat_at(act, -1, 1), check=False)[R]==1).all()\n            \n        # translate action into ue4 tuple action\n        act_converted = np.array([[ self.action_converter.convert_act_arr(self.agent_type[agentid], act) for agentid, act in enumerate(th) ] for th in act])\n        \n        # swap thread(batch) axis and agent axis\n        actions_list = np.swapaxes(act_converted, 0, 1)\n\n        # register callback hook\n        if not StateRecall['Test-Flag']:\n            StateRecall['_hook_'] = internal_recall['_hook_']\n            assert StateRecall['_hook_'] is not None\n        else:\n            if AlgorithmConfig.policy_matrix_testing:\n                StateRecall['_hook_'] = internal_recall['_hook_']\n                assert StateRecall['_hook_'] is not None\n                \n\n        # all done\n        return actions_list, StateRecall \n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/stage_planner.py",
    "content": "import math\nfrom .foundation import AlgorithmConfig\nfrom UTIL.colorful import *\n\nclass PolicyRsnConfig:\n    resonance_start_at_update = 10\n    yita_min_prob = 0.15  #  should be >= (1/n_action)\n    yita_max = 0.75\n    yita_inc_per_update = 0.0075 # (increase to 0.75 in 500 updates)\n    freeze_critic = False\n    \n    yita_shift_method = '-sin'\n    yita_shift_cycle = 1000\n\n\nclass StagePlanner:\n    def __init__(self, mcv) -> None:\n        if AlgorithmConfig.policy_resonance:\n            self.resonance_active = False\n            self.yita = 0\n            self.yita_min_prob = PolicyRsnConfig.yita_min_prob\n        self.freeze_body = False\n        self.update_cnt = 0\n        self.mcv = mcv\n        self.trainer = None\n        if AlgorithmConfig.wait_norm_stable:\n            self.wait_norm_stable_cnt = 2\n        else:\n            self.wait_norm_stable_cnt = 0\n        return\n    \n    def is_resonance_active(self,):\n        return self.resonance_active\n    \n    def is_body_freeze(self,):\n        return self.freeze_body\n    \n    def get_yita(self):\n        return self.yita\n    \n    def get_yita_min_prob(self):\n        return PolicyRsnConfig.yita_min_prob\n    \n    def can_exec_trainning(self):\n        if self.wait_norm_stable_cnt > 0:\n            print亮绿('waiting initial normalization stable, skip training!')\n            self.wait_norm_stable_cnt -= 1\n            return False\n        else:\n            return True\n\n    def update_plan(self):\n        self.update_cnt += 1\n        if AlgorithmConfig.policy_resonance:\n            if self.resonance_active:\n                self.when_pr_active()\n            elif not self.resonance_active:\n                self.when_pr_inactive()\n        return\n    \n    def activate_pr(self):\n        self.resonance_active = True\n        self.freeze_body = True\n        if PolicyRsnConfig.freeze_critic:\n            self.trainer.freeze_body()\n\n    def when_pr_inactive(self):\n        assert not self.resonance_active\n        if PolicyRsnConfig.resonance_start_at_update >= 0:\n            # mean need to activate pr later\n            if self.update_cnt > PolicyRsnConfig.resonance_start_at_update:\n                # time is up, activate pr\n                self.activate_pr()\n        # log\n        pr = 1 if self.resonance_active else 0\n        self.mcv.rec(pr, 'resonance')\n        self.mcv.rec(self.yita, 'self.yita')\n\n    def when_pr_active(self):\n        assert self.resonance_active\n        self._update_yita()\n        # log\n        pr = 1 if self.resonance_active else 0\n        self.mcv.rec(pr, 'resonance')\n        self.mcv.rec(self.yita, 'self.yita')\n\n    def _update_yita(self):\n        '''\n            increase self.yita by @yita_inc_per_update per function call\n        '''\n        if PolicyRsnConfig.yita_shift_method == '-cos':\n            self.yita = PolicyRsnConfig.yita_max\n            t = -math.cos(2*math.pi/PolicyRsnConfig.yita_shift_cycle * self.update_cnt) * PolicyRsnConfig.yita_max\n            if t<=0:\n                self.yita = 0\n            else:\n                self.yita = t\n            print亮绿('yita update:', self.yita)\n\n        elif PolicyRsnConfig.yita_shift_method == '-sin':\n            self.yita = PolicyRsnConfig.yita_max\n            t = -math.sin(2*math.pi/PolicyRsnConfig.yita_shift_cycle * self.update_cnt) * PolicyRsnConfig.yita_max\n            if t<=0:\n                self.yita = 0\n            else:\n                self.yita = t\n            print亮绿('yita update:', self.yita)\n\n        elif PolicyRsnConfig.yita_shift_method == 'slow-inc':\n            self.yita += PolicyRsnConfig.yita_inc_per_update\n            if self.yita > PolicyRsnConfig.yita_max:\n                self.yita = PolicyRsnConfig.yita_max\n            print亮绿('yita update:', self.yita)\n        else:\n            assert False"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/hete_league_onenet_fix/trajectory.py",
    "content": "# cython: language_level=3\nfrom config import GlobalConfig\nimport numpy as np\nfrom numpy.core.numeric import indices\nfrom .foundation import AlgorithmConfig\nfrom ALGORITHM.common.traj import TRAJ_BASE\nimport copy\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import __hash__, my_view, np_one_hot, np_repeat_at, np_softmax, scatter_with_nan\n\nclass trajectory(TRAJ_BASE):\n\n    def __init__(self, traj_limit, env_id):\n        super().__init__(traj_limit, env_id)\n        self.reference_track_name = 'value'\n\n    def early_finalize(self):\n        assert not self.readonly_lock   # unfinished traj\n        self.need_reward_bootstrap = True\n\n    def set_terminal_obs(self, tobs):\n        self.tobs = copy.deepcopy(tobs)\n\n    def cut_tail(self):\n        # 删去多余的预留空间\n        super().cut_tail()\n        TJ = lambda key: getattr(self, key)\n        # 进一步地， 根据这个轨迹上的NaN，删除所有无效时间点\n        reference_track = getattr(self, self.reference_track_name)\n        if self.need_reward_bootstrap:\n            assert False, ('it should not go here if everything goes as expected')\n            # print('need_reward_bootstrap') 找到最后一个不是nan的位置\n            T = np.where(~np.isnan(reference_track.squeeze()))[0][-1]\n            self.boot_strap_value = {\n                'bootstrap_value':TJ('value').squeeze()[T].copy(), \n            }\n            assert not hasattr(self,'tobs')\n            self.set_terminal_obs(TJ('g_obs')[T].copy())\n            reference_track[T] = np.nan\n        # deprecated if nothing in it\n        p_invalid = np.isnan(my_view(reference_track, [0, -1])).any(axis=-1)\n        p_valid = ~p_invalid\n        if p_invalid.all(): #invalid traj\n            self.deprecated_flag = True\n            return\n        # adjust reward position\n        reward = TJ('reward')\n        for i in reversed(range(self.time_pointer)):\n            if p_invalid[i] and i != 0: # invalid, push reward forward\n                reward[i-1] += reward[i]; reward[i] = np.nan\n        setattr(self, 'reward', reward)\n        # clip NaN\n        for key in self.key_dict: setattr(self, key, TJ(key)[p_valid])\n        # all done\n        return\n\n    def reward_push_forward(self, dead_mask):\n        # self.new_reward = self.reward.copy()\n        if AlgorithmConfig.gamma_in_reward_forwarding:\n            gamma = AlgorithmConfig.gamma_in_reward_forwarding_value \n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i]*gamma, 0)      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n\n        else:\n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i], 0)      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n        return\n\n\n    # new finalize\n    def finalize(self):\n        self.readonly_lock = True\n        assert not self.deprecated_flag\n        TJ = lambda key: getattr(self, key) \n        assert not np.isnan(TJ('reward')).any()\n        # deadmask\n        tmp = np.isnan(my_view(self.obs, [0,0,-1]))\n        dead_mask = tmp.all(-1)\n        # if (True): # check if the mask is correct\n        #     dead_mask_self = np.isnan(my_view(self.obs, [0,0,-1])[:,:,0])\n        #     assert (dead_mask==dead_mask_self).all()\n        # dead_mask2 = tmp.any(-1)\n        # assert (dead_mask==dead_mask2).all()\n        self.reward_push_forward(dead_mask) # push terminal reward forward 38 42 54\n        threat = np.zeros(shape=dead_mask.shape) - 1\n        assert dead_mask.shape[0] == self.time_pointer\n        for i in reversed(range(self.time_pointer)):\n            # threat[:(i+1)] 不包含threat[(i+1)]\n            if i+1 < self.time_pointer:\n                threat[:(i+1)] += (~(dead_mask[i+1]&dead_mask[i])).astype(np.int)\n            elif i+1 == self.time_pointer:\n                threat[:] += (~dead_mask[i]).astype(np.int)\n\n        SAFE_LIMIT = 11\n        threat = np.clip(threat, -1, SAFE_LIMIT)\n        setattr(self, 'threat', np.expand_dims(threat, -1))\n\n        # ! Use GAE to calculate return\n        self.gae_finalize_return(reward_key='reward', value_key='value', new_return_name='return')\n        return\n\n    def gae_finalize_return(self, reward_key, value_key, new_return_name):\n        # ------- gae parameters -------\n        gamma = AlgorithmConfig.gamma \n        tau = AlgorithmConfig.tau\n        # ------- -------------- -------\n        rewards = getattr(self, reward_key)\n        value = getattr(self, value_key)\n        length = rewards.shape[0]\n        assert rewards.shape[0]==value.shape[0]\n        # if dimension not aligned\n        if rewards.ndim == value.ndim-1: rewards = np.expand_dims(rewards, -1)\n        # initalize two more tracks\n        setattr(self, new_return_name, np.zeros_like(value))\n        self.key_dict.append(new_return_name)\n\n        returns = getattr(self, new_return_name)\n        boot_strap = 0 if not self.need_reward_bootstrap else self.boot_strap_value['bootstrap_'+value_key]\n\n        for step in reversed(range(length)):\n            if step==(length-1): # 最后一帧\n                value_preds_delta = rewards[step] + gamma * boot_strap      - value[step]\n                gae = value_preds_delta\n            else:\n                value_preds_delta = rewards[step] + gamma * value[step + 1] - value[step]\n                gae = value_preds_delta + gamma * tau * gae\n            returns[step] = gae + value[step]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nclass TrajPoolManager(object):\n    def __init__(self):\n        self.cnt = 0\n\n    def absorb_finalize_pool(self, pool):\n        for traj_handle in pool:\n            traj_handle.cut_tail()\n        pool = list(filter(lambda traj: not traj.deprecated_flag, pool))\n        for traj_handle in pool: traj_handle.finalize()\n        self.cnt += 1\n        task = ['train']\n        return task, pool\n\n\n\n\n\n\n\n\n\n\n\n\n\n'''\n    轨迹池管理\n'''\n\nclass TrajManagerBase(object):\n    def __init__(self, n_env, traj_limit):\n        self.n_env = n_env\n        self.traj_limit = traj_limit\n        self.update_cnt = 0\n        self.traj_pool = []\n        self.registered_keys = []\n        self.live_trajs = [trajectory(self.traj_limit, env_id=i) for i in range(self.n_env)]\n        self.live_traj_frame = [0 for _ in range(self.n_env)]\n        self._traj_lock_buf = None\n        self.patience = 1000\n        pass\n    \n    def __check_integraty(self, traj_frag):\n        if self.patience < 0: \n            return # stop wasting time checking this\n        self.patience -= 1\n        for key in traj_frag:\n            if key not in self.registered_keys and (not key.startswith('_')):\n                self.registered_keys.append(key)\n        for key in self.registered_keys:\n            assert key in traj_frag, ('this key sometimes disappears from the traj_frag:', key)\n\n    def batch_update(self, traj_frag):\n        self.__check_integraty(traj_frag)\n        done = traj_frag['_DONE_']; traj_frag.pop('_DONE_') # done flag\n        skip = traj_frag['_SKIP_']; traj_frag.pop('_SKIP_') # skip/frozen flag\n        tobs = traj_frag['_TOBS_']; traj_frag.pop('_TOBS_') # terminal obs\n        # single bool to list bool\n        if isinstance(done, bool): done = [done for i in range(self.n_env)]\n        if isinstance(skip, bool): skip = [skip for i in range(self.n_env)]\n        n_active = sum(~skip)\n        # feed\n        cnt = 0\n        for env_i in range(self.n_env):\n            if skip[env_i]: continue\n            # otherwise\n            frag_index = cnt; cnt += 1\n            env_index = env_i\n            traj_handle = self.live_trajs[env_index]\n            for key in traj_frag:\n                self.traj_remember(traj_handle, key=key, content=traj_frag[key],frag_index=frag_index, n_active=n_active)\n            self.live_traj_frame[env_index] += 1\n            traj_handle.time_shift()\n            if done[env_i]:\n                assert tobs[env_i] is not None # get the final obs\n                traj_handle.set_terminal_obs(tobs[env_i])\n                self.traj_pool.append(traj_handle)\n                self.live_trajs[env_index] = trajectory(self.traj_limit, env_id=env_index)\n                self.live_traj_frame[env_index] = 0\n\n    def traj_remember(self, traj, key, content, frag_index, n_active):\n        if content is None: traj.remember(key, None)\n        elif isinstance(content, dict):\n            for sub_key in content: \n                self.traj_remember(traj, \"\".join((key , \">\" , sub_key)), content=content[sub_key], frag_index=frag_index, n_active=n_active)\n        else:\n            assert n_active == len(content), ('length error')\n            traj.remember(key, content[frag_index]) # *\n\n\nclass BatchTrajManager(TrajManagerBase):\n    def __init__(self, n_env, traj_limit, trainer_hook):\n        super().__init__(n_env, traj_limit)\n        self.trainer_hook = trainer_hook\n        self.traj_limit = traj_limit\n        self.train_traj_needed = AlgorithmConfig.train_traj_needed\n        self.pool_manager = TrajPoolManager()\n\n    def update(self, traj_frag, index):\n        assert traj_frag is not None\n        for j, env_i in enumerate(index):\n            traj_handle = self.live_trajs[env_i]\n            for key in traj_frag:\n                if traj_frag[key] is None:\n                    assert False, key\n                if isinstance(traj_frag[key], dict):  # 如果是二重字典，特殊处理\n                    for sub_key in traj_frag[key]:\n                        content = traj_frag[key][sub_key][j]\n                        traj_handle.remember(key + \">\" + sub_key, content)\n                else:\n                    content = traj_frag[key][j]\n                    traj_handle.remember(key, content)\n            self.live_traj_frame[env_i] += 1\n            traj_handle.time_shift()\n        return\n\n    # 函数入口\n    def feed_traj_framedata(self, traj_frag, require_hook=False):\n        # an unlock hook must be executed before new trajectory feed in\n        assert self._traj_lock_buf is None\n        if require_hook: \n            # the traj_frag is not intact, lock up traj_frag, wait for more\n            assert '_SKIP_' in traj_frag\n            assert '_DONE_' not in traj_frag\n            assert 'reward' not in traj_frag\n            self._traj_lock_buf = traj_frag\n            return self.unlock_fn\n        else:\n            assert '_DONE_' in traj_frag\n            assert '_SKIP_' in traj_frag\n            self.batch_update(traj_frag=traj_frag)\n            return\n\n        \n    def clear_traj_pool(self):\n        print('do update %d'%self.update_cnt)\n        _, self.traj_pool = self.pool_manager.absorb_finalize_pool(pool=self.traj_pool)\n        self.traj_pool = []\n        # self.update_cnt += 1\n        # assert ppo_update_cnt == self.update_cnt\n        return self.update_cnt\n\n        \n    def train_and_clear_traj_pool(self):\n        print('do update %d'%self.update_cnt)\n\n        current_task_l, self.traj_pool = self.pool_manager.absorb_finalize_pool(pool=self.traj_pool)\n        for current_task in current_task_l:\n            ppo_update_cnt = self.trainer_hook(self.traj_pool, current_task)\n\n        self.traj_pool = []\n        self.update_cnt += 1\n        # assert ppo_update_cnt == self.update_cnt\n        return self.update_cnt\n\n    def can_exec_training(self):\n        if len(self.traj_pool) >= self.train_traj_needed:  return True\n        else:  return False\n \n    def unlock_fn(self, traj_frag):\n        assert self._traj_lock_buf is not None\n        traj_frag.update(self._traj_lock_buf)\n        self._traj_lock_buf = None\n        assert '_DONE_' in traj_frag\n        assert '_SKIP_' in traj_frag\n        self.batch_update(traj_frag=traj_frag)\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/my_ai/foundation.py",
    "content": "import numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__\nfrom config import GlobalConfig\nfrom MISSION.uhmap.actionset import strActionToDigits, ActDigitLen\nclass AlgorithmConfig:\n    preserve = ''\n\n\n\nclass ReinforceAlgorithmFoundation(object):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n        active_thread_obs = obs[~P]\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        for env_index in range(self.n_thread):\n            for agent_index in range(self.n_agent):\n                if np.random.rand() < 0.5:\n                    color_index = np.random.randint(low=0, high=4)\n                    actions[env_index, agent_index] = strActionToDigits(f'ActionSetDemo::ChangeColor;{color_index}')\n                else:\n                    uid = 11 if agent_index % 2 == 0 else 10\n                    actions[env_index, agent_index] = strActionToDigits(f'ActionSetDemo::FireToWaterdrop;{uid}')\n\n\n        StateRecall['_hook_'] = None\n        return actions, StateRecall \n\n\n\n\nclass DiscreteRLFoundation(object):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.action_list = [\n            strActionToDigits('ActionSetDemo::ChangeColor;0'),\n            strActionToDigits('ActionSetDemo::ChangeColor;1'),\n            strActionToDigits('ActionSetDemo::ChangeColor;2'),\n            strActionToDigits('ActionSetDemo::ChangeColor;3'),\n            strActionToDigits('ActionSetDemo::FireToWaterdrop;10'),\n            strActionToDigits('ActionSetDemo::FireToWaterdrop;11'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=1.0 Y=0.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=1.0 Y=1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=0.0 Y=1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=-1.0 Y=1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=-1.0 Y=0.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=-1.0 Y=-1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=0.0 Y=-1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=1.0 Y=-1.0 Z=0.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=0.0 Y=0.0 Z=1.0'),\n            strActionToDigits('ActionSetDemo::MoveToDirection;X=0.0 Y=0.0 Z=-1.0'),\n        ]\n        self.how_many_actions = len(self.action_list)\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n        active_thread_obs = obs[~P]\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        for env_index in range(self.n_thread):\n            for agent_index in range(self.n_agent):\n                action_x = np.random.randint(low=0,high=self.how_many_actions)\n                actions[env_index, agent_index] = self.action_list[action_x]\n\n\n        StateRecall['_hook_'] = None\n        return actions, StateRecall \n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/ccategorical.py",
    "content": "from torch.distributions.categorical import Categorical\nimport torch\nfrom .foundation import AlgorithmConfig\nfrom UTIL.tensor_ops import repeat_at, _2tensor\nfrom torch.distributions import kl_divergence\nEPS = 1e-9\n# yita = p_hit = 0.14\n\ndef random_process(probs, rsn_flag):\n    yita = AlgorithmConfig.yita\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(-1, max_place, 1).bool()\n        pmax = probs[mask_max]\n        if rsn_flag:\n            assert max_place.shape[-1] == 1\n            return max_place.squeeze(-1)\n        else:\n            # forbit max prob being chosen, pmax = probs.max(axis=-1)\n            p_hat = pmax + (pmax-1)/(1/yita-1)\n            k = 1/(1-yita)\n            #!!! write\n            probs *= k  \n            #!!! write\n            probs[mask_max] = p_hat \n            # print(probs)\n            dist = Categorical(probs=probs)\n            samp = dist.sample()\n            assert samp.shape[-1] != 1\n            return samp\n\ndef random_process_allow_big_yita(probs, rsn_flag):\n    yita = AlgorithmConfig.yita\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(-1, max_place, 1).bool()\n        pmax = probs[mask_max].reshape(max_place.shape) #probs[max_place].clone()\n        if rsn_flag:\n            assert max_place.shape[-1] == 1\n            return max_place.squeeze(-1)\n        else:\n            # forbit max prob being chosen\n            # pmax = probs.max(axis=-1) #probs[max_place].clone()\n            yita_arr = torch.ones_like(pmax)*yita\n            yita_arr_clip = torch.minimum(pmax, yita_arr)\n            # p_hat = pmax + (pmax-1) / (1/yita_arr_clip-1) + 1e-10\n            p_hat = (pmax-yita_arr_clip)/(1-yita_arr_clip)\n            k = 1/(1-yita_arr_clip)\n            probs *= k\n            probs[mask_max] = p_hat.reshape(-1)\n\n            # print(probs)\n            dist = Categorical(probs=probs)\n            samp = dist.sample()\n            assert samp.shape[-1] != 1\n            return samp #.squeeze(-1)\n\n\n\ndef random_process_with_clamp3(probs, yita, yita_min_prob, rsn_flag):\n\n    with torch.no_grad():\n        max_place = probs.argmax(-1, keepdims=True)\n        mask_max = torch.zeros_like(probs).scatter_(dim=-1, index=max_place, value=1).bool()\n        pmax = probs[mask_max].reshape(max_place.shape)\n        # act max\n        assert max_place.shape[-1] == 1\n        act_max = max_place.squeeze(-1)\n        # act samp\n        yita_arr = torch.ones_like(pmax)*yita\n        # p_hat = pmax + (pmax-1) / (1/yita_arr_clip-1) + 1e-10\n        p_hat = (pmax-yita_arr)/((1-yita_arr)+EPS)\n        p_hat = p_hat.clamp(min=yita_min_prob)\n        k = (1-p_hat)/((1-pmax)+EPS)\n        probs *= k\n        probs[mask_max] = p_hat.reshape(-1)\n        dist = Categorical(probs=probs)\n        act_samp = dist.sample()\n        # assert act_samp.shape[-1] != 1\n        hit_e = _2tensor(rsn_flag)\n        return torch.where(hit_e, act_max, act_samp)\n\n\nclass CCategorical():\n    def __init__(self, planner):\n        self.planner = planner\n        \n        pass\n\n    def sample(self, dist, eprsn):\n        probs = dist.probs.clone()\n        return random_process_with_clamp3(probs, self.planner.yita, self.planner.yita_min_prob, eprsn)\n\n    def register_rsn(self, rsn_flag):\n        self.rsn_flag = rsn_flag\n\n    def feed_logits(self, logits):\n        try:\n            return Categorical(logits=logits)\n        except:\n            print('error')\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/cython_func.pyx",
    "content": "import numpy as np\ncimport numpy as np\ncimport cython\nfrom cython.parallel import prange\nnp.import_array()\nctypedef fused DTYPE_float:\n    np.float32_t\n    np.float64_t\nctypedef fused DTYPE_int64_t:\n    np.int64_t\n    np.int32_t  # to compat Windows\nctypedef np.uint8_t DTYPE_bool_t\n\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\n@cython.nonecheck(False)\ndef roll_hisory( DTYPE_float[:,:,:,:] obs_feed_new, \n                DTYPE_float[:,:,:,:] prev_obs_feed, \n                DTYPE_bool_t[:,:,:] valid_mask, \n                DTYPE_int64_t[:,:] N_valid, \n                DTYPE_float[:,:,:,:] next_his_pool):\n    # how many threads\n    cdef Py_ssize_t vmax = N_valid.shape[0]\n    # how many agents\n    cdef Py_ssize_t wmax = N_valid.shape[1]\n    # how many entity subjects (including self @0)\n    cdef Py_ssize_t max_obs_entity = obs_feed_new.shape[2]\n    cdef int n_v, th, a, t, k, pointer\n    for th in prange(vmax, nogil=True):\n        # for each thread range -> prange\n        for a in prange(wmax):\n            # for each agent\n            pointer = 0\n            # step 1 fill next_his_pool[0 ~ (nv-1)] with obs_feed_new[0 ~ max_obs_entity-1]\n            for k in range(max_obs_entity):\n                if valid_mask[th,a,k]:\n                    next_his_pool[th,a, pointer] = obs_feed_new[th,a, k]\n                    pointer = pointer + 1\n\n            # step 2 fill next_his_pool[nv ~ (max_obs_entity-1)] with prev_obs_feed[0 ~ (max_obs_entity-1-nv)]\n            n_v = N_valid[th,a]\n            for k in range(n_v, max_obs_entity):\n                next_his_pool[th,a, k] = prev_obs_feed[th,a, k-n_v]\n    return np.asarray(next_his_pool)\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/div_tree.py",
    "content": "import torch\nimport torch.nn as nn\nimport numpy as np\nfrom ALGORITHM.common.mlp import LinearFinal\nfrom UTIL.tensor_ops import add_onehot_id_at_last_dim, add_onehot_id_at_last_dim_fixlen, repeat_at, _2tensor, gather_righthand, scatter_righthand\n\n\n    \nclass DivTree(nn.Module): # merge by MLP version\n    def __init__(self, input_dim, h_dim, n_action):\n        super().__init__()\n\n        # to design a division tree, I need to get the total number of agents\n        from .foundation import AlgorithmConfig\n        self.n_agent = AlgorithmConfig.n_agent\n        self.div_tree = get_division_tree(self.n_agent)\n        self.n_level = len(self.div_tree)\n        self.max_level = len(self.div_tree) - 1\n        self.current_level = 0\n        self.init_level = AlgorithmConfig.div_tree_init_level\n        if self.init_level < 0:\n            self.init_level = self.max_level\n        self.current_level_floating = 0.0\n\n        get_net = lambda: nn.Sequential(\n            nn.Linear(h_dim+self.n_agent, h_dim), \n            nn.ReLU(inplace=True),\n            LinearFinal(h_dim, n_action)\n        )\n        # Note: this is NOT net defining for each agent\n        # Instead, all agents starts from self.nets[0]\n        self.nets = torch.nn.ModuleList(modules=[\n            get_net() for i in range(self.n_agent)  \n        ])\n\n    def set_to_init_level(self, auto_transfer=True):\n        if self.init_level!=self.current_level:\n            for i in range(self.current_level, self.init_level):\n                self.change_div_tree_level(i+1, auto_transfer)\n\n\n\n    def change_div_tree_level(self, level, auto_transfer=True):\n        print('performing div tree level change (%d -> %d/%d) \\n'%(self.current_level, level, self.max_level))\n        self.current_level = level\n        self.current_level_floating = level\n        assert len(self.div_tree) > self.current_level, ('Reach max level already!')\n        if not auto_transfer: return\n        transfer_list = []\n        for i in range(self.n_agent):\n            previous_net_index = self.div_tree[self.current_level-1, i]\n            post_net_index = self.div_tree[self.current_level, i]\n            if post_net_index!=previous_net_index:\n                transfer = (previous_net_index, post_net_index)\n                if transfer not in transfer_list:\n                    transfer_list.append(transfer)\n        for transfer in transfer_list:\n            from_which_net = transfer[0]\n            to_which_net = transfer[1]\n            self.nets[to_which_net].load_state_dict(self.nets[from_which_net].state_dict())\n            print('transfering model parameters from %d-th net to %d-th net'%(from_which_net, to_which_net))\n        return \n\n    def forward(self, x_in, agent_ids):  # x0: shape = (?,...,?, n_agent, core_dim)\n        if self.current_level == 0:\n            x0 = add_onehot_id_at_last_dim_fixlen(x_in, fixlen=self.n_agent, agent_ids=agent_ids)\n            x2 = self.nets[0](x0)\n            return x2, None\n        else:\n            x0 = add_onehot_id_at_last_dim_fixlen(x_in, fixlen=self.n_agent, agent_ids=agent_ids)\n            res = []\n            for i in range(self.n_agent):\n                use_which_net = self.div_tree[self.current_level, i]\n                res.append(self.nets[use_which_net](x0[..., i, :]))\n            x2 = torch.stack(res, -2)\n            # x22 = self.nets[0](x1)\n            \n            return x2, None\n\n    # def forward_try_parallel(self, x0):  # x0: shape = (?,...,?, n_agent, core_dim)\n    #     x1 = self.shared_net(x0)\n    #     stream = []\n    #     res = []\n    #     for i in range(self.n_agent):\n    #         stream.append(torch.cuda.Stream())\n        \n    #     torch.cuda.synchronize()\n    #     for i in range(self.n_agent):\n    #         use_which_net = self.div_tree[self.current_level, i]\n    #         with torch.cuda.stream(stream[i]):\n    #             res.append(self.nets[use_which_net](x1[..., i, :]))\n    #             print(res[i])\n\n    #     # s1 = torch.cuda.Stream()\n    #     # s2 = torch.cuda.Stream()\n    #     # # Wait for the above tensors to initialise.\n    #     # torch.cuda.synchronize()\n    #     # with torch.cuda.stream(s1):\n    #     #     C = torch.mm(A, A)\n    #     # with torch.cuda.stream(s2):\n    #     #     D = torch.mm(B, B)\n    #     # Wait for C and D to be computed.\n    #     torch.cuda.synchronize()\n    #     # Do stuff with C and D.\n\n    #     x2 = torch.stack(res, -2)\n\n    #     return x2\n\n\n\ndef _2div(arr):\n    arr_res = arr.copy()\n    arr_pieces = []\n    pa = 0\n    st = 0\n    needdivcnt = 0\n    for i, a in enumerate(arr):\n        if a!=pa:\n            arr_pieces.append([st, i])\n            if (i-st)!=1: needdivcnt+=1\n            pa = a\n            st = i\n\n    arr_pieces.append([st, len(arr)])\n    if (len(arr)-st)!=1: needdivcnt+=1\n\n    offset = range(len(arr_pieces), len(arr_pieces)+needdivcnt)\n    p=0\n    for arr_p in arr_pieces:\n        length = arr_p[1] - arr_p[0]\n        if length == 1: continue\n        half_len = int(np.ceil(length / 2))\n        for j in range(arr_p[0]+half_len, arr_p[1]):\n            try:\n                arr_res[j] = offset[p]\n            except:\n                print('wtf')\n        p+=1\n    return arr_res\n\ndef get_division_tree(n_agents):\n    agent2divitreeindex = np.arange(n_agents)\n    np.random.shuffle(agent2divitreeindex)\n    max_div = np.ceil(np.log2(n_agents)).astype(int)\n    levels = np.zeros(shape=(max_div+1, n_agents), dtype=int)\n    tree_of_agent = []*(max_div+1)\n    for ith, level in enumerate(levels):\n        if ith == 0: continue\n        res = _2div(levels[ith-1,:])\n        levels[ith,:] = res\n    res_levels = levels.copy()\n    for i, div_tree_index in enumerate(agent2divitreeindex):\n        res_levels[:, i] = levels[:, div_tree_index]\n    return res_levels"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/foundation.py",
    "content": "import os, time, torch, traceback, shutil\nimport numpy as np\nfrom UTIL.colorful import *\nfrom config import GlobalConfig\nfrom UTIL.tensor_ops import repeat_at\nfrom ALGORITHM.common.rl_alg_base import RLAlgorithmBase\nclass AlgorithmConfig:\n    '''\n        AlgorithmConfig: This config class will be 'injected' with new settings from json.\n        (E.g., override configs with ```python main.py --cfg example.jsonc```)\n        (please see UTIL.config_args to find out how this advanced trick works out.)\n    '''\n    # configuration, open to jsonc modification\n    gamma = 0.99\n    tau = 0.95\n    train_traj_needed = 512\n    TakeRewardAsUnity = False\n    use_normalization = True\n    add_prob_loss = False\n    n_entity_placeholder = 10\n    load_checkpoint = False\n    load_specific_checkpoint = ''\n\n    # PPO part\n    clip_param = 0.2\n    ppo_epoch = 16\n    n_pieces_batch_division = 1\n    value_loss_coef = 0.1\n    entropy_coef = 0.05\n    max_grad_norm = 0.5\n    clip_param = 0.2\n    lr = 1e-4\n\n    # sometimes the episode length gets longer,\n    # resulting in more samples and causing GPU OOM,\n    # prevent this by fixing the number of samples to initial\n    # by randomly sampling and droping\n    prevent_batchsize_oom = False\n    gamma_in_reward_forwarding = False\n    gamma_in_reward_forwarding_value = 0.99\n\n    net_hdim = 24\n    \n    dual_conc = True\n\n    n_entity_placeholder = 'auto load, do not change'\n    n_agent = 'auto load, do not change'\n    entity_distinct = 'auto load, do not change'\n\n    ConfigOnTheFly = True\n\n\n    \n\n    policy_resonance = False\n\n    use_avail_act = True\n    \n    debug = False\n    \ndef str_array_to_num(str_arr):\n    out_arr = []\n    buffer = {}\n    for str in str_arr:\n        if str not in buffer:\n            buffer[str] = len(buffer)\n        out_arr.append(buffer[str])\n    return out_arr\n\ndef itemgetter(*items):\n    # same with operator.itemgetter\n    def g(obj): return tuple(obj[item] if item in obj else None for item in items)\n    return g\n\nclass ReinforceAlgorithmFoundation(RLAlgorithmBase):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .shell_env import ShellEnvWrapper, ActionConvertLegacy\n        from .net import Net\n        super().__init__(n_agent, n_thread, space, mcv, team)\n        AlgorithmConfig.n_agent = n_agent\n\n        # change obs format, e.g., converting dead agent obs into NaN\n        self.shell_env = ShellEnvWrapper(n_agent, n_thread, space, mcv, self, AlgorithmConfig, GlobalConfig.ScenarioConfig, self.team)\n\n        n_actions = len(self.shell_env.action_converter.dictionary_args)\n\n        if self.ScenarioConfig.EntityOriented: \n            AlgorithmConfig.n_entity_placeholder = GlobalConfig.ScenarioConfig.obs_n_entity\n            rawob_dim = self.ScenarioConfig.obs_vec_length\n        else: rawob_dim = space['obs_space']['obs_shape']\n\n        # self.StagePlanner, for policy resonance\n        from .stage_planner import StagePlanner\n        self.stage_planner = StagePlanner(mcv=mcv)\n\n        # initialize policy\n        self.policy = Net(rawob_dim=rawob_dim, n_action=n_actions, stage_planner=self.stage_planner)\n        self.policy = self.policy.to(self.device)\n\n        # initialize optimizer and trajectory (batch) manager\n        from .ppo import PPO\n        from .trajectory import BatchTrajManager\n        self.trainer = PPO(self.policy, ppo_config=AlgorithmConfig, mcv=mcv)\n        self.traj_manager = BatchTrajManager(\n            n_env=n_thread, traj_limit=int(GlobalConfig.ScenarioConfig.MaxEpisodeStep),\n            trainer_hook=self.trainer.train_on_traj)\n        self.stage_planner.trainer = self.trainer\n\n        # confirm that reward method is correct\n        self.check_reward_type(AlgorithmConfig)\n\n        # load checkpoints if needed\n        self.load_model(AlgorithmConfig)\n\n        # enable config_on_the_fly ability\n        if AlgorithmConfig.ConfigOnTheFly:\n            self._create_config_fly()\n\n\n    def action_making(self, StateRecall, test_mode):\n        # make sure hook is cleared\n        assert ('_hook_' not in StateRecall)\n        \n        # read obs\n        obs, threads_active_flag, avail_act, eprsn = \\\n            itemgetter('obs', 'threads_active_flag', 'avail_act', '_EpRsn_')(StateRecall)\n            \n        \n        # make sure obs is right\n        assert obs is not None, ('Make sure obs is ok')\n        assert len(obs) == sum(threads_active_flag), ('check batch size')\n        # make sure avail_act is correct\n        if AlgorithmConfig.use_avail_act: assert avail_act is not None\n\n        # policy resonance flag reshape\n        eprsn = repeat_at(eprsn, -1, self.n_agent)\n        thread_index = np.arange(self.n_thread)[threads_active_flag]\n\n        # make decision\n        with torch.no_grad():\n            action, value, action_log_prob = self.policy.act(obs=obs,\n                                                             test_mode=test_mode,\n                                                             avail_act=avail_act,\n                                                             eprsn=eprsn,\n                                                             )\n\n        # commit obs to buffer, vars named like _x_ are aligned, others are not!\n        traj_framefrag = {\n            \"_SKIP_\":        ~threads_active_flag,\n            \"value\":         value,\n            \"avail_act\":     avail_act,\n            \"actionLogProb\": action_log_prob,\n            \"obs\":           obs,\n            \"action\":        action,\n        }\n        if avail_act is not None: traj_framefrag.update({'avail_act':  avail_act})\n        # deal with rollout later when the reward is ready, leave a hook as a callback here\n        if not test_mode: StateRecall['_hook_'] = self.commit_traj_frag(traj_framefrag, req_hook = True)\n        return action.copy(), StateRecall\n\n\n    def interact_with_env(self, StateRecall):\n        '''\n            Interfacing with marl, standard method that you must implement\n            (redirect to shell_env to help with history rolling)\n        '''\n        return self.shell_env.interact_with_env(StateRecall)\n\n\n    def interact_with_env_genuine(self, StateRecall):\n        '''\n            When shell_env finish the preparation, interact_with_env_genuine is called\n            (Determine whether or not to do a training routinue)\n        '''\n        # if not StateRecall['Test-Flag']: self.train()  # when needed, train!\n        return self.action_making(StateRecall, StateRecall['Test-Flag'])\n\n    def train(self):\n        '''\n            Get event from hmp task runner, save model now!\n        '''\n        if self.traj_manager.can_exec_training():\n            if self.stage_planner.can_exec_trainning():\n                self.traj_manager.train_and_clear_traj_pool()\n            else:\n                self.traj_manager.clear_traj_pool()\n                \n            # read configuration\n            if AlgorithmConfig.ConfigOnTheFly: self._config_on_fly()\n            \n            # \n            self.stage_planner.update_plan()\n\n\n\n    def save_model(self, update_cnt, info=None):\n        '''\n            save model now!\n            save if triggered when:\n            1. Update_cnt = 50, 100, ...\n            2. Given info, indicating a hmp command\n            3. A flag file is detected, indicating a save command from human\n        '''\n        if not os.path.exists('%s/history_cpt/' % GlobalConfig.logdir): \n            os.makedirs('%s/history_cpt/' % GlobalConfig.logdir)\n\n        # dir 1\n        pt_path = '%s/model.pt' % GlobalConfig.logdir\n        print绿('saving model to %s' % pt_path)\n        torch.save({\n            'policy': self.policy.state_dict(),\n            'optimizer': self.trainer.optimizer.state_dict(),\n        }, pt_path)\n\n        # dir 2\n        info = str(update_cnt) if info is None else ''.join([str(update_cnt), '_', info])\n        pt_path2 = '%s/history_cpt/model_%s.pt' % (GlobalConfig.logdir, info)\n        shutil.copyfile(pt_path, pt_path2)\n\n        print绿('save_model fin')\n\n\n\n    def load_model(self, AlgorithmConfig):\n        '''\n            load model now\n        '''\n\n        if AlgorithmConfig.load_checkpoint:\n            manual_dir = AlgorithmConfig.load_specific_checkpoint\n            ckpt_dir = '%s/model.pt' % GlobalConfig.logdir if manual_dir == '' else '%s/%s' % (GlobalConfig.logdir, manual_dir)\n            cuda_n = 'cpu' if 'cpu' in self.device else self.device\n            strict = True\n            \n            cpt = torch.load(ckpt_dir, map_location=cuda_n)\n            self.policy.load_state_dict(cpt['policy'], strict=strict)\n            # https://github.com/pytorch/pytorch/issues/3852\n            self.trainer.optimizer.load_state_dict(cpt['optimizer'])\n\n            print黄('loaded checkpoint:', ckpt_dir)\n\n\n    def process_framedata(self, traj_framedata):\n        ''' \n            hook is called when reward and next moment observation is ready,\n            now feed them into trajectory manager.\n            Rollout Processor | 准备提交Rollout, 以下划线开头和结尾的键值需要对齐(self.n_thread, ...)\n            note that keys starting with _ must have shape (self.n_thread, ...), details see fn:mask_paused_env()\n        '''\n        # strip info, since it is not array\n        items_to_pop = ['info', 'Latest-Obs']\n        for k in items_to_pop:\n            if k in traj_framedata:\n                traj_framedata.pop(k)\n        # the agent-wise reward is supposed to be the same, so averge them\n        if self.ScenarioConfig.RewardAsUnity:\n            traj_framedata['reward'] = repeat_at(traj_framedata['reward'], insert_dim=-1, n_times=self.n_agent)\n        # change the name of done to be recognised (by trajectory manager)\n        traj_framedata['_DONE_'] = traj_framedata.pop('done')\n        traj_framedata['_TOBS_'] = traj_framedata.pop(\n            'Terminal-Obs-Echo') if 'Terminal-Obs-Echo' in traj_framedata else None\n        # mask out pause thread\n        traj_framedata = self.mask_paused_env(traj_framedata)\n        # put the frag into memory\n        self.traj_manager.feed_traj_framedata(traj_framedata)\n\n    def mask_paused_env(self, frag):\n        running = ~frag['_SKIP_']\n        if running.all():\n            return frag\n        for key in frag:\n            if not key.startswith('_') and hasattr(frag[key], '__len__') and len(frag[key]) == self.n_thread:\n                frag[key] = frag[key][running]\n        return frag\n\n\n    def _create_config_fly(self):\n        logdir = GlobalConfig.logdir\n        self.input_file_dir = '%s/cmd_io.txt' % logdir\n        if not os.path.exists(self.input_file_dir):\n            with open(self.input_file_dir, 'w+', encoding='utf8') as f: f.writelines([\"# Write cmd at next line: \", \"\"])\n\n    def _config_on_fly(self):\n        if not os.path.exists(self.input_file_dir): return\n\n        with open(self.input_file_dir, 'r', encoding='utf8') as f:\n            cmdlines = f.readlines()\n\n        cmdlines_writeback = []\n        any_change = False\n\n        for cmdline in cmdlines:\n            if cmdline.startswith('#') or cmdline==\"\\n\" or cmdline==\" \\n\":\n                cmdlines_writeback.append(cmdline)\n            else:\n                any_change = True\n                try:\n                    print亮绿('[foundation.py] ------- executing: %s ------'%cmdline)\n                    exec(cmdline)\n                    cmdlines_writeback.append('# [execute successfully]\\t'+cmdline)\n                except:\n                    print红(traceback.format_exc())\n                    cmdlines_writeback.append('# [execute failed]\\t'+cmdline)\n\n        if any_change:\n            with open(self.input_file_dir, 'w+', encoding='utf8') as f:\n                f.writelines(cmdlines_writeback)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/net.py",
    "content": "import torch, math, copy\nimport numpy as np\nimport torch.nn as nn\nfrom torch.distributions.categorical import Categorical\nfrom UTIL.colorful import print亮绿\nfrom UTIL.tensor_ops import Args2tensor_Return2numpy, Args2tensor, __hashn__, my_view\nfrom UTIL.tensor_ops import pt_inf\nfrom UTIL.exp_helper import changed\nfrom .ccategorical import CCategorical\nfrom .foundation import AlgorithmConfig\nfrom ALGORITHM.common.attention import SimpleAttention\nfrom ALGORITHM.common.norm import DynamicNormFix\nfrom ALGORITHM.common.net_manifest import weights_init\n\n\n\n\"\"\"\n    network initialize\n\"\"\"\nclass Net(nn.Module):\n    def __init__(self, rawob_dim, n_action, **kwargs):\n        super().__init__()\n        self.update_cnt = nn.Parameter(\n            torch.zeros(1, requires_grad=False, dtype=torch.long), requires_grad=False)\n        self.use_normalization = AlgorithmConfig.use_normalization\n        self.use_policy_resonance = AlgorithmConfig.policy_resonance\n        self.n_action = n_action\n        \n        \n        if self.use_policy_resonance:\n            self.ccategorical = CCategorical(kwargs['stage_planner'])\n            self.is_resonance_active = lambda: kwargs['stage_planner'].is_resonance_active()\n\n        h_dim = AlgorithmConfig.net_hdim\n\n        # observation normalization\n        if self.use_normalization:\n            self._batch_norm = DynamicNormFix(rawob_dim, only_for_last_dim=True, exclude_one_hot=True, exclude_nan=True)\n\n        n_entity = AlgorithmConfig.n_entity_placeholder\n        \n        # # # # # # # # # #  actor-critic share # # # # # # # # # # # #\n        self.obs_encoder = nn.Sequential(nn.Linear(rawob_dim, h_dim), nn.ReLU(inplace=True), nn.Linear(h_dim, h_dim))\n        self.attention_layer = SimpleAttention(h_dim=h_dim)\n        # # # # # # # # # #        actor        # # # # # # # # # # # #\n        _size = n_entity * h_dim\n        self.policy_head = nn.Sequential(\n            nn.Linear(_size, h_dim), nn.ReLU(inplace=True),\n            nn.Linear(h_dim, h_dim//2), nn.ReLU(inplace=True),\n            nn.Linear(h_dim//2, self.n_action))\n        # # # # # # # # # # critic # # # # # # # # # # # #\n        \n        _size = n_entity * h_dim\n        self.ct_encoder = nn.Sequential(nn.Linear(_size, h_dim), nn.ReLU(inplace=True), nn.Linear(h_dim, h_dim))\n        self.ct_attention_layer = SimpleAttention(h_dim=h_dim)\n        self.get_value = nn.Sequential(nn.Linear(h_dim, h_dim), nn.ReLU(inplace=True),nn.Linear(h_dim, 1))\n\n\n        self.is_recurrent = False\n        self.apply(weights_init)\n        return\n    \n    @Args2tensor_Return2numpy\n    def act(self, *args, **kargs):\n        return self._act(*args, **kargs)\n    \n    @Args2tensor\n    def evaluate_actions(self, *args, **kargs):\n        return self._act(*args, **kargs, eval_mode=True)\n\n    def _act(self, obs=None, test_mode=None, eval_mode=False, eval_actions=None, avail_act=None, agent_ids=None, eprsn=None):\n        eval_act = eval_actions if eval_mode else None\n        others = {}\n        if self.use_normalization:\n            if torch.isnan(obs).all(): pass\n            else: obs = self._batch_norm(obs, freeze=(eval_mode or test_mode))\n\n        mask_dead = torch.isnan(obs).any(-1)\n        obs = torch.nan_to_num_(obs, 0)         # replace dead agents' obs, from NaN to 0\n        \n        # # # # # # # # # # actor-critic share # # # # # # # # # # # #\n        baec = self.obs_encoder(obs)\n        baec = self.attention_layer(k=baec,q=baec,v=baec, mask=mask_dead)\n\n        # # # # # # # # # # actor # # # # # # # # # # # #\n        at_bac = my_view(baec,[0,0,-1])\n        logits = self.policy_head(at_bac)\n        \n        # choose action selector\n        logit2act = self._logit2act_rsn if self.use_policy_resonance and self.is_resonance_active() else self._logit2act\n        \n        # apply action selector\n        act, actLogProbs, distEntropy, probs = logit2act(   logits, \n                                                            eval_mode=eval_mode,\n                                                            test_mode=test_mode, \n                                                            eval_actions=eval_act, \n                                                            avail_act=avail_act,\n                                                            eprsn=eprsn)\n        \n        \n        # # # # # # # # # # critic # # # # # # # # # # # #\n        ct_bac = my_view(baec,[0,0,-1])\n        ct_bac = self.ct_encoder(ct_bac)\n        ct_bac = self.ct_attention_layer(k=ct_bac,q=ct_bac,v=ct_bac)\n        value = self.get_value(ct_bac)\n        \n        if not eval_mode: return act, value, actLogProbs\n        else:             return value, actLogProbs, distEntropy, probs, others\n\n    def _logit2act_rsn(self, logits_agent_cluster, eval_mode, test_mode, eval_actions=None, avail_act=None, eprsn=None):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = self.ccategorical.feed_logits(logits_agent_cluster)\n        \n        if not test_mode: act = self.ccategorical.sample(act_dist, eprsn) if not eval_mode else eval_actions\n        else:             act = torch.argmax(act_dist.probs, axis=2)\n        # the policy gradient loss will feedback from here\n        actLogProbs = self._get_act_log_probs(act_dist, act) \n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    def _logit2act(self, logits_agent_cluster, eval_mode, test_mode, eval_actions=None, avail_act=None, **kwargs):\n        if avail_act is not None: logits_agent_cluster = torch.where(avail_act>0, logits_agent_cluster, -pt_inf())\n        act_dist = Categorical(logits = logits_agent_cluster)\n        if not test_mode:  act = act_dist.sample() if not eval_mode else eval_actions\n        else:              act = torch.argmax(act_dist.probs, axis=2)\n        actLogProbs = self._get_act_log_probs(act_dist, act) # the policy gradient loss will feedback from here\n        # sum up the log prob of all agents\n        distEntropy = act_dist.entropy().mean(-1) if eval_mode else None\n        return act, actLogProbs, distEntropy, act_dist.probs\n\n    @staticmethod\n    def _get_act_log_probs(distribution, action):\n        return distribution.log_prob(action.squeeze(-1)).unsqueeze(-1)\n    \n    \n\n    \n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/ppo.py",
    "content": "import torch, math, traceback\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport numpy as np\nfrom random import randint, sample\nfrom torch.utils.data.sampler import BatchSampler, SubsetRandomSampler\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import _2tensor, __hash__, __hashn__\nfrom config import GlobalConfig as cfg\nfrom UTIL.gpu_share import GpuShareUnit\nfrom .ppo_sampler import TrajPoolSampler\nfrom VISUALIZE.mcom import mcom\n\nclass PPO():\n    def __init__(self, policy_and_critic, ppo_config, mcv=None):\n        self.policy_and_critic = policy_and_critic\n        self.clip_param = ppo_config.clip_param\n        self.ppo_epoch = ppo_config.ppo_epoch\n        self.use_avail_act = ppo_config.ppo_epoch\n        self.n_pieces_batch_division = ppo_config.n_pieces_batch_division\n        self.value_loss_coef = ppo_config.value_loss_coef\n        self.entropy_coef = ppo_config.entropy_coef\n        self.max_grad_norm = ppo_config.max_grad_norm\n        self.add_prob_loss = ppo_config.add_prob_loss\n        self.prevent_batchsize_oom = ppo_config.prevent_batchsize_oom\n        # self.freeze_body = ppo_config.freeze_body\n        self.lr = ppo_config.lr\n        self.all_parameter = list(policy_and_critic.named_parameters())\n\n        # if not self.freeze_body:\n        self.parameter = [p for p_name, p in self.all_parameter]\n        self.optimizer = optim.Adam(self.parameter, lr=self.lr)\n\n        self.g_update_delayer = 0\n        self.g_initial_value_loss = 0\n        \n        # 轮流训练式\n        self.mcv = mcv\n        self.ppo_update_cnt = 0\n        self.batch_size_reminder = True\n        self.trivial_dict = {}\n\n        assert self.n_pieces_batch_division == 1\n        self.gpu_share_unit = GpuShareUnit(cfg.device, gpu_party=cfg.gpu_party)\n\n\n    def train_on_traj(self, traj_pool, task):\n        while True:\n            try:\n                with self.gpu_share_unit:\n                    self.train_on_traj_(traj_pool, task) \n                break # 运行到这说明显存充足\n            except RuntimeError as err:\n                print(traceback.format_exc())\n                if self.prevent_batchsize_oom:\n                    # in some cases, reversing MaxSampleNum a single time is not enough\n                    if TrajPoolSampler.MaxSampleNum[-1] < 0: TrajPoolSampler.MaxSampleNum.pop(-1)\n                    assert TrajPoolSampler.MaxSampleNum[-1] > 0\n                    TrajPoolSampler.MaxSampleNum[-1] = -1\n                    print亮红('Insufficient gpu memory, using previous sample size !')\n                else:\n                    assert False\n            torch.cuda.empty_cache()\n\n    def train_on_traj_(self, traj_pool, task):\n\n        ppo_valid_percent_list = []\n        sampler = TrajPoolSampler(n_div=1, traj_pool=traj_pool, flag=task, prevent_batchsize_oom=self.prevent_batchsize_oom, mcv=self.mcv)\n        # before_training_hash = [__hashn__(t.parameters()) for t in (self.policy_and_critic._nets_flat_placeholder_)]\n        for e in range(self.ppo_epoch):\n            sample_iter = sampler.reset_and_get_iter()\n            self.optimizer.zero_grad()\n            # ! get traj fragment\n            sample = next(sample_iter)\n            # ! build graph, then update network\n            loss_final, others = self.establish_pytorch_graph(task, sample, e)\n            loss_final = loss_final*0.5\n            if e==0: print('[PPO.py] Memory Allocated %.2f GB'%(torch.cuda.memory_allocated()/1073741824))\n            loss_final.backward()\n            # log\n            ppo_valid_percent_list.append(others.pop('PPO valid percent').item())\n            self.log_trivial(dictionary=others); others = None\n            nn.utils.clip_grad_norm_(self.parameter, self.max_grad_norm)\n            self.optimizer.step()\n            \n            if ppo_valid_percent_list[-1] < 0.70: \n                print亮黄('policy change too much, epoch terminate early'); break \n        pass # finish all epoch update\n\n        print亮黄(np.array(ppo_valid_percent_list))\n        self.log_trivial_finalize()\n\n        self.ppo_update_cnt += 1\n                \n        \n        return self.ppo_update_cnt\n\n    def freeze_body(self):\n        assert False, \"function forbidden\"\n        self.freeze_body = True\n        self.parameter_pv = [p_name for p_name, p in self.all_parameter if not any(p_name.startswith(kw)  for kw in ('obs_encoder', 'attention_layer'))]\n        self.parameter = [p for p_name, p in self.all_parameter if not any(p_name.startswith(kw)  for kw in ('obs_encoder', 'attention_layer'))]\n        self.optimizer = optim.Adam(self.parameter, lr=self.lr)\n        print('change train object')\n\n    def log_trivial(self, dictionary):\n        for key in dictionary:\n            if key not in self.trivial_dict: self.trivial_dict[key] = []\n            item = dictionary[key].item() if hasattr(dictionary[key], 'item') else dictionary[key]\n            self.trivial_dict[key].append(item)\n\n    def log_trivial_finalize(self, print=True):\n        for key in self.trivial_dict:\n            self.trivial_dict[key] = np.array(self.trivial_dict[key])\n        \n        print_buf = ['[ppo.py] ']\n        for key in self.trivial_dict:\n            self.trivial_dict[key] = self.trivial_dict[key].mean()\n            print_buf.append(' %s:%.3f, '%(key, self.trivial_dict[key]))\n            if self.mcv is not None:  self.mcv.rec(self.trivial_dict[key], key)\n        if print: print紫(''.join(print_buf))\n        if self.mcv is not None:\n            self.mcv.rec_show()\n        self.trivial_dict = {}\n\n\n    def establish_pytorch_graph(self, flag, sample, n):\n        obs = _2tensor(sample['obs'])\n        advantage = _2tensor(sample['advantage'])\n        action = _2tensor(sample['action'])\n        oldPi_actionLogProb = _2tensor(sample['actionLogProb'])\n        real_value = _2tensor(sample['return'])\n        avail_act = _2tensor(sample['avail_act']) if 'avail_act' in sample else None\n\n        # batchsize = advantage.shape[0]#; print亮紫(batchsize)\n        batch_agent_size = advantage.shape[0]*advantage.shape[1]\n\n        assert flag == 'train'\n        newPi_value, newPi_actionLogProb, entropy, probs, others = \\\n            self.policy_and_critic.evaluate_actions(\n                obs=obs, \n                eval_actions=action, \n                test_mode=False, \n                avail_act=avail_act)\n        entropy_loss = entropy.mean()\n\n\n        n_actions = probs.shape[-1]\n        if self.add_prob_loss: assert n_actions <= 15  # \n        penalty_prob_line = (1/n_actions)*0.12\n        probs_loss = (penalty_prob_line - torch.clamp(probs, min=0, max=penalty_prob_line)).mean()\n        if not self.add_prob_loss:\n            probs_loss = torch.zeros_like(probs_loss)\n\n        # dual clip ppo core\n        E = newPi_actionLogProb - oldPi_actionLogProb\n        E_clip = torch.zeros_like(E)\n        E_clip = torch.where(advantage > 0, torch.clamp(E, max=np.log(1.0+self.clip_param)), E_clip)\n        E_clip = torch.where(advantage < 0, torch.clamp(E, min=np.log(1.0-self.clip_param), max=np.log(5) ), E_clip)\n        ratio  = torch.exp(E_clip)\n        policy_loss = -(ratio*advantage).mean()\n\n        # add all loses\n        value_loss = 0.5 * F.mse_loss(real_value, newPi_value)\n\n\n        AT_net_loss = policy_loss - entropy_loss*self.entropy_coef # + probs_loss*20\n        CT_net_loss = value_loss * 1.0\n        # AE_new_loss = ae_loss * 1.0\n\n        loss_final =  AT_net_loss + CT_net_loss  # + AE_new_loss\n\n        ppo_valid_percent = ((E_clip == E).int().sum()/batch_agent_size)\n\n        nz_mask = real_value!=0\n        value_loss_abs = (real_value[nz_mask] - newPi_value[nz_mask]).abs().mean()\n        others = {\n            'Value loss Abs':           value_loss_abs,\n            'PPO valid percent':        ppo_valid_percent,\n            'CT_net_loss':              CT_net_loss,\n            'AT_net_loss':              AT_net_loss,\n        }\n\n\n        return loss_final, others\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/ppo_sampler.py",
    "content": "import torch, math, traceback\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport numpy as np\nfrom random import randint, sample\nfrom torch.utils.data.sampler import BatchSampler, SubsetRandomSampler\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import _2tensor, __hash__, repeat_at\nfrom config import GlobalConfig as cfg\nfrom UTIL.gpu_share import GpuShareUnit\nclass TrajPoolSampler():\n    def __init__(self, n_div, traj_pool, flag, prevent_batchsize_oom=False, mcv=None):\n        self.n_pieces_batch_division = n_div\n        self.prevent_batchsize_oom = prevent_batchsize_oom    \n        self.mcv = mcv\n        if self.prevent_batchsize_oom:\n            assert self.n_pieces_batch_division==1, ('?')\n\n        self.num_batch = None\n        self.container = {}\n        self.warned = False\n        assert flag=='train'\n        req_dict =        ['avail_act', 'obs', 'action', 'actionLogProb', 'return', 'reward', 'value']\n        req_dict_rename = ['avail_act', 'obs', 'action', 'actionLogProb', 'return', 'reward', 'state_value']\n        return_rename = \"return\"\n        value_rename =  \"state_value\"\n        advantage_rename = \"advantage\"\n        # replace 'obs' to 'obs > xxxx'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name):\n                real_key_list = [real_key for real_key in traj_pool[0].__dict__ if (key_name+'>' in real_key)]\n                assert len(real_key_list) > 0, ('check variable provided!', key,key_index)\n                for real_key in real_key_list:\n                    mainkey, subkey = real_key.split('>')\n                    req_dict.append(real_key)\n                    req_dict_rename.append(key_rename+'>'+subkey)\n        self.big_batch_size = -1  # vector should have same length, check it!\n        \n        # load traj into a 'container'\n        for key_index, key in enumerate(req_dict):\n            key_name =  req_dict[key_index]\n            key_rename = req_dict_rename[key_index]\n            if not hasattr(traj_pool[0], key_name): continue\n            set_item = np.concatenate([getattr(traj, key_name) for traj in traj_pool], axis=0)\n            if not (self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0)):\n                print('error')\n            assert self.big_batch_size==set_item.shape[0] or (self.big_batch_size<0), (key,key_index)\n            self.big_batch_size = set_item.shape[0]\n            self.container[key_rename] = set_item    # 指针赋值\n\n        # normalize advantage inside the batch\n        self.container[advantage_rename] = self.container[return_rename] - self.container[value_rename]\n        self.container[advantage_rename] = ( self.container[advantage_rename] - self.container[advantage_rename].mean() ) / (self.container[advantage_rename].std() + 1e-5)\n        # size of minibatch for each agent\n        self.mini_batch_size = math.ceil(self.big_batch_size / self.n_pieces_batch_division)  \n\n    def __len__(self):\n        return self.n_pieces_batch_division\n\n    def determine_max_n_sample(self):\n        assert self.prevent_batchsize_oom\n        if not hasattr(TrajPoolSampler,'MaxSampleNum'):\n            # initialization\n            TrajPoolSampler.MaxSampleNum =  [int(self.big_batch_size*(i+1)/50) for i in range(50)]\n            max_n_sample = self.big_batch_size\n        elif TrajPoolSampler.MaxSampleNum[-1] > 0:  \n            # meaning that oom never happen, at least not yet\n            # only update when the batch size increases\n            if self.big_batch_size > TrajPoolSampler.MaxSampleNum[-1]: TrajPoolSampler.MaxSampleNum.append(self.big_batch_size)\n            max_n_sample = self.big_batch_size\n        else:\n            # meaning that oom already happened, choose TrajPoolSampler.MaxSampleNum[-2] to be the limit\n            assert TrajPoolSampler.MaxSampleNum[-2] > 0\n            max_n_sample = TrajPoolSampler.MaxSampleNum[-2]\n        return max_n_sample\n\n    def reset_and_get_iter(self):\n        if not self.prevent_batchsize_oom:\n            self.sampler = BatchSampler(SubsetRandomSampler(range(self.big_batch_size)), self.mini_batch_size, drop_last=False)\n        else:\n            max_n_sample = self.determine_max_n_sample()\n            n_sample = min(self.big_batch_size, max_n_sample)\n            if not hasattr(self,'reminded'):\n                self.reminded = True\n                drop_percent = (self.big_batch_size-n_sample)/self.big_batch_size*100\n                if self.mcv is not None:\n                    self.mcv.rec(drop_percent, 'drop percent')\n                if drop_percent > 20: \n                    print_ = print亮红\n                    print_('droping %.1f percent samples..'%(drop_percent))\n                    assert False, \"GPU OOM!\"\n                else:\n                    print_ = print\n                    print_('droping %.1f percent samples..'%(drop_percent))\n            self.sampler = BatchSampler(SubsetRandomSampler(range(n_sample)), n_sample, drop_last=False)\n\n        for indices in self.sampler:\n            selected = {}\n            for key in self.container:\n                selected[key] = self.container[key][indices]\n            for key in [key for key in selected if '>' in key]:\n                # 重新把子母键值组合成二重字典\n                mainkey, subkey = key.split('>')\n                if not mainkey in selected: selected[mainkey] = {}\n                selected[mainkey][subkey] = selected[key]\n                del selected[key]\n            yield selected\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/shell_env.py",
    "content": "import numpy as np\nfrom config import GlobalConfig\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__, repeat_at, gather_righthand\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\nfrom .foundation import AlgorithmConfig\nfrom .cython_func import roll_hisory\n\nclass ShellEnvConfig:\n    add_avail_act = False\n\nclass ActionConvertPredatorPrey():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.dictionary_args = [\n            'ActionSet4::MoveToDirection;X=1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=-1.0 Z=0.0',\n        ] \n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        return ret\n\n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        pass\n\nclass ActionConvertLegacy():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            ('N/A',         'N/A',              None, None, None, None, None, None),   # 0\n            ('Idle',        'DynamicGuard',     None, None, None, None, None, None),   # 1\n            ('Idle',        'StaticAlert',      None, None, None, None, None, None),   # 2\n            ('Idle',        'AsFarAsPossible',              None, None, None, None, None, None),   # 4\n            ('Idle',        'StayWhenTargetInRange',        None, None, None, None, None, None),   # 5\n            ('SpecificMoving',      'Dir+X',    None, None, None, None, None, None),   # 7\n            ('SpecificMoving',      'Dir+Y',    None, None, None, None, None, None),   # 8\n            ('SpecificMoving',      'Dir-X',    None, None, None, None, None, None),   # 9\n            ('SpecificMoving',      'Dir-Y',    None, None, None, None, None, None),   # 10\n        ] \n        for i in range(self.OPP_NUM_ASSUME):\n            self.dictionary_args.append( ('SpecificAttacking',   'N/A',      None, None, None, None, OPP_TEAM_ASSUME, i) )\n    \n    \n\n    def convert_act_arr(self, type, a):\n        if type == 'RLA_UAV_Support':\n            args = self.dictionary_args[a]\n            # override wrong actions\n            if args[0] == 'SpecificAttacking':\n                return encode_action_as_digits('N/A',         'N/A',              None, None, None, None, None, None)\n            # override incorrect actions\n            if args[0] == 'Idle':\n                return encode_action_as_digits('Idle',        'StaticAlert',      None, None, None, None, None, None)\n            return encode_action_as_digits(*args)\n        else:\n            return encode_action_as_digits(*self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        for i in range(n_act):\n            args = self.dictionary_args[i]\n            \n            # for all kind of agents\n            if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n            \n            if type == 'RLA_UAV_Support':\n                if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n                if args[0] == 'SpecificAttacking':  ret[i] = DISABLE\n                if args[0] == 'Idle':               ret[i] = DISABLE\n                if args[1] == 'StaticAlert':        ret[i] = ENABLE\n        return ret\n    \n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \ndef count_list_type(x):\n    type_cnt = {}\n    for xx in x:\n        if xx not in type_cnt: type_cnt[xx] = 0\n        type_cnt[xx] += 1\n    return len(type_cnt)\n\n \n\nclass ShellEnvWrapper(object):\n    def __init__(self, n_agent, n_thread, space, mcv, rl_functional, alg_config, ScenarioConfig, team):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.team = team\n        self.space = space\n        self.mcv = mcv\n        self.rl_functional = rl_functional\n        if GlobalConfig.ScenarioConfig.EntityOriented:\n            self.core_dim = GlobalConfig.ScenarioConfig.obs_vec_length\n        else:\n            self.core_dim = space['obs_space']['obs_shape']\n        self.n_entity_placeholder = alg_config.n_entity_placeholder\n\n        # whether to use avail_act to block forbiden actions\n        self.AvailActProvided = False\n        if hasattr(ScenarioConfig, 'AvailActProvided'):\n            self.AvailActProvided = ScenarioConfig.AvailActProvided \n\n        if GlobalConfig.ScenarioConfig.SubTaskSelection in ['UhmapLargeScale', 'UhmapHuge', 'UhmapBreakingBad']:\n            ActionToDiscreteConverter = ActionConvertLegacy\n        else:\n            ActionToDiscreteConverter = ActionConvertPredatorPrey\n\n        self.action_converter = ActionToDiscreteConverter(\n                SELF_TEAM_ASSUME=team, \n                OPP_TEAM_ASSUME=(1-team), \n                OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        \n        # check parameters\n        self.patience = 2000\n\n    def interact_with_env(self, StateRecall):\n        if not hasattr(self, 'agent_type'):\n            self.agent_uid = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n            self.agent_type = [agent_meta['type'] \n                               for agent_meta in StateRecall['Latest-Team-Info'][0]['dataArr']\n                               if agent_meta['uId'] in self.agent_uid]\n            if ShellEnvConfig.add_avail_act:\n                self.avail_act = np.stack(tuple(self.action_converter.get_tp_avail_act(tp) for tp in self.agent_type))\n                self.avail_act = repeat_at(self.avail_act, insert_dim=0, n_times=self.n_thread)\n\n        act = np.zeros(shape=(self.n_thread, self.n_agent), dtype=np.int) - 1 # 初始化全部为 -1\n        \n        # read internal coop graph info\n        obs = StateRecall['Latest-Obs']\n        obs = my_view(obs,[0, 0, -1, self.core_dim])\n        obs[(obs==0).all(-1)] = np.nan\n\n        n_entity_raw = obs.shape[-2]\n        AlgorithmConfig.entity_distinct = [list(range(1)), list(range(1,n_entity_raw)), list(range(n_entity_raw,2*n_entity_raw))]\n\n        P  =  StateRecall['ENV-PAUSE']\n        R  = ~P\n        RST = StateRecall['Env-Suffered-Reset']\n        \n        # when needed, train!\n        if not StateRecall['Test-Flag']: self.rl_functional.train()\n        \n        if RST.all(): \n            # just experienced full reset on all episode, this is the first step of all env threads\n            # randomly pick threads \n            eprsn_yita = self.rl_functional.stage_planner.yita if AlgorithmConfig.policy_resonance else 0\n            EpRsn = np.random.rand(self.n_thread) < eprsn_yita\n            StateRecall['_EpRsn_'] = EpRsn\n\n        # prepare observation for the real RL algorithm\n        obs_feed = obs[R]\n        I_StateRecall = {\n            'obs':obs_feed, \n            'avail_act':self.avail_act[R],\n            'Test-Flag':StateRecall['Test-Flag'], \n            '_EpRsn_':StateRecall['_EpRsn_'][R],\n            'threads_active_flag':R, \n            'Latest-Team-Info':StateRecall['Latest-Team-Info'][R],\n        }\n        # load available act to limit action space if possible\n        if self.AvailActProvided:\n            avail_act = np.array([info['avail-act'] for info in np.array(StateRecall['Latest-Team-Info'][R], dtype=object)])\n            I_StateRecall.update({'avail_act':avail_act})\n\n        # the real RL algorithm ! !\n        act_active, internal_recall = self.rl_functional.interact_with_env_genuine(I_StateRecall)\n\n        # get decision results\n        act[R] = act_active\n        \n        # confirm actions are valid (satisfy 'avail-act')\n        if ShellEnvConfig.add_avail_act and self.patience>0:\n            self.patience -= 1\n            assert (gather_righthand(self.avail_act, repeat_at(act, -1, 1), check=False)[R]==1).all()\n            \n        # translate action into ue4 tuple action\n        act_converted = np.array([[ self.action_converter.convert_act_arr(self.agent_type[agentid], act) for agentid, act in enumerate(th) ] for th in act])\n        \n        # swap thread(batch) axis and agent axis\n        actions_list = np.swapaxes(act_converted, 0, 1)\n\n        # register callback hook\n        if not StateRecall['Test-Flag']:\n            StateRecall['_hook_'] = internal_recall['_hook_']\n            assert StateRecall['_hook_'] is not None\n        return actions_list, StateRecall \n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/stage_planner.py",
    "content": "import math\nfrom .foundation import AlgorithmConfig\nfrom UTIL.colorful import *\n\nclass PolicyRsnConfig:\n    resonance_start_at_update = 10\n    yita_min_prob = 0.15  #  should be >= (1/n_action)\n    yita_max = 0.75\n    yita_inc_per_update = 0.0075 # (increase to 0.75 in 500 updates)\n    freeze_critic = False\n    \n    yita_shift_method = '-sin'\n    yita_shift_cycle = 1000\n\n\nclass StagePlanner:\n    def __init__(self, mcv) -> None:\n        if AlgorithmConfig.policy_resonance:\n            self.resonance_active = False\n            self.yita = 0\n            self.yita_min_prob = PolicyRsnConfig.yita_min_prob\n        self.freeze_body = False\n        self.update_cnt = 0\n        self.mcv = mcv\n        self.trainer = None\n\n    \n    def is_resonance_active(self,):\n        return self.resonance_active\n    \n    def is_body_freeze(self,):\n        return self.freeze_body\n    \n    def get_yita(self):\n        return self.yita\n    \n    def get_yita_min_prob(self):\n        return PolicyRsnConfig.yita_min_prob\n    \n    def can_exec_trainning(self):\n        return True\n\n    def update_plan(self):\n        self.update_cnt += 1\n        if AlgorithmConfig.policy_resonance:\n            if self.resonance_active:\n                self.when_pr_active()\n            elif not self.resonance_active:\n                self.when_pr_inactive()\n        return\n    \n    def activate_pr(self):\n        self.resonance_active = True\n        self.freeze_body = True\n        if PolicyRsnConfig.freeze_critic:\n            self.trainer.freeze_body()\n\n    def when_pr_inactive(self):\n        assert not self.resonance_active\n        if PolicyRsnConfig.resonance_start_at_update >= 0:\n            # mean need to activate pr later\n            if self.update_cnt > PolicyRsnConfig.resonance_start_at_update:\n                # time is up, activate pr\n                self.activate_pr()\n        # log\n        pr = 1 if self.resonance_active else 0\n        self.mcv.rec(pr, 'resonance')\n        self.mcv.rec(self.yita, 'self.yita')\n\n    def when_pr_active(self):\n        assert self.resonance_active\n        self._update_yita()\n        # log\n        pr = 1 if self.resonance_active else 0\n        self.mcv.rec(pr, 'resonance')\n        self.mcv.rec(self.yita, 'self.yita')\n\n    def _update_yita(self):\n        '''\n            increase self.yita by @yita_inc_per_update per function call\n        '''\n        if PolicyRsnConfig.yita_shift_method == '-cos':\n            self.yita = PolicyRsnConfig.yita_max\n            t = -math.cos(2*math.pi/PolicyRsnConfig.yita_shift_cycle * self.update_cnt) * PolicyRsnConfig.yita_max\n            if t<=0:\n                self.yita = 0\n            else:\n                self.yita = t\n            print亮绿('yita update:', self.yita)\n\n        elif PolicyRsnConfig.yita_shift_method == '-sin':\n            self.yita = PolicyRsnConfig.yita_max\n            t = -math.sin(2*math.pi/PolicyRsnConfig.yita_shift_cycle * self.update_cnt) * PolicyRsnConfig.yita_max\n            if t<=0:\n                self.yita = 0\n            else:\n                self.yita = t\n            print亮绿('yita update:', self.yita)\n\n        elif PolicyRsnConfig.yita_shift_method == 'slow-inc':\n            self.yita += PolicyRsnConfig.yita_inc_per_update\n            if self.yita > PolicyRsnConfig.yita_max:\n                self.yita = PolicyRsnConfig.yita_max\n            print亮绿('yita update:', self.yita)\n        else:\n            assert False"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/ppo_ma/trajectory.py",
    "content": "# cython: language_level=3\nfrom config import GlobalConfig\nimport numpy as np\nfrom numpy.core.numeric import indices\nfrom .foundation import AlgorithmConfig\nfrom ALGORITHM.common.traj import TRAJ_BASE\nimport copy\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import __hash__, my_view, np_one_hot, np_repeat_at, np_softmax, scatter_with_nan\n\nclass trajectory(TRAJ_BASE):\n\n    def __init__(self, traj_limit, env_id):\n        super().__init__(traj_limit, env_id)\n        self.reference_track_name = 'value'\n\n    def early_finalize(self):\n        assert not self.readonly_lock   # unfinished traj\n        self.need_reward_bootstrap = True\n\n    def set_terminal_obs(self, tobs):\n        self.tobs = copy.deepcopy(tobs)\n\n    def cut_tail(self):\n        # 删去多余的预留空间\n        super().cut_tail()\n        TJ = lambda key: getattr(self, key)\n        # 进一步地， 根据这个轨迹上的NaN，删除所有无效时间点\n        reference_track = getattr(self, self.reference_track_name)\n        if self.need_reward_bootstrap:\n            assert False, ('it should not go here if everything goes as expected')\n            # print('need_reward_bootstrap') 找到最后一个不是nan的位置\n            T = np.where(~np.isnan(reference_track.squeeze()))[0][-1]\n            self.boot_strap_value = {\n                'bootstrap_value':TJ('value').squeeze()[T].copy(), \n            }\n            assert not hasattr(self,'tobs')\n            self.set_terminal_obs(TJ('g_obs')[T].copy())\n            reference_track[T] = np.nan\n        # deprecated if nothing in it\n        p_invalid = np.isnan(my_view(reference_track, [0, -1])).any(axis=-1)\n        p_valid = ~p_invalid\n        if p_invalid.all(): #invalid traj\n            self.deprecated_flag = True\n            return\n        # adjust reward position\n        reward = TJ('reward')\n        for i in reversed(range(self.time_pointer)):\n            if p_invalid[i] and i != 0: # invalid, push reward forward\n                reward[i-1] += reward[i]; reward[i] = np.nan\n        setattr(self, 'reward', reward)\n        # clip NaN\n        for key in self.key_dict: setattr(self, key, TJ(key)[p_valid])\n        # all done\n        return\n\n    def reward_push_forward(self, dead_mask):\n        # self.new_reward = self.reward.copy()\n        if AlgorithmConfig.gamma_in_reward_forwarding:\n            gamma = AlgorithmConfig.gamma_in_reward_forwarding_value \n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i]*gamma, 0)      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n\n        else:\n            for i in reversed(range(self.time_pointer)):\n                if i==0: continue\n                self.reward[i-1] += np.where(dead_mask[i], self.reward[i], 0)      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n                self.reward[i]    = np.where(dead_mask[i], 0, self.reward[i])      # if dead_mask[i]==True, this frame is invalid, move reward forward, set self.reward[i] to 0\n        return\n\n\n    # new finalize\n    def finalize(self):\n        self.readonly_lock = True\n        assert not self.deprecated_flag\n        TJ = lambda key: getattr(self, key) \n        assert not np.isnan(TJ('reward')).any()\n        # deadmask\n        tmp = np.isnan(my_view(self.obs, [0,0,-1]))\n        dead_mask = tmp.all(-1)\n        # if (True): # check if the mask is correct\n        #     dead_mask_self = np.isnan(my_view(self.obs, [0,0,-1])[:,:,0])\n        #     assert (dead_mask==dead_mask_self).all()\n        # dead_mask2 = tmp.any(-1)\n        # assert (dead_mask==dead_mask2).all()\n        self.reward_push_forward(dead_mask) # push terminal reward forward 38 42 54\n        threat = np.zeros(shape=dead_mask.shape) - 1\n        assert dead_mask.shape[0] == self.time_pointer\n        for i in reversed(range(self.time_pointer)):\n            # threat[:(i+1)] 不包含threat[(i+1)]\n            if i+1 < self.time_pointer:\n                threat[:(i+1)] += (~(dead_mask[i+1]&dead_mask[i])).astype(np.int)\n            elif i+1 == self.time_pointer:\n                threat[:] += (~dead_mask[i]).astype(np.int)\n\n        SAFE_LIMIT = 11\n        threat = np.clip(threat, -1, SAFE_LIMIT)\n        setattr(self, 'threat', np.expand_dims(threat, -1))\n\n        # ! Use GAE to calculate return\n        self.gae_finalize_return(reward_key='reward', value_key='value', new_return_name='return')\n        return\n\n    def gae_finalize_return(self, reward_key, value_key, new_return_name):\n        # ------- gae parameters -------\n        gamma = AlgorithmConfig.gamma \n        tau = AlgorithmConfig.tau\n        # ------- -------------- -------\n        rewards = getattr(self, reward_key)\n        value = getattr(self, value_key)\n        length = rewards.shape[0]\n        assert rewards.shape[0]==value.shape[0]\n        # if dimension not aligned\n        if rewards.ndim == value.ndim-1: rewards = np.expand_dims(rewards, -1)\n        # initalize two more tracks\n        setattr(self, new_return_name, np.zeros_like(value))\n        self.key_dict.append(new_return_name)\n\n        returns = getattr(self, new_return_name)\n        boot_strap = 0 if not self.need_reward_bootstrap else self.boot_strap_value['bootstrap_'+value_key]\n\n        for step in reversed(range(length)):\n            if step==(length-1): # 最后一帧\n                value_preds_delta = rewards[step] + gamma * boot_strap      - value[step]\n                gae = value_preds_delta\n            else:\n                value_preds_delta = rewards[step] + gamma * value[step + 1] - value[step]\n                gae = value_preds_delta + gamma * tau * gae\n            returns[step] = gae + value[step]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nclass TrajPoolManager(object):\n    def __init__(self):\n        self.cnt = 0\n\n    def absorb_finalize_pool(self, pool):\n        for traj_handle in pool:\n            traj_handle.cut_tail()\n        pool = list(filter(lambda traj: not traj.deprecated_flag, pool))\n        for traj_handle in pool: traj_handle.finalize()\n        self.cnt += 1\n        task = ['train']\n        return task, pool\n\n\n\n\n\n\n\n\n\n\n\n\n\n'''\n    轨迹池管理\n'''\n\nclass TrajManagerBase(object):\n    def __init__(self, n_env, traj_limit):\n        self.n_env = n_env\n        self.traj_limit = traj_limit\n        self.update_cnt = 0\n        self.traj_pool = []\n        self.registered_keys = []\n        self.live_trajs = [trajectory(self.traj_limit, env_id=i) for i in range(self.n_env)]\n        self.live_traj_frame = [0 for _ in range(self.n_env)]\n        self._traj_lock_buf = None\n        self.patience = 1000\n        pass\n    \n    def __check_integraty(self, traj_frag):\n        if self.patience < 0: \n            return # stop wasting time checking this\n        self.patience -= 1\n        for key in traj_frag:\n            if key not in self.registered_keys and (not key.startswith('_')):\n                self.registered_keys.append(key)\n        for key in self.registered_keys:\n            assert key in traj_frag, ('this key sometimes disappears from the traj_frag:', key)\n\n    def batch_update(self, traj_frag):\n        self.__check_integraty(traj_frag)\n        done = traj_frag['_DONE_']; traj_frag.pop('_DONE_') # done flag\n        skip = traj_frag['_SKIP_']; traj_frag.pop('_SKIP_') # skip/frozen flag\n        tobs = traj_frag['_TOBS_']; traj_frag.pop('_TOBS_') # terminal obs\n        # single bool to list bool\n        if isinstance(done, bool): done = [done for i in range(self.n_env)]\n        if isinstance(skip, bool): skip = [skip for i in range(self.n_env)]\n        n_active = sum(~skip)\n        # feed\n        cnt = 0\n        for env_i in range(self.n_env):\n            if skip[env_i]: continue\n            # otherwise\n            frag_index = cnt; cnt += 1\n            env_index = env_i\n            traj_handle = self.live_trajs[env_index]\n            for key in traj_frag:\n                self.traj_remember(traj_handle, key=key, content=traj_frag[key],frag_index=frag_index, n_active=n_active)\n            self.live_traj_frame[env_index] += 1\n            traj_handle.time_shift()\n            if done[env_i]:\n                assert tobs[env_i] is not None # get the final obs\n                traj_handle.set_terminal_obs(tobs[env_i])\n                self.traj_pool.append(traj_handle)\n                self.live_trajs[env_index] = trajectory(self.traj_limit, env_id=env_index)\n                self.live_traj_frame[env_index] = 0\n\n    def traj_remember(self, traj, key, content, frag_index, n_active):\n        if content is None: traj.remember(key, None)\n        elif isinstance(content, dict):\n            for sub_key in content: \n                self.traj_remember(traj, \"\".join((key , \">\" , sub_key)), content=content[sub_key], frag_index=frag_index, n_active=n_active)\n        else:\n            assert n_active == len(content), ('length error')\n            traj.remember(key, content[frag_index]) # *\n\n\nclass BatchTrajManager(TrajManagerBase):\n    def __init__(self, n_env, traj_limit, trainer_hook):\n        super().__init__(n_env, traj_limit)\n        self.trainer_hook = trainer_hook\n        self.traj_limit = traj_limit\n        self.train_traj_needed = AlgorithmConfig.train_traj_needed\n        self.pool_manager = TrajPoolManager()\n\n    def update(self, traj_frag, index):\n        assert traj_frag is not None\n        for j, env_i in enumerate(index):\n            traj_handle = self.live_trajs[env_i]\n            for key in traj_frag:\n                if traj_frag[key] is None:\n                    assert False, key\n                if isinstance(traj_frag[key], dict):  # 如果是二重字典，特殊处理\n                    for sub_key in traj_frag[key]:\n                        content = traj_frag[key][sub_key][j]\n                        traj_handle.remember(key + \">\" + sub_key, content)\n                else:\n                    content = traj_frag[key][j]\n                    traj_handle.remember(key, content)\n            self.live_traj_frame[env_i] += 1\n            traj_handle.time_shift()\n        return\n\n    # 函数入口\n    def feed_traj_framedata(self, traj_frag, require_hook=False):\n        # an unlock hook must be executed before new trajectory feed in\n        assert self._traj_lock_buf is None\n        if require_hook: \n            # the traj_frag is not intact, lock up traj_frag, wait for more\n            assert '_SKIP_' in traj_frag\n            assert '_DONE_' not in traj_frag\n            assert 'reward' not in traj_frag\n            self._traj_lock_buf = traj_frag\n            return self.unlock_fn\n        else:\n            assert '_DONE_' in traj_frag\n            assert '_SKIP_' in traj_frag\n            self.batch_update(traj_frag=traj_frag)\n            return\n\n        \n    def clear_traj_pool(self):\n        print('do update %d'%self.update_cnt)\n        _, self.traj_pool = self.pool_manager.absorb_finalize_pool(pool=self.traj_pool)\n        self.traj_pool = []\n        # self.update_cnt += 1\n        # assert ppo_update_cnt == self.update_cnt\n        return self.update_cnt\n\n        \n    def train_and_clear_traj_pool(self):\n        print('do update %d'%self.update_cnt)\n\n        current_task_l, self.traj_pool = self.pool_manager.absorb_finalize_pool(pool=self.traj_pool)\n        for current_task in current_task_l:\n            ppo_update_cnt = self.trainer_hook(self.traj_pool, current_task)\n\n        self.traj_pool = []\n        self.update_cnt += 1\n        # assert ppo_update_cnt == self.update_cnt\n        return self.update_cnt\n\n    def can_exec_training(self):\n        if len(self.traj_pool) >= self.train_traj_needed:  return True\n        else:  return False\n \n    def unlock_fn(self, traj_frag):\n        assert self._traj_lock_buf is not None\n        traj_frag.update(self._traj_lock_buf)\n        self._traj_lock_buf = None\n        assert '_DONE_' in traj_frag\n        assert '_SKIP_' in traj_frag\n        self.batch_update(traj_frag=traj_frag)\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/random/actionset.py",
    "content": "# ====================================================================\n# random moving \n# ====================================================================\nimport numpy as np\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\n\nclass ActionConvertV1Dummy():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            'ActionSet1::N/A;N/A'                        ,\n        ]\n        self.ActDigitLen = ActDigitLen\n        self.n_act = len(self.dictionary_args)\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        for i in range(n_act):\n            args = self.dictionary_args[i]\n            \n            # for all kind of agents\n            if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n            \n            if type == 'RLA_UAV_Support':\n                if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n                if args[0] == 'SpecificAttacking':  ret[i] = DISABLE\n                if args[0] == 'Idle':               ret[i] = DISABLE\n                if args[1] == 'StaticAlert':        ret[i] = ENABLE\n        return ret\n\n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \n\n\nclass ActionConvertV1Carrier():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            'ActionSet1::N/A;N/A'                        ,\n            'ActionSet1::Special;Detach'                 ,\n            'ActionSet1::Idle;DynamicGuard'              ,\n            'ActionSet1::Idle;StaticAlert'               ,\n            'ActionSet1::Idle;AggressivePersue'          ,\n            'ActionSet1::Idle;AsFarAsPossible'           ,\n            'ActionSet1::Idle;StayWhenTargetInRange'     ,\n            'ActionSet1::Idle;StayWhenTargetInHalfRange' ,\n            'ActionSet1::SpecificMoving;Dir+X'           ,\n            'ActionSet1::SpecificMoving;Dir+Y'           ,\n            'ActionSet1::SpecificMoving;Dir-X'           ,\n            'ActionSet1::SpecificMoving;Dir-Y'           ,\n            'ActionSet1::PatrolMoving;Dir+X'             ,\n            'ActionSet1::PatrolMoving;Dir+Y'             ,\n            'ActionSet1::PatrolMoving;Dir-X'             ,\n            'ActionSet1::PatrolMoving;Dir-Y'             ,\n        ]\n        for i in range(self.OPP_NUM_ASSUME):\n            self.dictionary_args.append( f'ActionSet1::SpecificAttacking;T{OPP_TEAM_ASSUME}-{i}')\n        self.ActDigitLen = ActDigitLen\n        self.n_act = len(self.dictionary_args)\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        for i in range(n_act):\n            args = self.dictionary_args[i]\n            \n            # for all kind of agents\n            if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n            \n            if type == 'RLA_UAV_Support':\n                if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n                if args[0] == 'SpecificAttacking':  ret[i] = DISABLE\n                if args[0] == 'Idle':               ret[i] = DISABLE\n                if args[1] == 'StaticAlert':        ret[i] = ENABLE\n        return ret\n\n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \n\n\nclass ActionConvertV1Momentum():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            'ActionSet1::MoveToDirection2D@Z;X=1.0 Y=0.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=1.0 Y=1.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=0.0 Y=1.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=-1.0 Y=1.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=-1.0 Y=0.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=-1.0 Y=-1.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=0.0 Y=-1.0 Z=700.0',\n            'ActionSet1::MoveToDirection2D@Z;X=1.0 Y=-1.0 Z=700.0',\n        ]\n        self.ActDigitLen = ActDigitLen\n        self.n_act = len(self.dictionary_args)\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        return ret\n\n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \n\nclass ActionConvertMovingV4():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.dictionary_args = [\n            'ActionSet4::MoveToDirection;X=1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=-1.0 Z=0.0',\n        ] \n        self.n_act = len(self.dictionary_args)\n        self.ActDigitLen = ActDigitLen\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0; ENABLE = 1\n        ret = np.zeros(self.n_act) + ENABLE  # enable all\n        return ret\n\nclass CarrierAction():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.dictionary_args = [\n            'ActionSet4::MoveToDirection;X=1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=0.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=-1.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=0.0 Y=-1.0 Z=0.0',\n            'ActionSet4::MoveToDirection;X=1.0 Y=-1.0 Z=0.0',\n        ] \n        self.n_act = len(self.dictionary_args)\n        self.ActDigitLen = ActDigitLen\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0; ENABLE = 1\n        ret = np.zeros(self.n_act) + ENABLE  # enable all\n        return ret\n\nclass ActionConvertV2():\n    def __init__(self, SELF_TEAM_ASSUME, OPP_TEAM_ASSUME, OPP_NUM_ASSUME) -> None:\n        self.SELF_TEAM_ASSUME = SELF_TEAM_ASSUME\n        self.OPP_TEAM_ASSUME = OPP_TEAM_ASSUME\n        self.OPP_NUM_ASSUME = OPP_NUM_ASSUME\n        # (main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None)\n        self.dictionary_args = [\n            'ActionSet2::N/A;N/A'                        ,\n            'ActionSet2::Idle;DynamicGuard'              ,\n            'ActionSet2::Idle;StaticAlert'               ,\n            'ActionSet2::Idle;AggressivePersue'          ,\n            'ActionSet2::Idle;AsFarAsPossible'           ,\n            'ActionSet2::Idle;StayWhenTargetInRange'     ,\n            'ActionSet2::Idle;StayWhenTargetInHalfRange' ,\n            'ActionSet2::SpecificMoving;Dir+X'           ,\n            'ActionSet2::SpecificMoving;Dir+Y'           ,\n            'ActionSet2::SpecificMoving;Dir-X'           ,\n            'ActionSet2::SpecificMoving;Dir-Y'           ,\n            'ActionSet2::PatrolMoving;Dir+X'             ,\n            'ActionSet2::PatrolMoving;Dir+Y'             ,\n            'ActionSet2::PatrolMoving;Dir-X'             ,\n            'ActionSet2::PatrolMoving;Dir-Y'             ,\n        ]\n        for i in range(self.OPP_NUM_ASSUME):\n            self.dictionary_args.append( f'ActionSet2::SpecificAttacking;T{OPP_TEAM_ASSUME}-{i}')\n        self.ActDigitLen = ActDigitLen\n        self.n_act = len(self.dictionary_args)\n\n    def convert_act_arr(self, type, a):\n        return strActionToDigits(self.dictionary_args[a])\n\n    def get_tp_avail_act(self, type):\n        DISABLE = 0\n        ENABLE = 1\n        n_act = len(self.dictionary_args)\n        ret = np.zeros(n_act) + ENABLE\n        for i in range(n_act):\n            args = self.dictionary_args[i]\n            \n            # for all kind of agents\n            if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n            \n            if type == 'RLA_UAV_Support':\n                if args[0] == 'PatrolMoving':       ret[i] = DISABLE\n                if args[0] == 'SpecificAttacking':  ret[i] = DISABLE\n                if args[0] == 'Idle':               ret[i] = DISABLE\n                if args[1] == 'StaticAlert':        ret[i] = ENABLE\n        return ret\n\n    def confirm_parameters_are_correct(self, team, agent_num, opp_agent_num):\n        assert team == self.SELF_TEAM_ASSUME\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert self.SELF_TEAM_ASSUME + self.OPP_TEAM_ASSUME == 1\n        assert opp_agent_num == self.OPP_NUM_ASSUME\n    \n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/random/foundation.py",
    "content": "import numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__\nfrom config import GlobalConfig\n\nclass AlgorithmConfig:\n    preserve = ''\n\n\n# 改变自身颜色的动作 ChangeColor(color_index)\n# 运动动作 MoveToDirection(x, y, z)\n# 提高一段时间的加速度的动作 AccHighLevel4\n# 发射武器动作 FireToWaterDrop(water_drop_uid)\n\n\nclass RandomController(object):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.n_action = GlobalConfig.ScenarioConfig.n_actions\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n        StateRecall['_hook_'] = None\n        return actions, StateRecall \n\n\nclass DummyRandomControllerWithActionSetV1(object):\n\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .actionset import ActionConvertV1Dummy\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.actions_set = ActionConvertV1Dummy(\n            SELF_TEAM_ASSUME=team, \n            OPP_TEAM_ASSUME=(1-team), \n            OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        self.n_action = self.actions_set.n_act\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n\n        act_converted = np.array(\n                            list(map(lambda x: self.actions_set.convert_act_arr(None, x), \n                            actions.flatten()))).reshape(self.n_thread, self.n_agent, self.actions_set.ActDigitLen)\n\n        StateRecall['_hook_'] = None\n        return act_converted, StateRecall \n\n\nclass RandomControllerWithActionSetV2(object):\n\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .actionset import ActionConvertV2\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.actions_set = ActionConvertV2(\n            SELF_TEAM_ASSUME=team, \n            OPP_TEAM_ASSUME=(1-team), \n            OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        self.n_action = self.actions_set.n_act\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n\n        act_converted = np.array(\n                            list(map(lambda x: self.actions_set.convert_act_arr(None, x), \n                            actions.flatten()))).reshape(self.n_thread, self.n_agent, self.actions_set.ActDigitLen)\n\n        StateRecall['_hook_'] = None\n        return act_converted, StateRecall \n\n\nclass RandomControllerWithActionSetV4(object):\n\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .actionset import ActionConvertMovingV4\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.actions_set = ActionConvertMovingV4(\n            SELF_TEAM_ASSUME=team, \n            OPP_TEAM_ASSUME=(1-team), \n            OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        self.n_action = self.actions_set.n_act\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n\n        act_converted = np.array(\n                            list(map(lambda x: self.actions_set.convert_act_arr(None, x), \n                            actions.flatten()))).reshape(self.n_thread, self.n_agent, self.actions_set.ActDigitLen)\n\n        StateRecall['_hook_'] = None\n        return act_converted, StateRecall \n\n\n\nclass RandomControllerWithActionSetV1(object):\n\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .actionset import ActionConvertV1Carrier\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.actions_set = ActionConvertV1Carrier(\n            SELF_TEAM_ASSUME=team, \n            OPP_TEAM_ASSUME=(1-team), \n            OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        self.n_action = self.actions_set.n_act\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n\n        act_converted = np.array(\n                            list(map(lambda x: self.actions_set.convert_act_arr(None, x), \n                            actions.flatten()))).reshape(self.n_thread, self.n_agent, self.actions_set.ActDigitLen)\n\n        StateRecall['_hook_'] = None\n        return act_converted, StateRecall \n    \n\nclass RandomControllerWithMomentumAgent(object):\n\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from .actionset import ActionConvertV1Momentum\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.space = space\n        self.mcv = mcv\n        self.actions_set = ActionConvertV1Momentum(\n            SELF_TEAM_ASSUME=team, \n            OPP_TEAM_ASSUME=(1-team), \n            OPP_NUM_ASSUME=GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM[1-team]\n        )\n        self.n_action = self.actions_set.n_act\n\n    def interact_with_env(self, StateRecall):\n        obs = StateRecall['Latest-Obs']\n        P = StateRecall['ENV-PAUSE']\n\n        active_thread_obs = obs[~P]\n        actions = np.random.randint(low=0,high=self.n_action, size=(self.n_thread, self.n_agent, 1))\n\n        act_converted = np.array(\n                            list(map(lambda x: self.actions_set.convert_act_arr(None, x), \n                            actions.flatten()))).reshape(self.n_thread, self.n_agent, self.actions_set.ActDigitLen)\n\n        StateRecall['_hook_'] = None\n        return act_converted, StateRecall "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/a_attackpost.py",
    "content": "import numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__\nfrom config import GlobalConfig\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\n\nclass AlgorithmConfig:\n    preserve = ''\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team = team\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\nclass AttackPostPreprogramBaseline(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n        self_agent_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n            x_arr = np.array([d['agentLocationArr'][0] for d in np.array(State_Recall['Latest-Team-Info'][thread]['dataArr'])[self_agent_uid_range]])\n            x_arr_valid = np.array([x for x in x_arr if np.isfinite(x)])\n            x_avg = x_arr_valid.mean()\n            for index, x in enumerate(x_arr):\n                if not np.isfinite(x): pass\n\n                if x > x_avg-1000:\n                    actions[thread, index] = strActionToDigits(f'ActionSet2::SpecificAttacking;T1-0')\n                else:\n                    actions[thread, index] = strActionToDigits(f'ActionSet2::Idle;DynamicGuard')\n\n            # actions[thread, :] = strActionToDigits(f'ActionSet2::SpecificAttacking;T1-0')\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        return actions, {}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/a_escape.py",
    "content": "import numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__\nfrom config import GlobalConfig\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\n\nclass AlgorithmConfig:\n    preserve = ''\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team = team\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\nclass EscapeGreenPreprogramBaseline(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n        self_agent_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n            x_arr = np.array([d['agentLocationArr'][0] for d in np.array(State_Recall['Latest-Team-Info'][thread]['dataArr'])[self_agent_uid_range]])\n            x_arr_valid = np.array([x for x in x_arr if np.isfinite(x)])\n            x_avg = x_arr_valid.mean()\n            for index, x in enumerate(x_arr):\n                if not np.isfinite(x): pass\n\n                actions[thread, index] = strActionToDigits(f'ActionSet2::SpecificAttacking;T1-0')\n\n            # actions[thread, :] = strActionToDigits(f'ActionSet2::SpecificAttacking;T1-0')\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        return actions, {}\n    \nclass EscapeRedPreprogramBaseline(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n        self_agent_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n            x_arr = np.array([d['agentLocationArr'][0] for d in np.array(State_Recall['Latest-Team-Info'][thread]['dataArr'])[self_agent_uid_range]])\n            x_arr_valid = np.array([x for x in x_arr if np.isfinite(x)])\n            x_avg = x_arr_valid.mean()\n            for index, x in enumerate(x_arr):\n                if not np.isfinite(x): pass\n                if index <2:\n                    actions[thread, index] = strActionToDigits(f'ActionSet4::MoveToDirection;X=-1.0 Y=0.0 Z=0.0')\n                else:\n                    actions[thread, index] = strActionToDigits(f'ActionSet4::MoveToDirection;X=+1.0 Y=0.0 Z=0.0')\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        return actions, {}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/a_test_reproduce.py",
    "content": "import numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.tensor_ops import my_view, __hash__\nfrom config import GlobalConfig\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\n\nclass AlgorithmConfig:\n    preserve = ''\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team = team\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\npre_def_color = [\n    '(R=1,G=0,B=0,A=1)', \n    '(R=0,G=1,B=0,A=1)', \n    '(R=0,G=0,B=1,A=1)', \n]\n\nsel_l =  [\n    [-8, -8, -4, -3, -5, -5, -4, -2, 0, 0, 1, 2, 3, 0, 4, 4, 3, 5, 6, 8, -7, -5, -6, -6, -3, -3, -2, -1, -1, 0, 1, 1, 0, 4, 5, 6, 5, 6, 6, 4, -7, -6, -5, -6, -3, -4, -3, -2, -1, 0, 0, -1, 0, 1, 4, 3, 5, 6, 5, 6, -7, -8, -5, -4, -4, -2, -1, 0, 0, -1, 0, 1, 2, 0, 3, 5, 2, 4, 4, 8, -7, -6, -5, -6, -3, -4, 0, -2, -1, 0, -1, -1, 1, 2, 1, 2, 6, 4, 3, 5, -7, -6, -5, -4, -4, -3, -3, -2, -3, 1, 1, 1, 3, 2, 2, 6, 5, 3, 5, 7, -5, -5, -2, -3, -4, -2, -4, -1, 0, -1, 0, 0, 2, 1, 5, 1, 2, 3, 6, 6, -7, -6, -7, -5, -4, -1, -2, -5, -2, -1, -1, 1, 1, 4, 3, 4, 4, 4, 5, 7, -8, -5, -4, -2, -3, -1, 0, -1, 1, -1, 2, -1, 3, 1, 0, 2, 5, 4, 4, 5, -6, -5, -3, -4, -3, -4, -2, 0, -1, -3, 0, 2, 2, -1, 2, 5, 5, 3, 5, 4],\n    [-5, -6, -3, -4, -3, -4, -2, -2, -1, 1, -1, 2, 1, 2, 5, 4, 3, 2, 5, 8, -8, -8, -6, -2, -3, -2, -3, -1, 1, -2, 2, 1, 1, 3, 3, 3, 3, 4, 7, 6, -6, -5, -5, -7, -2, -2, -2, -4, -2, -1, 0, 1, 2, 2, 5, 3, 7, 5, 4, 7, -8, -8, -3, -4, -4, -4, -3, -3, -2, 1, -2, 1, 2, 1, 2, 4, 4, 5, 6, 7, -7, -6, -4, -3, -4, -3, -1, 1, -1, 0, 0, 0, 4, 2, 2, 3, 4, 5, 5, 5, -5, -5, -5, -3, -2, -2, -3, -2, -1, 0, 0, 2, 3, 3, 3, 2, 5, 5, 4, 6, -8, -6, -6, -3, -3, -2, 0, -2, -1, 2, 0, 2, 2, 2, 3, 2, 1, 4, 4, 7, -8, -6, -6, -3, -2, -3, -2, -1, 1, -1, 2, 3, 1, 2, 2, 3, 2, 3, 3, 8, -6, -6, -5, -2, -2, -2, -2, -1, 0, -1, -1, 2, 3, 2, 0, 3, 3, 5, 6, 8, -7, -5, -3, -5, -5, -4, -1, -2, 0, 0, 1, 1, 0, 1, 1, 3, 4, 3, 3, 5],\n    [-8, -8, -5, -6, -1, -2, -2, 0, -2, -2, 0, 2, 2, 2, 5, 2, 3, 6, 7, 6, -8, -8, -4, -5, -4, -5, -2, -1, -1, -1, 1, 0, 3, 1, 3, 5, 5, 7, 5, 7, -7, -6, -5, -5, -7, -2, -1, 0, -1, -2, 1, 1, 0, 1, 3, 3, 6, 4, 5, 7, -8, -7, -6, -4, -3, -3, -2, -1, -1, -1, 0, 1, 0, 0, 3, 3, 4, 5, 5, 8, -6, -5, -6, -3, -4, -3, -3, -2, -1, 1, 0, 0, 1, 2, 2, 4, 5, 5, 4, 5, -8, -4, -7, -6, -3, -2, -3, -3, 1, 0, 0, 1, 1, 2, 2, 4, 4, 5, 6, 6, -5, -5, -3, -5, -4, -4, -1, -1, -1, -1, 0, 1, 4, 4, 6, 3, 4, 4, 5, 7, -6, -7, -5, -4, -3, -4, -1, -2, 0, -1, 1, 1, 1, 3, 2, 3, 4, 3, 3, 5, -7, -8, -5, -5, -3, -3, -3, -3, -2, 0, 0, 2, 1, 2, 3, 2, 3, 4, 7, 6, -8, -5, -4, -4, -4, -4, -1, -4, 0, -1, 1, 0, 0, 1, 4, 1, 3, 4, 6, 6],\n    [-7, -6, -4, -6, -4, -4, -4, -2, -2, 1, -1, 1, 3, 3, 3, 4, 3, 6, 6, 8, -7, -5, -6, -7, -4, -3, -4, -2, 0, -1, 0, 2, 2, 0, 3, 4, 5, 5, 6, 7, -7, -6, -7, -3, -4, -3, -1, -5, -1, 0, -1, 1, 1, 2, 2, 3, 5, 5, 8, 6, -6, -6, -6, -4, -3, -2, -4, -2, -2, 2, 1, 1, 2, 0, 3, 4, 5, 5, 5, 7, -7, -5, -4, -3, -7, -2, -2, -2, -1, 0, 1, 1, 1, 4, 4, 4, 5, 4, 4, 6, -5, -5, -5, -4, -2, -3, -4, -1, 0, -1, -2, 1, 0, 2, 3, 3, 5, 6, 7, 6, -7, -5, -5, -2, -3, -3, -3, 1, 0, -2, 0, -1, 2, 2, 3, 4, 4, 4, 6, 7, -8, -6, -6, -4, -4, -2, -2, -2, -2, -1, 0, 0, 1, 2, 2, 3, 4, 2, 5, 5, -6, -4, -5, -4, -3, -3, -1, -1, -2, -2, 0, 1, 2, 2, 4, 5, 6, 5, 6, 5, -7, -5, -4, -2, -3, -4, -2, -2, -2, -1, 2, 1, 1, 2, 3, 3, 4, 4, 4, 6],\n]\nclass TestReproduce(DummyAlgorithmBase):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        super().__init__(n_agent, n_thread, space, mcv, team)\n        self.episode = -1\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        RST = State_Recall['Env-Suffered-Reset']\n        ENV_ACTIVE = ~ENV_PAUSE\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        if all(RST):\n            self.episode += 1\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n        self_agent_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                continue\n            sel_l_ = [] # 1\n            x_arr_ = np.array([d['agentLocationArr'][0] for d in np.array(State_Recall['Latest-Team-Info'][thread]['dataArr'])[self_agent_uid_range]]) # 1\n            for a in range(self.n_agent):\n                sel = sel_l[self.episode][a] # 2\n                sel_ = (x_arr_[a] + 35) // 70 # 1\n                sel_l_.append(int(sel_)) # 1\n                actions[thread, a] = strActionToDigits(f'ActionSet1::ChangeColor;{pre_def_color[int(sel)%3]}')\n\n\n        print(sel_l[self.episode][:10], sel_l_[:10])\n        print(sel_l_)\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        return actions, {}\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/assignment.py",
    "content": "import copy\nimport random\nimport numpy as np\nimport datetime\nimport time\nfrom ALGORITHM.script_ai.module_evaluation import *\nfrom ALGORITHM.script_ai.global_params import *\n\n\nclass TaskAssign(object):\n\t\"\"\"docstring for TaskAssign\"\"\"\n\tdef __init__(self, attackers, drone, defenders):\n\t\tsuper(TaskAssign, self).__init__()\n\t\tself.attackers = attackers\n\t\tself.drone = drone\n\t\tself.defenders = defenders\n\n\t\tself.evaluator = Evaluation_module()\n\n\t\t# params\n\t\tself.ratio_thres = 1\n\n\t# UGV attack(get defender target id)\n\tdef assign_attack(self, attack_IDlist):\n\t\t# simple strategy -- one opponent\n\t\tmax_stlist = []\n\t\tmax_idlist = []\n\t\tdef_idlist = []\n\t\tfor ID, attr in self.defenders.items():\n\t\t\tif 'dead' not in attr['state']:\n\t\t\t\tdef_idlist.append(ID)\n\t\tfor att in attack_IDlist:\n\t\t\tinner_stance_list = [self.evaluator.UAV2UAV_id('offensive', self.attackers[att], defender) for defender in self.defenders.values() if 'dead' not in defender['state']]\n\t\t\t# print(inner_stance_list)\n\t\t\tinner_max_stance = max(inner_stance_list)\n\t\t\tmax_stlist.append(inner_max_stance)\n\t\t\tmax_idlist.append(inner_stance_list.index(inner_max_stance))\n\n\t\tmax_stance = max(max_stlist)\n\t\ttarget_id = def_idlist[max_idlist[max_stlist.index(max_stance)]]\n\t\treturn target_id\n\n\n\t# # UAV hold(hold 1)\n\t# def assign_drone_initial(self, ):\n\t# \t# check for key points\n\t# \tall_defender_pos = []\n\t# \tmin_dist = []\n\t# \tfor attr in self.defenders.values():\n\t# \t\tall_defender_pos.append([attr['X'], attr['Y']])\n\n\t# \tif len(all_defender_pos)>0:\n\t# \t\tfor key_point in key_points:\n\t# \t\t\tmin_dist.append(min([np.linalg.norm(np.array(key_point)-np.array(enemy_pos)) for enemy_pos in all_defender_pos]))\n\n\t# \t\tif max(min_dist) > DRIVE_AWAY_DIST:\n\t# \t\t\treturn ['running', min_dist.index(max(min_dist))]\n\t# \t\telse:\n\t# \t\t\treturn ['running', 0]\n\t# \telse:\n\t# \t\treturn ['running', 0]\n\n\n\t# UAV hold(running back and forth)\n\tdef assign_drone_ini(self, ):\n\t\t# check for key points\n\t\tall_defender_pos = []\n\t\tmin_dist = []\n\t\tfor attr in self.defenders.values():\n\t\t\tall_defender_pos.append([attr['X'], attr['Y']])\n\n\t\tif len(all_defender_pos) > 0:\n\t\t\tfor key_point in key_points:\n\t\t\t\tmin_dist.append(\n\t\t\t\t\tmin([np.linalg.norm(np.array(key_point) - np.array(enemy_pos)) for enemy_pos in all_defender_pos]))\n\t\t\treturn ['running', min_dist.index(max(min_dist))]\n\t\telse:\n\t\t\treturn ['running', 0]\n\t\t# all_defender_pos = []\n\t\t# drone_dicvalue=[]\n\t\t# drone_pos=[]\n\t\t# min_dist = []\n\t\t# for attr in self.defenders.values():\n\t\t# \tall_defender_pos.append([attr['X'], attr['Y']])\n\t\t# for attr in self.drone.values():\n\t\t# \tdrone_dicvalue.append(attr)\n\t\t# drone_pos.append([drone_dicvalue[2],drone_dicvalue[3]])\n\t\t# drone_pos.append([drone_dicvalue[2], drone_dicvalue[3]])\n\t\t# if len(all_defender_pos)>0:\n\t\t# \tfor key_point in key_points:\n\t\t# \t\tdrone2point=[np.linalg.norm(np.array(key_point) - np.array(pos)) for pos in drone_pos]\n\t\t# \t\tdefender2point=[np.linalg.norm(np.array(key_point) - np.array(enemy_pos)) for enemy_pos in all_defender_pos]\n\t\t# \t\tmin_dist.append(min([a/b for a,b in zip(drone2point,defender2point)]))\n\t\t# \treturn ['running', min_dist.index(min(min_dist))]\n\t\t# else:\n\t\t# \treturn ['running', 0]\n\n\n\tdef judge_expeled(self, ):\n\n\t\tall_defender_pos = []\n\t\t# drone_pos = [self.drone['X'], self.drone['Y']]\n\t\tkey_point_idx = self.drone['state'][1]\n\t\tkey_point_pos = key_points[key_point_idx]\n\n\t\tfor attr in self.defenders.values():\n\t\t\tall_defender_pos.append([attr['X'], attr['Y']])\n\n\t\tmin_dist = min([np.linalg.norm(np.array(key_point_pos) - np.array(def_pos)) for def_pos in all_defender_pos])\n\n\t\tif min_dist < DRIVE_AWAY_DIST:\n\t\t\treturn True\n\t\telse:\n\t\t\treturn False\n\n\n\t# # UGV defend(negetive defend)\n\t# def assign_defend(self, def_ID):\n\t# \tdefend = self.defenders[def_ID]\n\t# \tdefend_pos = [defend['X'], defend['Y']]\n\n\t# \tall_attacker_pos = []\n\t# \tall_attacker_ids = []\n\t# \tfor ID, attr in self.attackers.items():\n\t# \t\tall_attacker_pos.append([attr['X'], attr['Y']])\n\t# \t\tall_attacker_ids.append(ID)\n\n\t# \tif len(all_attacker_pos) > 0:\n\t# \t\tdist_list = [np.linalg.norm(np.array(defend_pos)-np.array(attack_pos)) for attack_pos in all_attacker_pos]\n\t# \t\tmin_dist = min(dist_list)\n\t# \t\tif min_dist < DEFEND_DIST:\n\t# \t\t\treturn ['attack', all_attacker_ids[dist_list.index(min_dist)]]\n\t# \t\telse:\n\t# \t\t\treturn None\n\t# \telse:\n\t# \t\treturn None\n\n\n\t# UGV defend(active defend)\n\t# def defend(self, def_ID):\n\n\t\t# attack_ids = [ID for ID, attr in self.attackers.items() if 'dead' not in attr['state']]\n\t\t# defend_ids = [ID for ID, attr in self.defenders.items() if 'dead' not in attr['state']]\n\t\t#\n\t\t# alive_attack = len(attack_ids)\n\t\t# alive_defend = len(defend_ids)\n\t\t#\n\t\t# if alive_attack > 0 and alive_defend > 0 and def_ID in defend_ids:\n\t\t# \tif alive_attack == alive_defend:\n\t\t# \t\tidx = defend_ids.index(def_ID)\n\t\t# \t\treturn ['attack', attack_ids[idx]]\n\t\t# \telif alive_attack < alive_defend:\n\t\t# \t\treturn ['attack', attack_ids[0]]\n\t\t# \telse:\n\t\t# \t\treturn ['attack', attack_ids[0]]\n\t\t# else:\n\t\t# \treturn ['idle']\n\n\t\t# def2att_ids = [defender['state'][1] for defender in self.defenders.values() if defender['state'][0] == 'attack']\n\t\t# avail_ids = [ID for ID in attack_ids if ID not in def2att_ids]\n\t\t#\n\t\t# if len(avail_ids)>0:\n\t\t# \treturn ['attack', avail_ids[0]]\n\t\t# else:\n\t\t# \treturn ['attack', def2att_ids[0]]\n\n\t# # UGV expel(nearest assign)\n\t# def assign_expel(self, def_ID):\n\t# \tdrone_pos = [self.drone['X'], self.drone['Y']]\n\t# \tdrone_min_dist = []\n\t# \tfor key_point in key_points:\n\t# \t\tdrone_min_dist.append(np.linalg.norm(np.array(key_point)-np.array(drone_pos)))\n\n\t# \tif min(drone_min_dist) < DRIVE_AWAY_DIST:\n\t# \t\tmin_dist = 1000\n\t# \t\tmin_id = list(self.defenders.keys())[0]\n\t# \t\tkey_point_idx = drone_min_dist.index(min(drone_min_dist))\n\t# \t\tfor ID, attr in self.defenders.items():\n\t# \t\t\texpel_dist = np.linalg.norm(np.array(key_points[key_point_idx])-np.array([attr['X'], attr['Y']]))\n\t# \t\t\tif expel_dist < min_dist:\n\t# \t\t\t\tmin_dist = expel_dist\n\t# \t\t\t\tmin_id = ID\n\t# \t\tif def_ID == min_id:\n\t# \t\t\treturn ['expel', key_point_idx]\n\t# \t\telse:\n\t# \t\t\treturn None\n\t# \telse:\n\t# \t\treturn None\n\n\t# UGV expel(both)\n\tdef expel(self, ):\n\t\tdrone_pos = [self.drone['X'], self.drone['Y']]\n\t\tdrone_dist = []\n\t\tfor key_point in key_points:\n\t\t\tdrone_dist.append(np.linalg.norm(np.array(key_point)-np.array(drone_pos)))\n\t\t# if min(drone_dist) < DRIVE_AWAY_DIST:\n\t\treturn ['expel', drone_dist.index(min(drone_dist))]\n\t\t# else:\n\t\t# \treturn None\n\n\t# defender assign\n\tdef assign_defend(self, def_ID):\n\n\t\talive_attackers = [attacker for attacker in self.attackers.values() if 'dead' not in attacker['state']]\n\t\talive_attackers_ids = [ID for ID, attr in self.attackers.items() if 'dead' not in attr['state']]\n\t\tdist = [np.linalg.norm(np.array([att['X'], att['Y']]) - np.array([self.defenders[def_ID]['X'], self.defenders[def_ID]['Y']])) for att in alive_attackers]\n\n\t\tif len(dist) > 0 and min(dist) < DEFEND_DIST:\n\t\t\tidx = dist.index(min(dist))\n\t\t\treturn ['attack', alive_attackers_ids[idx]]\n\t\telse:\n\t\t\treturn self.expel()\n\n\t\t# alive_attacker_list = [att for att in self.attackers.values() if 'dead' not in att['state']]\n\t\t#\n\t\t# if len(alive_attacker_list) > 0:\n\t\t# \tUGV_stance = [self.evaluator.UAV2UAV_id('offensive', attacker, self.defenders[def_ID]) for attacker in alive_attacker_list]\n\t\t# \t# print(inner_stance_list)\n\t\t# \tmax_UGV_stance = max(UGV_stance)\n\t\t#\n\t\t# \tdrone_stance = [self.evaluator.Drone2Point_id(self.drone, keyPoint) for keyPoint in key_points]\n\t\t# \tmax_drone_stance = max(drone_stance)\n\t\t#\n\t\t# \tprint('UGV stance: ', max_UGV_stance)\n\t\t# \tprint('drone stance: ', max_drone_stance)\n\t\t# \tprint('ratio: ', max_UGV_stance/max_drone_stance)\n\t\t#\n\t\t# \tratio = max_UGV_stance/max_drone_stance\n\t\t#\n\t\t# \tif ratio > self.ratio_thres:\n\t\t# \t\tassigned_state = self.defend(def_ID)\n\t\t# \telse:\n\t\t# \t\tassigned_state = self.expel()\n\t\t#\n\t\t# else:\n\t\t# \tassigned_state = self.expel()\n\t\t#\n\t\t# return assigned_state\n\n\t# judge retreat for attackers\n\tdef is_retreat(self, att_ID, def_ID):\n\t\tif 'dead' in self.attackers[att_ID]['state'] or 'dead' in self.defenders[def_ID]['state']:\n\t\t\treturn False\n\t\telse:\n\t\t\t# # dist version\n\t\t\t# attacker_pos = [self.attackers[att_ID]['X'], self.attackers[att_ID]['Y']]\n\t\t\t# dist_list = [np.linalg.norm(np.array(attacker_pos) - np.array([defender['X'], defender['Y']])) for defender \\\n\t\t\t# \t\t\t in self.defenders.values()]\n\t\t\t# if min(dist_list) < 800:\n\t\t\t# \treturn True\n\t\t\t# else:\n\t\t\t# \treturn False\n\n\t\t\t# stance_version\n\t\t\tstance = self.evaluator.UAV2UAV_id('offensive', self.attackers[att_ID], self.defenders[def_ID])\n\t\t\tif stance > RETREAT_STANCE:\n\t\t\t\tprint('retreat stance: ', stance)\n\t\t\t\treturn True\n\t\t\telse:\n\t\t\t\treturn False\n\n\n\tdef is_attack(self, att_ID):\n\t\tif 'dead' in self.attackers[att_ID]['state']:\n\t\t\treturn False\n\t\telse:\n\t\t\t# # dist version\n\t\t\t# attacker_pos = [self.attackers[att_ID]['X'], self.attackers[att_ID]['Y']]\n\t\t\t# dist_list = [np.linalg.norm(np.array(attacker_pos) - np.array([defender['X'], defender['Y']])) for defender \\\n\t\t\t# \t\t\t in self.defenders.values()]\n\t\t\t# if min(dist_list) > 1000:\n\t\t\t# \treturn True\n\t\t\t# else:\n\t\t\t# \treturn False\n\n\t\t\t# stance version\n\t\t\tstance_list = [self.evaluator.UAV2UAV_id('offensive', self.attackers[att_ID], self.defenders[def_ID]) for def_ID in \\\n\t\t\t\t\t\t   self.defenders.keys() if 'dead' not in self.defenders[def_ID]['state']]\n\t\t\tif max(stance_list) < RETREAT_STANCE:\n\t\t\t\tprint('attack stance: ', max(stance_list))\n\t\t\t\treturn True\n\t\t\telse:\n\t\t\t\tprint('retreat stance: ', max(stance_list))\n\t\t\t\treturn False\n\n# test\n# if __name__ == '__main__':\n#\n# \t# assigner = TaskAssign()\n# \t# attack_goals, defend_goals, avoid_goals, uav_point = align.assign_all(ally_agents_data, enemy_agents_data, key_points)"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/blue_strategy.py",
    "content": "import numpy as np\nimport math\n\n\ndef offense_combat(self_data, ally_agents_data, enemy_agents_data, key_points, blue_alive, red_alive,\n                   agent_type):\n    # 防守方red小车最大速度\n    red_car_max_vel = 600\n    # 进攻方blue小车最大速度\n    blue_car_max_vel = 600\n    # 进攻方blue无人机最大速度\n    blue_drone_max_vel = 600\n    # 进攻方无人机占领夺控点胜利时间\n    time_to_win = 2.0\n    # 驱离载荷作用范围\n    expel_range = 1200\n    # 无人车打击距离\n    fire_dist = 2000\n\n    all_enemy_agent_pos = []\n    for agent_id, dict_value in enemy_agents_data.items():\n        all_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n    \"\"\"\n    all_enemy_agent_yaw = []\n    for agent_id, dict_value in enemy_agents_data.items():\n        if agent_id != '231':\n            all_enemy_agent_yaw.append([dict_value['Yaw']])\n    \"\"\"\n    blue_drone_current_pos = np.array([[ally_agents_data['231']['X'], ally_agents_data['231']['Y'], ally_agents_data['231']['Z']]])\n    ally_agents_data.pop('231')\n    if '211' in self_data.keys():\n        friend_agents_data = dict(self_data, **ally_agents_data)\n    if '211' in ally_agents_data.keys():\n        friend_agents_data = dict(ally_agents_data, **self_data)\n    all_friend_agent_pos = []\n    for agent_id, dict_value in friend_agents_data.items():\n        all_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n    \"\"\"\n    all_friend_agent_yaw = []\n    for agent_id, dict_value in friend_agents_data.items():\n        all_friend_agent_yaw.append([dict_value['Yaw']])\n    \"\"\"\n\n    target_location = np.array(key_points)\n    red_car_current_pos = np.array(all_enemy_agent_pos)\n    blue_car_current_pos = np.array(all_friend_agent_pos)\n    blue_car_current_pos = blue_car_current_pos\n\n    if agent_type == 0:\n        drone_keypoint_relative_dist = np.array([[np.linalg.norm(blue_drone_current_pos[0][:2] - target_location[b][:2])\n                                                  for b in range(2)]])\n        if red_alive[0] is True and red_alive[1] is True:\n            dist_red_car_blue_drone = np.array(\n                [[np.linalg.norm(red_car_current_pos[a][:2] - blue_drone_current_pos[0][:2])]\n                 for a in range(2)])\n            if np.min(dist_red_car_blue_drone) < expel_range:# and np.argmin(drone_keypoint_relative_dist) < 600:\n                flag = 'defense'\n            else:\n                flag = 'offense'\n        elif red_alive[0] is True and red_alive[1] is False:\n            dist_red_car_blue_drone = np.linalg.norm(red_car_current_pos[0][:2] - blue_drone_current_pos[0][:2])\n            if dist_red_car_blue_drone < expel_range:# and np.argmin(drone_keypoint_relative_dist) < 600:\n                flag = 'defense'\n            else:\n                flag = 'offense'\n        elif red_alive[0] is False and red_alive[1] is True:\n            dist_red_car_blue_drone = np.linalg.norm(red_car_current_pos[1][:2] - blue_drone_current_pos[0][:2])\n            if dist_red_car_blue_drone < expel_range:# and np.argmin(drone_keypoint_relative_dist) < 600:\n                flag = 'defense'\n            else:\n                flag = 'offense'\n        elif red_alive[0] is False and red_alive[1] is False:\n            flag = 'offense'\n        if flag == 'offense':\n            if blue_alive[0] is True or blue_alive[1] is True:\n                # 无人车\n                if blue_alive[0] is True and blue_alive[1] is True:\n                    center_x = (blue_car_current_pos[0][0] + blue_car_current_pos[1][0]) / 2\n                    center_y = (blue_car_current_pos[0][1] + blue_car_current_pos[1][1]) / 2\n                    center_z = (blue_car_current_pos[0][2] + blue_car_current_pos[1][2]) / 2\n                elif blue_alive[0] is True and blue_alive[1] is False:\n                    center_x = blue_car_current_pos[0][0]\n                    center_y = blue_car_current_pos[0][1]\n                    center_z = blue_car_current_pos[0][2]\n                elif blue_alive[0] is False and blue_alive[1] is True:\n                    center_x = blue_car_current_pos[1][0]\n                    center_y = blue_car_current_pos[1][1]\n                    center_z = blue_car_current_pos[1][2]\n\n                blue_center = np.array([[center_x, center_y, center_z]])\n\n                if red_alive[0] is True and red_alive[1] is True:\n                    dist = np.array([[np.linalg.norm(blue_center[0][:2] - red_car_current_pos[b][:2])\n                                      for b in range(2)]])\n                    target_pos = red_car_current_pos[np.argmin(dist)]\n                    target_id = np.argmin(dist)\n                elif red_alive[0] is True and red_alive[1] is False:\n                    target_pos = red_car_current_pos[0]\n                    target_id = 0\n                elif red_alive[0] is False and red_alive[1] is True:\n                    target_pos = red_car_current_pos[1]\n                    target_id = 1\n                elif red_alive[0] is False and red_alive[1] is False:\n                    target_pos = target_location[0]\n                    target_id = 1\n            else:\n                target_pos = blue_car_current_pos[0]\n                target_id = 0\n        else:\n            target_pos = blue_drone_current_pos[0]\n            target_pos[0] = target_pos[0] + (-2000 * np.sign(blue_drone_current_pos[0][0]))\n            target_pos[1] = target_pos[1] + (-3000 * np.sign(blue_drone_current_pos[0][1]))\n            target_pos[2] = blue_car_current_pos[0][2]\n            target_id = 0\n        return target_pos, target_id, flag\n    elif agent_type == 1:\n        # 无人机\n        drone_keypoint_relative_dist = np.array([[np.linalg.norm(blue_drone_current_pos[0][:2] - target_location[b][:2])\n                                                  for b in range(2)]])\n        safe_target_location = []\n        for index in range(2):\n            if red_alive[0] is True and red_alive[1] is True:\n                dist = np.array([[np.linalg.norm(target_location[index][:2] - red_car_current_pos[b][:2])\n                                  for b in range(2)]])\n                if np.min(dist) > expel_range:\n                    safe_target_location.append(True)\n                else:\n                    safe_target_location.append(False)\n            elif red_alive[0] is True and red_alive[1] is False:\n                dist = np.linalg.norm(target_location[index][:2] - red_car_current_pos[0][:2])\n                if dist > expel_range:\n                    safe_target_location.append(True)\n                else:\n                    safe_target_location.append(False)\n            elif red_alive[0] is False and red_alive[1] is True:\n                dist = np.linalg.norm(target_location[index][:2] - red_car_current_pos[1][:2])\n                if dist > expel_range:\n                    safe_target_location.append(True)\n                else:\n                    safe_target_location.append(False)\n            elif red_alive[0] is False and red_alive[1] is False:\n                safe_target_location = [True, True]\n\n        if safe_target_location[0] is True and safe_target_location[1] is True:\n            red_car_dist = np.array(\n                [[np.linalg.norm(red_car_current_pos[a][:2] - target_location[b][:2]) - expel_range for b in range(2)]\n                 for a in range(2)])\n            if red_alive[0] is False:\n                red_car_dist[0] = 100000\n            elif red_alive[1] is False:\n                red_car_dist[1] = 100000\n\n            target_defense_index = np.argmax(np.max(red_car_dist, axis=0, keepdims=True))\n            blue_drone_dist_to_go = np.array(\n                [[np.linalg.norm(blue_drone_current_pos[a][:2] - target_location[b][:2]) for b in range(2)]\n                 for a in range(1)])\n            # return target_location[np.argmin(blue_drone_dist_to_go)]\n            return target_location[target_defense_index]\n        elif safe_target_location[0] is True and safe_target_location[1] is False:\n            return target_location[0]\n        elif safe_target_location[0] is False and safe_target_location[1] is True:\n            return target_location[1]\n        elif safe_target_location[0] is False and safe_target_location[1] is False:\n            return np.array([(target_location[0][0] + target_location[0][1]) / 2,\n                             (target_location[1][0] + target_location[1][1]) / 2])\n\n\nif __name__ == '__main__':\n    agent_type = 0\n    # 211和221是进攻方blue小车， 231是进攻方blue无人机\n    # 311和321是防守方red小车\n    # 当前要决策的blue小车的信息\n    self_data = {'211': {'X': 1500, 'Y': -2000, 'Z': 0, 'Yaw': 30, 'Blood': 100}}\n    ally_agents_data = {'221': {'X': -2500, 'Y': -2500, 'Z': 0, 'Yaw': 40, 'Blood': 100},\n                        '231': {'X': 700, 'Y': 3300, 'Z': 1500, 'Yaw': 0}}\n    # 进攻方blue小车和无人机信息\n    enemy_agents_data = {'311': {'X': 2700.0, 'Y': 3300, 'Z': 0, 'Yaw': 20, 'Blood': 100},\n                         '321': {'X': -1000, 'Y': -700, 'Z': 0, 'Yaw': 10, 'Blood': 100}}\n    # 夺控点信息\n    key_points = [[700, 3300, 0], [-2500, -700, 0]]\n    # 存活状态\n    blue_alive = [True, True]\n    red_alive = [True, True]\n    if agent_type == 0:\n        target_position, target_id, flag = offense_combat(self_data, ally_agents_data, enemy_agents_data,\n                                                          key_points, blue_alive, red_alive, agent_type)\n        print(target_position)\n        print('\\r\\n')\n        print(target_id)\n        print('\\r\\n')\n        print(flag)\n    elif agent_type == 1:\n        target_position = offense_combat(self_data, ally_agents_data, enemy_agents_data,\n                                         key_points, blue_alive, red_alive, agent_type)\n        print(target_position)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/decision.py",
    "content": "import copy\nimport random\nimport numpy as np\nimport datetime\nimport time\nfrom ALGORITHM.script_ai.assignment import *\nfrom ALGORITHM.script_ai.global_params import *\n\n\n\nclass decision():\n\t\"\"\"docstring for decision\"\"\"\n\tdef __init__(self, attackers, drone, defenders):\n\t\tsuper(decision, self).__init__()\n\t\tself.attackers = attackers\n\t\tself.drone = drone\n\t\tself.defenders = defenders\n\n\t\tself.assigner = TaskAssign(self.attackers, self.drone, self.defenders)\n\n\n\t# output actions\n\tdef act(self, type=None):\n\t\tactions_list = {}\n\n\t\tself.alive_attack = len([att for att in list(self.attackers.values()) if 'dead' not in att['state']])\n\t\tself.alive_defend = len([ded for ded in list(self.defenders.values()) if 'dead' not in ded['state']])\n\n\t\tif not type:\n\t\t\ttype = 'attackers'\n\n\t\tif type == 'attackers':\n\t\t\tself.attack_StateTrans()\n\n\t\t\tfor ID, attr in self.attackers.items():\n\n\t\t\t\tif 'dead' in attr['state']:\n\t\t\t\t\tdes_pos = [attr['X'], attr['Y'], attr['Z']]\n\t\t\t\t\tactions_list[ID] = des_pos\n\t\t\t\t\tcontinue\n\n\t\t\t\tif 'attack' in attr['state'] and attr['state'][1] is not '0':\n\t\t\t\t\tdef_ID = attr['state'][1]\n\t\t\t\t\topp = self.defenders[def_ID]\n\t\t\t\t\tdes_pos = [opp['X']-400, opp['Y']-400, opp['Z']]\n\t\t\t\t\tactions_list[ID] = des_pos\n\n\t\t\t\telif 'retreat' in attr['state']:\n\t\t\t\t\tif self.drone['state'][0] is not 'idle':\n\t\t\t\t\t\tselect_key_points = key_points[self.drone['state'][1]]\n\t\t\t\t\t\tagent_pos = [attr['X'], attr['Y'], attr['Z']]\n\t\t\t\t\t\tif select_key_points[1] - agent_pos[1] > 50 and select_key_points[0] - agent_pos[0] > 50:\n\t\t\t\t\t\t\tk = (select_key_points[1] - agent_pos[1]) / (select_key_points[0] - agent_pos[0])\n\t\t\t\t\t\t\tdes_pos = [agent_pos[0] - 1400, agent_pos[1] - 1400 * k, agent_pos[2]]\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\tdes_pos = [agent_pos[0] - 1400, agent_pos[1], agent_pos[2]]\n\t\t\t\t\telse:\n\t\t\t\t\t\tdes_pos = ATTA_RETREAT_POS\n\t\t\t\t\tactions_list[ID] = des_pos\n\t\t\t\t\t# des_pos = ATTA_RETREAT_POS\n\t\t\t\t\t# actions_list[ID] = des_pos\n\n\t\t\t\telif 'idle' in attr['state']:\n\t\t\t\t\tdes_pos = [attr['X'], attr['Y'], attr['Z']]\n\t\t\t\t\tactions_list[ID] = des_pos\n\n\t\t\t# attack target assign\n\t\t\tassign_attackers = [ID for ID, attr in self.attackers.items() if (len(attr['state'])>1 and attr['state'][1] == '0')]\n\n\t\t\t# attacker assignment\n\t\t\tif len(assign_attackers)>0:\n\t\t\t\ttarget_ID = self.assigner.assign_attack(assign_attackers)\n\t\t\t\ttarget = self.defenders[target_ID]\n\n\t\t\tfor attacker_ID in assign_attackers:\n\t\t\t\tactions_list[attacker_ID] = [target['X']-300, target['Y']-300, target['Z']]\n\t\t\t\tself.attackers[attacker_ID]['state'][1] = target_ID\n\n\n\t\t\t# drone action\n\t\t\tself.drone_StateTrans()\n\t\t\tif 'idle' in self.drone['state']:\n\t\t\t\tdes_pos = [self.drone['X'], self.drone['Y'], self.drone['Z']]\n\t\t\telif 'running' in self.drone['state'] or 'hold' in self.drone['state']:\n\t\t\t\tdes_pos = key_points[self.drone['state'][1]]\n\n\t\t\tactions_list['drone'] = des_pos\n\n\n\t\t# same as attackers\n\t\telif type == 'defenders':\n\t\t\tself.defend_StateTrans()\n\n\t\t\tfor ID, attr in self.defenders.items():\n\n\t\t\t\tif 'dead' in attr['state']:\n\t\t\t\t\tdes_pos = [attr['X'], attr['Y'], attr['Z']]\n\t\t\t\t\tactions_list[ID] = des_pos\n\t\t\t\t\tcontinue\n\n\t\t\t\tif 'attack' in attr['state']:\n\t\t\t\t\tdef_ID = attr['state'][1]\n\t\t\t\t\topp = self.attackers[def_ID]\n\t\t\t\t\tdes_pos = [opp['X'], opp['Y'], opp['Z']]\n\n\t\t\t\telif 'expel' in attr['state']:\n\t\t\t\t\tdes_pos = key_points[attr['state'][1]]\n\n\t\t\t\telif 'retreat' in attr['state']:\n\t\t\t\t\tdes_pos = DEF_RETREAT_POS\n\n\t\t\t\telif 'idle' in attr['state']:\n\t\t\t\t\tdes_pos = key_points[0]\n\n\t\t\t\t# else:\n\t\t\t\t# \tdes_pos = [attr['X'], attr['Y'], attr['Z']]\n\n\t\t\t\tactions_list[ID] = des_pos\n\n\n\t\telse:\n\t\t\traise ValueError('invalid type!')\n\n\t\treturn actions_list\n\n\n\n\t# state machine\n\tdef attack_StateTrans(self, ):\n\n\t\t# attackers\n\t\tfor ID in list(self.attackers.keys()):\n\t\t\tattr = self.attackers[ID]\n\n\t\t\t# check alive/dead(blood thres: 2)\n\t\t\tif attr['blood'] <= 2 and 'dead' not in attr['state']:\n\t\t\t\tattr['blood'] = 0\n\t\t\t\tattr['state'] = ['dead']\n\t\t\t\tcontinue\n\n\t\t\t# idle2attack\n\t\t\tif 'idle' in attr['state']:\n\t\t\t\tif self.alive_defend > 0:\n\t\t\t\t\tattr['state'] = ['attack']\n\t\t\t\t\tdef_ID = '0'\n\t\t\t\t\tattr['state'].append(def_ID)\n\n\t\t\t\tcontinue\n\n\t\t\t# attack2idle/retreat(blood thres: 10)\n\t\t\tif 'attack' in attr['state']:\n\t\t\t\tdef_ID = attr['state'][1]\n\t\t\t\tis_attack = self.assigner.is_attack(ID)\n\n\t\t\t\t# 2idle\n\t\t\t\tif 'dead' in self.defenders[def_ID]['state']:\n\t\t\t\t\tattr['state'] = ['attack']\n\t\t\t\t\tdef_ID = '0'\n\t\t\t\t\tattr['state'].append(def_ID)\n\t\t\t\telif not is_attack:\n\t\t\t\t\tattr['state'] = ['retreat']\n\n\t\t\t\tcontinue\n\n\t\t\t# retreat2attack\n\t\t\tif 'retreat' in attr['state']:\n\t\t\t\tif self.alive_defend > 0 and attr['blood'] > 10:\n\t\t\t\t\tis_attack = self.assigner.is_attack(ID)\n\t\t\t\t\tif is_attack:\n\t\t\t\t\t\tattr['state'] = ['attack']\n\t\t\t\t\t\tdef_ID = '0'\n\t\t\t\t\t\tattr['state'].append(def_ID)\n\t\t\t\t\t# dist_list = [np.linalg.norm(np.array([attr['X'], attr['Y'], attr['Z']]) - np.array([ded['X'], ded['Y'], ded['Z']])) for ded in self.defenders.values()]\n\t\t\t\t\t# min_dist = min(dist_list)\n\t\t\t\t\t# if min_dist > 1500:\n\t\t\t\t\t# \tattr['state'] = ['attack']\n\t\t\t\t\t# \tdef_ID = '0'\n\t\t\t\t\t# \tattr['state'].append(def_ID)\n\n\t\t# if 'idle' in self.drone['state']:\n\t\t# self.drone['state'] = self.assigner.assign_2point()\n\n\n\tdef drone_StateTrans(self, ):\n\n\t\tdrone_pos = [self.drone['X'], self.drone['Y']]\n\n\t\t# initial assign\n\t\tif 'idle' in self.drone['state']:\n\t\t\tself.drone['state'] = self.assigner.assign_drone_ini()\n\n\t\t# # run2hold\n\t\t# elif 'running' in self.drone['state']:\n\t\t# \tcur_point_idx = self.drone['state'][1]\n\t\t# \tcur_point_pos = key_points[cur_point_idx]\n\t\t# \tif np.linalg.norm(np.array(drone_pos) - np.array(cur_point_pos)) < 10:\n\t\t# \t\tself.drone['state'] = ['hold', cur_point_idx]\n\t\t# \telse:\n\t\t# \t\tpass\n\t\telif 'running' in self.drone['state']:\n\t\t\tcur_point_idx = self.drone['state'][1]\n\t\t\tcur_point_pos = key_points[cur_point_idx]\n\t\t\tif self.assigner.judge_expeled():\n\t\t\t\tself.drone['state'] = ['running', int(1 - cur_point_idx)]\n\t\t\t\tself.drone['state'] = self.assigner.assign_drone_ini()\n\n\t\t\telse:\n\t\t\t\tpass\n\n\t# defender\n\tdef defend_StateTrans(self, ):\n\t\tfor ID in list(self.defenders.keys()):\n\t\t\tattr = self.defenders[ID]\n\n\t\t\t# check alive/dead\n\t\t\tif attr['blood'] <= 2 and 'dead' not in attr['state']:\n\t\t\t\tattr['blood'] = 0\n\t\t\t\tattr['state'] = ['dead']\n\t\t\t\tcontinue\n\n\t\t\t# # expel nearest\n\t\t\t# if self.assigner.assign_expel(ID) is not None:\n\t\t\t# \tattr['state'] = self.assigner.assign_expel(ID)\n\t\t\t# \tcontinue\n\n\t\t\t# # expel both\n\t\t\t# if self.assigner.assign_expel() is not None:\n\t\t\t# \tattr['state'] = self.assigner.assign_expel()\n\t\t\t# \tcontinue\n\t\t\t#\n\t\t\t# # idle2attack\n\t\t\t# if self.alive_attack>0 and self.assigner.assign_defend() is not None:\n\t\t\t# \tattr['state'] = self.assigner.assign_defend()\n\t\t\t# \tcontinue\n\t\t\tattr['state'] = self.assigner.assign_defend(ID)\n\n\t\t\t# attack2idle/retreat(blood thres: 10)\n\t\t\tif 'attack' in attr['state']:\n\t\t\t\tatt_ID = attr['state'][1]\n\n\t\t\t\t# 2retreat\n\t\t\t\tif attr['blood'] <= 10 and att_ID in self.attackers.keys() and self.attackers[att_ID]['blood'] > 5:\n\t\t\t\t\tattr['state'] = ['retreat']\n\n\t\t\t\t# 2idle\n\t\t\t\telif 'dead' in self.attackers[att_ID]['state']:\n\t\t\t\t\tattr['state'] = ['idle']\n\n\t\t\t# else:\n\t\t\t# \tattr['state'] = self.assigner.assign_defend(ID)\n\n\n\ndef test():\n\n\t# test initial data\n\t# ally_agents_data={\"221\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}, \"231\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":1.5, \"Yaw\":0, 'blood':100, 'state': ['idle']}}\n\t# enemy_agents_data={\"311\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":5, \"Y\":5, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}, \"321\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":6, \"Y\":6, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}}\n\n\t# test dead detection √\n\t# ally_agents_data={\"221\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['idle']}, \"231\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":1.5, \"Yaw\":0, 'blood':1, 'state': ['retreat']}}\n\t# drone_data={\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['idle']}\n\t# enemy_agents_data={\"311\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":5, \"Y\":5, \"Z\":0, \"Yaw\":0, 'blood':1, 'state': ['attack']}, \"321\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":6, \"Y\":6, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['dead']}}\n\n\t# test idle2attack √\n\t# ally_agents_data={\"221\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}, \"231\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":1.5, \"Yaw\":0, 'blood':100, 'state': ['idle']}}\n\t# drone_data={\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['idle']}\n\t# enemy_agents_data={\"311\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":5, \"Y\":5, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}, \"321\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":6, \"Y\":6, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}}\n\n\t# test attack2idle √\n\t# ally_agents_data={\"221\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['attack', '311']}, \"231\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":1.5, \"Yaw\":0, 'blood':100, 'state': ['attack', '311']}}\n\t# drone_data={\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['idle']}\n\t# enemy_agents_data = {}\n\n\t# test attack2retreat √\n\tally_agents_data={\"221\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":1, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':11, 'state': ['attack', '311']}, \"231\": {\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":1.5, \"Yaw\":0, 'blood':10, 'state': ['attack', '311']}}\n\tdrone_data={\"ammo\": 100, \"velocity\": 0.5, \"X\":-3, \"Y\":0, \"Z\":0, \"Yaw\":0, 'blood':0, 'state': ['idle']}\n\tenemy_agents_data={\"311\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":2, \"Y\":1, \"Z\":0, \"Yaw\":0, 'blood':6, 'state': ['idle']}, \"321\": {\"ammo\": 100, \"velocity\": 0.8, \"X\":6, \"Y\":6, \"Z\":0, \"Yaw\":0, 'blood':100, 'state': ['idle']}}\n\t# enemy_agents_data={\"311\":{\"ammo\": 100, \"velocity\": 0.5, \"X\":2, \"Y\":2, \"Z\":0, \"Yaw\":0, 'blood':11, 'state': ['expel']}}\n\n\tDecisionMake = decision(ally_agents_data, drone_data, enemy_agents_data)\n\n\tattackers = DecisionMake.attackers\n\tdefenders = DecisionMake.defenders\n\tdrone = DecisionMake.drone\n\n\t# decision module test\n\tattack_actions = DecisionMake.act(type='attackers')\n\tdefend_actions = DecisionMake.act(type='defenders')\n\n\tprint('attack property: ', attackers)\n\tprint('defend property: ', defenders)\n\n\tatt_states = []\n\tdef_states = []\n\tfor k, v in attackers.items():\n\t\tatt_states.append({k:v['state']})\n\tfor k, v in defenders.items():\n\t\tdef_states.append({k:v['state']})\n\n\tdrone_state = drone['state']\n\n\tprint('attack states: ', att_states)\n\tprint('defend states: ', def_states)\n\tprint('drone states: ', drone_state)\n\n\tprint('attack actions: ', attack_actions)\n\tprint('defend actions: ', defend_actions)\n\n\n\nif __name__ == '__main__':\n\n\ttest()\n\n\t# DecisionMake = decision(ally_agents_data, enemy_agents_data)\n\n\t# while(1):\n\n\t# \tattack_actions = DecisionMake.act(type='attackers')\n\t# \tdefend_actions = DecisionMake.act(type='defenders')\n\n\t# \ttime.sleep(0.05)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/dummy.py",
    "content": "import numpy as np\nfrom UTIL.tensor_ops import copy_clone\n\nclass DummyAlgConfig():\n    reserve = \"\"\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from config import GlobalConfig\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        \n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\nclass DummyAlgorithm(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 4))\n\n        env0_step = State_Recall['Current-Obs-Step']\n        if env0_step%2==0:\n            actions[..., 0] = 1 # AT\n            for i in range(5): actions[:, i, 1] = i # TT\n            actions[..., 2] = 0 # HT\n            actions[..., 3] = 0 # SP\n        else:\n            actions[..., 0] = 5 # AT\n            for i in range(5): actions[:, i, 1] = i # TT\n            actions[..., 2] = 0 # HT\n            actions[..., 3] = 0 # SP\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n    \n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/dummy_uhmap.py",
    "content": "import numpy as np\nfrom UTIL.tensor_ops import copy_clone\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom ALGORITHM.script_ai.decision import decision\nfrom ALGORITHM.script_ai.assignment import *\nfrom ALGORITHM.script_ai.global_params import *\n\nattact_states={'211':['idle'],'221':['idle']}\ndrone_state={'231':['idle']}\ndefend_states={'311':['idle'],'321':['idle']}\nprint(\"===========================\")\nprint(attact_states)\nprint(drone_state)\nprint(defend_states)\nprint(\"===========================\")\nclass DummyAlgConfig():\n    reserve = \"\"\n\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from config import GlobalConfig\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n\n        assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread)\n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n# 进攻方决策\nclass DummyAlgorithmT1(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        try:\n            res = self.interact_with_env_(State_Recall)\n        except:\n            actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n            actions[:] = encode_action_as_digits(\"N/A\", \"N/A\", x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            actions = np.swapaxes(actions, 0, 1)\n            res = (actions, None)\n        return res\n\n\n    def interact_with_env_(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n\n        assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n\n        env0_step = State_Recall['Current-Obs-Step']\n\n        obs = State_Recall['Latest-Team-Info']\n        thread = 0\n        global attact_states\n        global drone_state\n        global defend_states\n        if State_Recall['Env-Suffered-Reset']==[True]:\n             attact_states = {'211': ['idle'], '221': ['idle']}\n             drone_state = {'231': ['idle']}\n             defend_states = {'311': ['idle'], '321': ['idle']}\n        # 防守方red小车的信息\n        red_agents_data = {'311': {'ammo':100, 'velocity':0, 'X': obs[0]['dataArr'][3]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][3]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][3]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][3]['agentHp'], 'state':defend_states['311']},\n                           '321': {'ammo':100, 'velocity':0, 'X': obs[0]['dataArr'][4]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][4]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][4]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][4]['agentHp'], 'state':defend_states['321']}}\n        # 进攻方blue小车和无人机信息，其中231是无人机\n        blue_agents_data = {'211': {'ammo':100, 'velocity':0, 'X': obs[0]['dataArr'][0]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][0]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][0]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][0]['agentHp'], 'state':attact_states['211']},\n                            '221': {'ammo':100, 'velocity':0, 'X': obs[0]['dataArr'][1]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][1]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][1]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][1]['agentHp'], 'state':attact_states['221']}}\n        drone_data={'ammo':100, 'velocity':0, 'X': obs[0]['dataArr'][2]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][2]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][2]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][2]['agentHp'], 'state':drone_state['231']}\n\n        blue_alive = [obs[0]['dataArr'][0]['agentAlive'], obs[0]['dataArr'][1]['agentAlive']]\n        red_alive = [obs[0]['dataArr'][3]['agentAlive'], obs[0]['dataArr'][4]['agentAlive']]\n        # 夺控点信息 在global——params.py修改\n       # key_points = [[700, -3300, 500], [-3000, 700, 500]]\n        DecisionMake = decision(blue_agents_data,drone_data,red_agents_data)\n        attackers = DecisionMake.attackers\n        defenders = DecisionMake.defenders\n        drone = DecisionMake.drone\n        #decision module test\n        attack_actions = DecisionMake.act(type='attackers')\n        defend_actions = DecisionMake.act(type='defenders')\n\n        att_states = []\n        def_states = []\n        for k, v in attackers.items():\n            att_states.append({k: v['state']})\n        for k, v in defenders.items():\n            def_states.append({k: v['state']})\n        # print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')\n        # print(drone['state'])\n        # print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')\n\n        attact_states['211']=att_states[0]['211']\n        attact_states['221'] = att_states[1]['221']\n        defend_states['311'] = def_states[0]['311']\n        defend_states['321'] = def_states[1]['321']\n        drone_state['231'] = drone['state']\n\n        print('+++++++++++++++++++++++++ info +++++++++++++++++++++++++++++++++')\n        print('211 state: ', attact_states['211'])\n        print('221 state: ', attact_states['221'])\n        print('311 state: ', defend_states['311'])\n        print('321 state: ', defend_states['321'])\n        print('drone state: ', drone['state'])\n        print('attack actions: ', attack_actions)\n        print('defend actions: ', defend_actions)\n        print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')\n        # 小车211决策\n        if attact_states['211'][0] == 'attack':\n            if attact_states['211'][1] == '311':\n                # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None,UID=3,T=None, T_index=None)\n                actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['211'][0], y=attack_actions['221'][1], z=500,UID=None,T=None, T_index=None)\n            else:\n                # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=4,T=None, T_index=None)\n                actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['211'][0], y=attack_actions['211'][1],z=500,UID=None,T=None, T_index=None)\n        else:\n            actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['211'][0], y=attack_actions['211'][1],z=500,UID=None,T=None, T_index=None)\n\n        # 小车221决策\n        if attact_states['221'][0] == 'attack':\n            if attact_states['221'][1] == '311':\n                # actions[thread, 1] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=3,T=None, T_index=None)\n                actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['221'][0], y=attack_actions['221'][1],z=500,UID=None,T=None, T_index=None)\n            else:\n                # actions[thread, 1] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=4,T=None, T_index=None)\n                actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['221'][0], y=attack_actions['221'][1],z=500,UID=None,T=None, T_index=None)\n        else:\n            actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['221'][0], y=attack_actions['221'][1],z=500,UID=None,T=None, T_index=None)\n        # 无人机231决策\n        actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=attack_actions['drone'][0],\n                                                     y=attack_actions['drone'][1],\n                                                     z=500,\n                                                     UID=None,\n                                                     T=None, T_index=None)\n\n        # if env0_step < 2:\n        # actions[thread, :] = self.act2digit_dictionary['ActionSet2::Idle;DynamicGuard']\n        # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=4, T=None, T_index=None)\n        # actions[thread, 0] = encode_action_as_digits(\"PatrolMoving\", \"N/A\", x=0, y=0, z=379, UID=None, T=None, T_index=None)\n        # actions[thread, 1] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=3, T=None, T_index=None)\n        # actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=-3000, y=700, z=500, UID=None,T=None, T_index=None)\n        # actions[thread, 2] = encode_action_as_digits(\"Idle\", \"DynamicGuard\", x=700, y=-3300, z=500, UID=None, T=None, T_index=None)\n        \"\"\"\n        if env0_step%4 == 0:\n            actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"Dir+X+Y\", x=700, y=-3300, z=500, UID=None, T=None, T_index=None)\n        if env0_step%4 == 1:\n            actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"Dir+X-Y\", x=700, y=-3300, z=500, UID=None, T=None, T_index=None)\n        if env0_step%4 == 2:\n            actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"Dir-X-Y\", x=700, y=-3300, z=500, UID=None, T=None, T_index=None)\n        if env0_step%4 == 3:\n            actions[thread, 2] = encode_action_as_digits(\"SpecificMoving\", \"Dir-X+Y\", x=700, y=-3300, z=500, UID=None, T=None, T_index=None)\n        \"\"\"\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread)\n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n# 防守方决策\nclass DummyAlgorithmT2(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        try:\n            res = self.interact_with_env_(State_Recall)\n        except:\n            actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n            actions[:] = encode_action_as_digits(\"N/A\", \"N/A\", x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            actions = np.swapaxes(actions, 0, 1)\n            res = (actions, None)\n        return res\n\n\n    def interact_with_env_(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n\n        assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n\n        env0_step = State_Recall['Current-Obs-Step']\n\n        obs = State_Recall['Latest-Team-Info']\n        thread = 0\n\n\n        # 防守方red小车的信息\n        red_agents_data = {'311': {'ammo': 100, 'velocity': 0, 'X': obs[0]['dataArr'][3]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][3]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][3]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][3]['agentHp'], 'state': defend_states['311']},\n                           '321': {'ammo': 100, 'velocity': 0, 'X': obs[0]['dataArr'][4]['agentLocation']['x'],\n                                   'Y': obs[0]['dataArr'][4]['agentLocation']['y'],\n                                   'Z': obs[0]['dataArr'][4]['agentLocation']['z'],\n                                   'Yaw': 0,\n                                   'blood': obs[0]['dataArr'][4]['agentHp'], 'state': defend_states['321']}}\n        # 进攻方blue小车和无人机信息，其中231是无人机\n        blue_agents_data = {'211': {'ammo': 100, 'velocity': 0, 'X': obs[0]['dataArr'][0]['agentLocation']['x'],\n                                    'Y': obs[0]['dataArr'][0]['agentLocation']['y'],\n                                    'Z': obs[0]['dataArr'][0]['agentLocation']['z'],\n                                    'Yaw': 0,\n                                    'blood': obs[0]['dataArr'][0]['agentHp'], 'state': attact_states['211']},\n                            '221': {'ammo': 100, 'velocity': 0, 'X': obs[0]['dataArr'][1]['agentLocation']['x'],\n                                    'Y': obs[0]['dataArr'][1]['agentLocation']['y'],\n                                    'Z': obs[0]['dataArr'][1]['agentLocation']['z'],\n                                    'Yaw': 0,\n                                    'blood': obs[0]['dataArr'][1]['agentHp'], 'state': attact_states['221']}}\n        drone_data =  {'ammo': 100, 'velocity': 0, 'X': obs[0]['dataArr'][2]['agentLocation']['x'],\n                              'Y': obs[0]['dataArr'][2]['agentLocation']['y'],\n                              'Z': obs[0]['dataArr'][2]['agentLocation']['z'],\n                              'Yaw': 0,\n                              'blood': obs[0]['dataArr'][2]['agentHp'], 'state': drone_state['231']}\n\n        blue_alive = [obs[0]['dataArr'][0]['agentAlive'], obs[0]['dataArr'][1]['agentAlive']]\n        red_alive = [obs[0]['dataArr'][3]['agentAlive'], obs[0]['dataArr'][4]['agentAlive']]\n        # 夺控点信息 在global——params.py修改\n        # key_points = [[700, -3300, 500], [-3000, 700, 500]]\n        DecisionMake = decision(blue_agents_data, drone_data, red_agents_data)\n        attackers = DecisionMake.attackers\n        defenders = DecisionMake.defenders\n        drone = DecisionMake.drone\n        # decision module test\n        attack_actions = DecisionMake.act(type='attackers')\n        defend_actions = DecisionMake.act(type='defenders')\n\n        att_states = []\n        def_states = []\n        for k, v in attackers.items():\n            att_states.append({k: v['state']})\n        for k, v in defenders.items():\n            def_states.append({k: v['state']})\n        attact_states['211'] = att_states[0]['211']\n        attact_states['221'] = att_states[1]['221']\n        defend_states['311'] = def_states[0]['311']\n        defend_states['321'] = def_states[1]['321']\n        drone_state['231'] = drone['state']\n\n        # print(\"==============智能体速度位置信息======================\")\n        # print(red_agents_data)\n        # print(blue_agents_data)\n        # print(drone_data)\n        # print(\"====================================\")\n        print(\"==============智能体state信息======================\")\n        print(attact_states)\n        print(defend_states)\n        # print(drone_state)\n        print(\"====================================\")\n\n        # 小车311决策\n        if defend_states['311'][0] == 'attack':\n            if defend_states['311'][1] == '211':\n                # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=0,T=None, T_index=None)\n                actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['311'][0], y=defend_actions['311'][1],z=500,UID=None,T=None, T_index=None)\n            else:\n                # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=1,T=None, T_index=None)\n                actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['311'][0], y=defend_actions['311'][1],z=500,UID=None,T=None, T_index=None)\n        else:\n            actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['311'][0], y=defend_actions['311'][1],z=500,UID=None,T=None, T_index=None)\n\n\n        # 小车321决策\n        if defend_states['321'][0] == 'attack':\n            if defend_states['321'][1] == '211':\n                # actions[thread, 1] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=0,T=None, T_index=None)\n                actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['321'][0], y=defend_actions['321'][1],z=500,UID=None,T=None, T_index=None)\n            else:\n                # actions[thread, 1] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=1,T=None, T_index=None)\n                actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['321'][0], y=defend_actions['321'][1],z=500,UID=None,T=None, T_index=None)\n        else:\n            actions[thread, 1] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=defend_actions['321'][0], y=defend_actions['321'][1],z=500,UID=None,T=None, T_index=None)\n\n\n        # actions[thread, 0] = encode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=1, T=None, T_index=None)\n        # actions[thread, 0] = encode_action_as_digits(\"Idle\", \"AggressivePersue\", x=10000, y=-10000, z=379, UID=None, T=None, T_index=None)\n        # actions[thread, 0] = encode_action_as_digits(\"SpecificMoving\", \"N/A\", x=10000, y=-10000, z=379, UID=None, T=None, T_index=None)\n        # actions[thread, 1] = encode_action_as_digits(\"PatrolMoving\", \"N/A\", x=444*5, y=444*5, z=379, UID=None, T=None, T_index=None)\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread)\n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n'''\n        if env0_step < 5:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::Idle;DynamicGuard']\n        elif env0_step < 15:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-3']\n        elif env0_step < 25:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-4']\n        elif env0_step < 35:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-5']\n        elif env0_step < 45:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-6']\n        elif env0_step < 55:\n            actions[thread, :] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-7']\n'''\n\n'''\n        if env0_step < 5:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::Idle;DynamicGuard']\n        else:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::SpecificAttacking;UID-1']\n\n\n'''\n\n'''\n    if env0_step < 5:\n        if env0_step%4 == 0:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X+Y']\n        if env0_step%4 == 1:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X-Y']\n        if env0_step%4 == 2:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X-Y']\n        if env0_step%4 == 3:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X+Y']\n    elif env0_step < 10:\n        actions[thread, 2] = self.act2digit_dictionary['ActionSet2::Idle;DynamicGuard']\n    elif env0_step < 15:\n        if env0_step%4 == 0:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir+X']\n        if env0_step%4 == 1:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir+Y']\n        if env0_step%4 == 2:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir-X']\n        if env0_step%4 == 3:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir-Y']\n    elif env0_step < 20:\n        actions[thread, 0] = self.act2digit_dictionary['ActionSet2::Idle;StaticAlert']\n    elif env0_step < 30:\n        if env0_step%4 == 0:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X+Y']\n        if env0_step%4 == 1:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X-Y']\n        if env0_step%4 == 2:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X-Y']\n        if env0_step%4 == 3:\n            actions[thread, 2] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X+Y']\n    else:\n        actions[thread, 0] = self.act2digit_dictionary['ActionSet2::Idle;StaticAlert']\n\n'''\n\n\"\"\"\n        thread = 0\n        if env0_step%4 == 0:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X+Y']\n        if env0_step%4 == 1:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir+X-Y']\n        if env0_step%4 == 2:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X-Y']\n        if env0_step%4 == 3:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::PatrolMoving;Dir-X+Y']\n\"\"\"\n\n\"\"\"\n        thread = 0\n        if env0_step%4 == 0:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir+X+Y']\n        if env0_step%4 == 1:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir+X-Y']\n        if env0_step%4 == 2:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir-X-Y']\n        if env0_step%4 == 3:\n            actions[thread, 0] = self.act2digit_dictionary['ActionSet2::SpecificMoving;Dir-X+Y']\n\"\"\""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/global_params.py",
    "content": "# # expel dist\n# DRIVE_AWAY_DIST = 1.2\n#\n# # defend dist\n# DEFEND_DIST = 2\n#\n# # retreat to the safe entrance\n# ATTA_RETREAT_POS = [-0.5, -3]\n#\n# # retreat to the dangerous entrance\n# DEF_RETREAT_POS = [3, 2]\n#\n# # key points\n# key_points = [[0.7, -3.3], [-3, 0.7]]\n\n# stance thres\nRETREAT_STANCE = 0.3\n\n\n# expel dist\nDRIVE_AWAY_DIST = 1000\n\n# defend dist\nDEFEND_DIST = 1000\n\n# retreat to the safe entrance\nATTA_RETREAT_POS = [-500, -3000]\n\n# retreat to the dangerous entrance\nDEF_RETREAT_POS = [3000, 2000]\n\n# key points\nkey_points = [[700, -3300], [-3000, 700]]"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/manual.py",
    "content": "import numpy as np\nfrom UTIL.tensor_ops import my_view, copy_clone\ntry:\n    from numba import jit\nexcept:\n    from UTIL.tensor_ops import dummy_decorator as jit\n\n\ndef to_cpu_numpy(x):\n    return x.cpu().numpy() if hasattr(x,'cpu') else x\n\nclass CoopAlgConfig():\n    reserve = None\n    \nclass DummyAlgorithmFoundationHI3D():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from config import GlobalConfig\n        super().__init__()\n        self.n_agent = n_agent\n        ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.num_entity = ScenarioConfig.num_entity\n        self.landmark_uid = ScenarioConfig.uid_dictionary['landmark_uid']\n        self.agent_uid = ScenarioConfig.uid_dictionary['agent_uid']\n        self.entity_uid = ScenarioConfig.uid_dictionary['entity_uid']\n        self.pos_decs = ScenarioConfig.obs_vec_dictionary['pos']\n        self.vel_decs = ScenarioConfig.obs_vec_dictionary['vel']\n        self.num_landmarks = len(self.landmark_uid)\n\n        self.invader_uid = ScenarioConfig.uid_dictionary['invader_uid']\n\n        self.n_entity = ScenarioConfig.num_entity\n        self.n_basic_dim = ScenarioConfig.obs_vec_length\n        self.n_thread = n_thread\n        self.attack_target = [None] * self.n_thread\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def get_previous(self, team_intel):\n        info = copy_clone(team_intel['Latest-Obs'])\n        Env_Suffered_Reset = copy_clone(team_intel['Env-Suffered-Reset'])\n        return info, Env_Suffered_Reset\n\n    def interact_with_env(self, State_Recall):\n        main_obs, Env_Suffered_Reset = self.get_previous(State_Recall)\n        action = np.ones(shape=(main_obs.shape[0], main_obs.shape[1], 1)) * -1\n\n        n_thread = main_obs.shape[0]\n        about_all_objects = main_obs[:,0,:]\n        objects_emb  = my_view(x=about_all_objects, shape=[0,-1,self.n_basic_dim]) # select one agent\n        \n        invader_emb     = objects_emb[:, self.invader_uid, :]\n        landmark_emb    = objects_emb[:, self.landmark_uid,:]\n\n        invader_pos = invader_emb[:, :, self.pos_decs]\n        invader_vel = invader_emb[:, :, self.vel_decs]\n\n        landmark_pos = landmark_emb[:, :, self.pos_decs]\n\n        # 为每一个invader设置一个随机目标，当且仅当step == 0 时（episode刚刚开始）\n        self.set_nearest_target(Env_Suffered_Reset, invader_pos, landmark_pos)\n\n        n_thread = self.n_thread\n        n_agent = self.n_agent\n        attack_target =  np.array(self.attack_target)\n        action = self.get_action(action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread)\n\n        assert not (action == -1).any()\n        actions_list = []\n        for i in range(self.n_agent):\n            actions_list.append(action[:, i])\n        return np.array(actions_list), None\n\n\n        \n    # @jit(nopython=True)\n    # @staticmethod\n    @jit(forceobj=True)\n    def get_action(self, action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread):\n        posit_vec = np.zeros_like(invader_vel)\n        for thread in range(n_thread):\n            for agent in range(n_agent):\n                posit_vec[thread,agent] = landmark_pos[thread, attack_target[thread][agent]] - invader_pos[thread, agent]\n\n\n        return self.dir_to_action3d(vec=posit_vec,vel=invader_vel)\n\n\n\n    @staticmethod\n    def dir_to_action3d(vec, vel):\n        def np_mat3d_normalize_each_line(mat):\n            return mat / np.expand_dims(np.linalg.norm(mat, axis=2) + 1e-16, axis=-1)\n        desired_speed = 0.8\n        vec = np_mat3d_normalize_each_line(vec)*desired_speed\n        return vec\n\n\n\n\n    def set_nearest_target(self, Env_Suffered_Reset, invader_pos, landmark_pos):\n        for thread, env_suffered_reset_ in enumerate(Env_Suffered_Reset):\n            if env_suffered_reset_:\n                invader_attack_target = [None] * self.n_agent\n                for i in range(self.n_agent):\n                    posit_vec = np.array([landmark_pos[thread, j] - invader_pos[thread, i] for j in range(self.num_landmarks)])\n                    dis_arr = np.linalg.norm(posit_vec, axis=-1)\n                    assigned_target = np.argmin(dis_arr)\n                    # assigned_target = np.random.randint(low=0, high=self.num_landmarks)\n                    invader_attack_target[i] = assigned_target\n                self.attack_target[thread] = np.array(invader_attack_target)\nclass DummyAlgorithmFoundationHI3D_old():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from config import GlobalConfig\n        super().__init__()\n        self.n_agent = n_agent\n        ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.num_entity = ScenarioConfig.num_entity\n        self.landmark_uid = ScenarioConfig.uid_dictionary['landmark_uid']\n        self.agent_uid = ScenarioConfig.uid_dictionary['agent_uid']\n        self.entity_uid = ScenarioConfig.uid_dictionary['entity_uid']\n        self.pos_decs = ScenarioConfig.obs_vec_dictionary['pos']\n        self.vel_decs = ScenarioConfig.obs_vec_dictionary['vel']\n        self.num_landmarks = len(self.landmark_uid)\n\n        self.invader_uid = ScenarioConfig.uid_dictionary['invader_uid']\n\n        self.n_entity = ScenarioConfig.num_entity\n        self.n_basic_dim = ScenarioConfig.obs_vec_length\n        self.n_thread = n_thread\n        self.attack_target = [None] * self.n_thread\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def get_previous(self, team_intel):\n        info = copy_clone(team_intel['Latest-Obs'])\n        Env_Suffered_Reset = copy_clone(team_intel['Env-Suffered-Reset'])\n        return info, Env_Suffered_Reset\n\n    def interact_with_env(self, State_Recall):\n        main_obs, Env_Suffered_Reset = self.get_previous(State_Recall)\n        action = np.ones(shape=(main_obs.shape[0], main_obs.shape[1], 1)) * -1\n\n        n_thread = main_obs.shape[0]\n        about_all_objects = main_obs[:,0,:]\n        objects_emb  = my_view(x=about_all_objects, shape=[0,-1,self.n_basic_dim]) # select one agent\n        \n        invader_emb     = objects_emb[:, self.invader_uid, :]\n        landmark_emb    = objects_emb[:, self.landmark_uid,:]\n\n        invader_pos = invader_emb[:, :, self.pos_decs]\n        invader_vel = invader_emb[:, :, self.vel_decs]\n\n        landmark_pos = landmark_emb[:, :, self.pos_decs]\n\n        # 为每一个invader设置一个随机目标，当且仅当step == 0 时（episode刚刚开始）\n        self.set_random_target(Env_Suffered_Reset)\n\n        n_thread = self.n_thread\n        n_agent = self.n_agent\n        attack_target =  np.array(self.attack_target)\n        action = self.get_action(action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread)\n\n        assert not (action == -1).any()\n        actions_list = []\n        for i in range(self.n_agent):\n            actions_list.append(action[:, i])\n        return np.array(actions_list), None\n\n\n        \n    # @jit(nopython=True)\n    # @staticmethod\n    @jit(forceobj=True)\n    def get_action(self, action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread):\n        posit_vec = np.zeros_like(invader_vel)\n        for thread in range(n_thread):\n            for agent in range(n_agent):\n                posit_vec[thread,agent] = landmark_pos[thread, attack_target[thread][agent]] - invader_pos[thread, agent]\n\n\n        return self.dir_to_action3d(vec=posit_vec,vel=invader_vel)\n\n\n    @staticmethod\n    @jit(forceobj=True)\n    def dir_to_action3d(vec, vel):\n        def np_mat3d_normalize_each_line(mat):\n            return mat / np.expand_dims(np.linalg.norm(mat, axis=2) + 1e-16, axis=-1)\n        vec = np_mat3d_normalize_each_line(vec)\n\n        e_u = np.array([0  ,1  , 0 ])\n        e_d = np.array([0  ,-1 , 0 ])\n        e_r = np.array([1  ,0  , 0 ])\n        e_l = np.array([-1 ,0  , 0 ])\n        e_a = np.array([0  ,0  , 1 ])\n        e_b = np.array([0  ,0  ,-1 ])\n\n        vel_u = np_mat3d_normalize_each_line(vel + e_u * 0.1)\n        vel_d = np_mat3d_normalize_each_line(vel + e_d * 0.1)\n        vel_r = np_mat3d_normalize_each_line(vel + e_r * 0.1)\n        vel_l = np_mat3d_normalize_each_line(vel + e_l * 0.1)\n        vel_a = np_mat3d_normalize_each_line(vel + e_a * 0.1)\n        vel_b = np_mat3d_normalize_each_line(vel + e_b * 0.1)\n\n        proj_u = (vel_u * vec).sum(-1)\n        proj_d = (vel_d * vec).sum(-1)\n        proj_r = (vel_r * vec).sum(-1)\n        proj_l = (vel_l * vec).sum(-1)\n        proj_a = (vel_a * vec).sum(-1)\n        proj_b = (vel_b * vec).sum(-1)\n\n        _u = ((vec * e_u).sum(-1)>0).astype(np.int)\n        _d = ((vec * e_d).sum(-1)>0).astype(np.int)\n        _r = ((vec * e_r).sum(-1)>0).astype(np.int)\n        _l = ((vec * e_l).sum(-1)>0).astype(np.int)\n        _a = ((vec * e_a).sum(-1)>0).astype(np.int)\n        _b = ((vec * e_b).sum(-1)>0).astype(np.int)\n\n        proj_u = proj_u + _u*2\n        proj_d = proj_d + _d*2\n        proj_r = proj_r + _r*2\n        proj_l = proj_l + _l*2\n        proj_a = proj_a + _a*2\n        proj_b = proj_b + _b*2\n\n        dot_stack = np.stack([proj_u, proj_d, proj_r, proj_l, proj_a, proj_b])\n        direct = np.argmax(dot_stack, 0)\n\n        action = np.where(direct == 0, 2, 0)\n        action += np.where(direct == 1, 4, 0)\n        action += np.where(direct == 2, 1, 0)\n        action += np.where(direct == 3, 3, 0)\n\n        action += np.where(direct == 4, 5, 0)\n        action += np.where(direct == 5, 6, 0)\n\n        return np.expand_dims(action, axis=-1)\n\n\n\n\n    def set_random_target(self, Env_Suffered_Reset):\n        for thread, env_suffered_reset_ in enumerate(Env_Suffered_Reset):\n            if env_suffered_reset_:\n                invader_attack_target = [None] * self.n_agent\n                for i in range(self.n_agent):\n                    assigned_target = np.random.randint(low=0, high=self.num_landmarks)\n                    invader_attack_target[i] = assigned_target\n                self.attack_target[thread] = np.array(invader_attack_target)\n\n\nclass IHDummyAlgorithmFoundation():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        from config import GlobalConfig\n        super().__init__()\n        self.n_agent = n_agent\n        ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.num_entity = ScenarioConfig.num_entity\n        self.landmark_uid = ScenarioConfig.uid_dictionary['landmark_uid']\n        self.agent_uid = ScenarioConfig.uid_dictionary['agent_uid']\n        self.invader_uid = ScenarioConfig.uid_dictionary['invader_uid']\n        self.n_entity = ScenarioConfig.num_entity\n        self.n_basic_dim = ScenarioConfig.obs_vec_length\n        self.n_object = ScenarioConfig.num_object\n        self.n_thread = n_thread\n        self.num_landmarks = ScenarioConfig.num_landmarks\n        self.attack_target = [None] * self.n_thread\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def get_previous(self, team_intel):\n        info = copy_clone(team_intel['Latest-Obs'])\n        done = copy_clone(team_intel['Env-Suffered-Reset'])\n        return info, done\n    '''\n        info, done = self.get_previous(team_intel)\n        current_step = info[:,0,-1]\n        object_info = my_view(info[:,0,:-1],[0,-1,5])\n\n\n        worker_emb = object_info[:, self.worker_uid]\n        cargo_emb = object_info[:, self.cargo_uid]\n\n        worker_pos = worker_emb[:,:,self.dec_pos]\n        worker_vel = worker_emb[:,:,self.dec_vel]\n        worker_drag = worker_emb[:,:,self.dec_other]\n\n        cargo_dropoff_pos = cargo_emb[:,:,self.dec_pos]\n        cargo_dropoff_weight = cargo_emb[:,:,self.dec_other]\n\n        cargo_pos = cargo_dropoff_pos[:, :self.n_cargo]\n        dropoff_pos = cargo_dropoff_pos[:, self.n_cargo:]\n        cargo_weight = cargo_dropoff_weight[:, :self.n_cargo]\n    '''\n    def interact_with_env(self, State_Recall):\n        info, done = self.get_previous(State_Recall)\n        current_step = info[:,0,-1]\n        entity_pure_emb = my_view(info[:,0,:-1],shape=[0,-1,5])\n\n        action = np.ones(shape=(info.shape[0], info.shape[1], 1)) * -1\n\n        entity_pos = entity_pure_emb[:, :, (0,1)]\n        entity_vel = entity_pure_emb[:, :, (2,3)]\n\n        invader_vel = entity_vel[:, self.invader_uid]\n        invader_pos = entity_pos[:, self.invader_uid]\n        landmark_pos = entity_pos[:, self.landmark_uid]\n\n        # 为每一个invader设置一个随机目标，当且仅当step == 0 时（episode刚刚开始）\n        self.set_random_target(current_step)\n\n        n_thread = self.n_thread\n        n_agent = self.n_agent\n        attack_target =  np.array(self.attack_target)\n        self.get_action(action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread)\n\n        assert not (action == -1).any()\n        actions_list = []\n        for i in range(self.n_agent):\n            actions_list.append(action[:, i])\n        return np.array(actions_list), None\n\n    @staticmethod\n    @jit(nopython=True)\n    def get_action(action, attack_target, invader_pos, invader_vel, landmark_pos, n_agent, n_thread):\n        def Norm(x):\n            return np.linalg.norm(x)\n        for thread in range(n_thread):\n            for agent in range(n_agent):\n                speed_vec = invader_vel[thread, agent]\n                posit_vec = landmark_pos[thread, attack_target[thread][agent]] - invader_pos[thread, agent]\n\n                posit_norm = Norm(posit_vec)\n                if posit_norm != 0:\n                    posit_vec = posit_vec / posit_norm\n\n                speed_norm = Norm(speed_vec)\n                if speed_norm != 0:\n                    speed_vec = speed_vec / speed_norm\n\n                up = np.sum(posit_vec * np.array([0, 1]))\n                dn = np.sum(posit_vec * np.array([0, -1]))\n                ri = np.sum(posit_vec * np.array([1, 0]))\n                le = np.sum(posit_vec * np.array([-1, 0]))\n\n                up_v = np.sum(speed_vec * np.array([0, 1]))\n                dn_v = np.sum(speed_vec * np.array([0, -1]))\n                ri_v = np.sum(speed_vec * np.array([1, 0]))\n                le_v = np.sum(speed_vec * np.array([-1, 0]))\n\n                dot_product = np.array([up, dn, ri, le])\n                dot_product_v = np.array([up_v, dn_v, ri_v, le_v])\n\n                # situation 1\n                bool_ = (dot_product > dot_product_v) & (dot_product > 0)\n                direct = bool_.astype(np.int64)\n\n                if np.sum(direct) != 1:  # 向量重合，或者速度为0，不再对比速度方向\n                    direct = np.argmax(dot_product)\n                else:\n                    # assert sum(direct) == 1 #检查\n                    direct = np.argmax(direct)\n\n                # stay_no_acc?[0], left[1], right[2], DOWN[3], Up[4]\n                if direct == 0:  # Up\n                    action[thread, agent, 0] = 2\n                elif direct == 1:  # DOWN\n                    action[thread, agent, 0] = 4\n                elif direct == 2:  # right\n                    action[thread, agent, 0] = 1\n                elif direct == 3:  # left\n                    action[thread, agent, 0] = 3\n\n\n    def set_random_target(self, step_env_cnt_cnt):\n        for thread, step_env_cnt in enumerate(step_env_cnt_cnt):\n            if step_env_cnt == 0:\n                invader_attack_target = [None] * self.n_agent\n                for i in range(self.n_agent):\n                    assigned_target = np.random.randint(low=0, high=self.num_landmarks)\n                    invader_attack_target[i] = assigned_target\n                self.attack_target[thread] = np.array(invader_attack_target)\n\n\n\n\nclass DummyAlgorithmFoundation():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        super().__init__()\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.mcv = mcv\n        self.act_space = space['act_space']\n        self.obs_space = space['obs_space']\n\n        self.n_cargo = GlobalConfig.ScenarioConfig.n_cargo\n\n        self.worker_uid = GlobalConfig.ScenarioConfig.uid_dictionary['agent_uid']\n        self.cargo_uid = GlobalConfig.ScenarioConfig.uid_dictionary['entity_uid']\n\n        self.dec_pos = GlobalConfig.ScenarioConfig.obs_vec_dictionary['pos']\n        self.dec_vel = GlobalConfig.ScenarioConfig.obs_vec_dictionary['vel']\n        self.dec_other = GlobalConfig.ScenarioConfig.obs_vec_dictionary['mass']\n        self.vec_len = GlobalConfig.ScenarioConfig.obs_vec_length\n\n\n    def interact_with_env(self, team_intel):\n        info, done = self.get_previous(team_intel)\n        current_step = info[:,0,-1]\n        object_info = my_view(info[:,0,:-1],[0,-1,self.vec_len])\n\n\n        worker_emb = object_info[:, self.worker_uid]\n        cargo_emb = object_info[:, self.cargo_uid]\n\n        worker_pos = worker_emb[:,:,self.dec_pos]\n        worker_vel = worker_emb[:,:,self.dec_vel]\n        worker_drag = worker_emb[:,:,self.dec_other]\n\n        cargo_dropoff_pos = cargo_emb[:,:,self.dec_pos]\n        cargo_dropoff_weight = cargo_emb[:,:,self.dec_other]\n\n        cargo_pos = cargo_dropoff_pos[:, :self.n_cargo]\n        dropoff_pos = cargo_dropoff_pos[:, self.n_cargo:]\n        cargo_weight = (cargo_dropoff_weight[:, :self.n_cargo]+1)*(self.n_agent/self.n_cargo)\n\n        worker_target_sel = np.zeros(shape=(self.n_thread,self.n_agent, 1))\n        for t in range(self.n_thread):\n            p = 0\n            for c, cw in enumerate(cargo_weight[t]):\n                if cw > self.n_agent: continue\n                for j in range(int(p), int(p+cw)):\n                    worker_target_sel[t,j] = c if worker_drag[t,j] < 0 else (c+self.n_cargo)\n                p = p+cw\n\n        target_pos = np.take_along_axis(cargo_dropoff_pos,worker_target_sel.astype(np.long),1)\n\n        actions_list = []\n\n        act = np.random.randint(low=0,high=5,size=(self.n_thread, self.n_agent, 1))\n        act = self.dir_to_action(vec=target_pos-worker_pos, vel=worker_vel)\n        for i in range(self.n_agent):\n            actions_list.append(act[:, i])\n        return actions_list, None\n\n    def get_previous(self, team_intel):\n        info = copy_clone(team_intel['Latest-Obs'])\n        done = copy_clone(team_intel['Env-Suffered-Reset'])\n        return info, done\n\n\n\n    @staticmethod\n    def dir_to_action(vec, vel):\n        def np_mat3d_normalize_each_line(mat):\n            return mat / np.expand_dims(np.linalg.norm(mat, axis=2) + 1e-16, axis=-1)\n        vec = np_mat3d_normalize_each_line(vec)\n\n        e_u = np.array([0,1])\n        e_d = np.array([0,-1])\n        e_r = np.array([1,0])\n        e_l = np.array([-1,0])\n\n        vel_u = np_mat3d_normalize_each_line(vel + e_u * 0.1)\n        vel_d = np_mat3d_normalize_each_line(vel + e_d * 0.1)\n        vel_r = np_mat3d_normalize_each_line(vel + e_r * 0.1)\n        vel_l = np_mat3d_normalize_each_line(vel + e_l * 0.1)\n\n        proj_u = (vel_u * vec).sum(-1)\n        proj_d = (vel_d * vec).sum(-1)\n        proj_r = (vel_r * vec).sum(-1)\n        proj_l = (vel_l * vec).sum(-1)\n\n        _u = ((vec * e_u).sum(-1)>0).astype(np.int)\n        _d = ((vec * e_d).sum(-1)>0).astype(np.int)\n        _r = ((vec * e_r).sum(-1)>0).astype(np.int)\n        _l = ((vec * e_l).sum(-1)>0).astype(np.int)\n\n        proj_u = proj_u + _u*2\n        proj_d = proj_d + _d*2\n        proj_r = proj_r + _r*2\n        proj_l = proj_l + _l*2\n\n        dot_stack = np.stack([proj_u, proj_d, proj_r, proj_l])\n        direct = np.argmax(dot_stack, 0)\n\n        action = np.where(direct == 0, 2, 0)\n        action += np.where(direct == 1, 4, 0)\n        action += np.where(direct == 2, 1, 0)\n        action += np.where(direct == 3, 3, 0)\n\n        return np.expand_dims(action, axis=-1)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/module_evaluation.py",
    "content": "import copy\nimport random\nimport numpy as np\nimport datetime\nimport time\nimport math\n\n#态势评估模块\n#接口输入：全局状态信息，包括进攻方无人车各种状态，防守方无人车各种状态以及地图状态\n\nclass Evaluation_module():\n\tdef __init__(self, critical_points=[[-3000, 700, 0], [700, -3300, 0]]):\n\t\tself.R0 = 750 # 距离态势缩放因子\n\t\tself.V0 = 0.1  # 速度态势缩放因子\n\t\tself.phi0 = 1   # 俯仰角态势系数\n\t\tself.psi0 = 0  # 偏航角态势系数\n\t\tself.ammo0 = 5  # 载荷态势缩放因子(增函数用)\n\t\tself.heal0 = 5  # 血量态势缩放因子(增函数用)\n\t\tself.AMMO0 = 5  # 载荷态势缩放因子(减函数用)\n\t\tself.HEAL0 = 5  # 血量态势缩放因子(减函数用)\n\n\t\t# 已知的环境信息\n\t\tself.critical_points = critical_points  # 夺控点位置\n\t\n\t# 计算相对速度态势时使用的增函数，输出区间为(0,1)\n\tdef SigmoidTen(self, x, c):\n\t\ty = np.exp(-x/c)\n\t\treturn 1/(1+10*y)\n\t\n\t# 计算停留时间态势时使用的增函数，输出区间为[0,0.9)\n\tdef SigmoidNine(self, x, c):\n\t\ty = np.exp(-x/c)\n\t\treturn 1/(1+9*y) - 0.1\n\t\n\t# 计算无人车相对坐标点的态势，以备最佳点规划使用，输入为点的坐标和此无人车的信息\n\t# 在进行最佳点规划时，会计算待打击目标附近的几个敌方无人车的态势之和，以此来计算最佳点\n\tdef UAV2Point(self, p_position, position, velocity, phi, psi, ammo, health):\n\t\t# 相对距离威胁\n\t\tp_position = np.array(p_position)\n\t\tposition = np.array(position)\n\t\tvelocity = np.array(velocity)\n\t\tr = p_position - position\n\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\tSr = np.exp(-dist/self.R0)\n\t\t# 相对速度威胁\n\t\tV = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n\t\tSv = self.SigmoidTen(V, self.V0)\n\t\t# 俯仰角威胁\n\t\tSphi = np.exp(-np.abs(phi - self.phi0))\n\t\t# 偏航角威胁\n\t\tSpsi = np.exp(-np.abs(psi - self.psi0))\n\t\t# 载荷威胁（增函数）载荷为0时威胁为0\n\t\t# Sammo = self.SigmoidNine(ammo, self.ammo0)\n\t\tSammo = 1\n\t\t# 强健度威胁（增函数） 血量为0时威胁为0\n\t\tSheal = self.SigmoidNine(health, self.heal0)\n\n\t\t# 总态势计算 (系数之和不一定为1，每个系数直接在此处修改）\n\t\t# 载荷威胁和强健度威胁此处用乘法，算法需要 [在打击范围内] 寻找总态势最小的点作为坐标点\n\t\tS_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi + 0.0 * Spsi) * Sammo * Sheal\n\t\treturn S_sum\n\n\t# 计算无人车相对无人车(智能体)的态势（威胁），以作为选取打击对象的依据（选取威胁大的但是优势低的）\n\t# 其中a_position等表示智能体无人车的参数，即计算态势时考虑的主体的参数\n\tdef UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity, phi, psi, ammo, health):\n\t\ta_position = np.array(a_position)\n\t\tposition = np.array(position)\n\t\tvelocity = np.array(velocity)\n\t\t# 能力比例系数，如我方无人车面对敌方无人车时为1.5, 敌方无人车面对我方无人车时为0.67\n\t\t# 己方载荷及健康态势计算（增函数）\n\t\t# Mammo = self.SigmoidNine(a_ammo, self.ammo0)\n\t\tMammo = 1\n\t\tMhealth = self.SigmoidNine(a_health, self.heal0)\n\t\t# 对方载荷及健康优势计算（减函数）\n\t\t# Sammo = np.exp(-ammo / self.AMMO0)\n\t\tSammo = 1\n\t\tShealth = np.exp(-health / self.HEAL0)\n\t\t# 进攻方优势计算(选择优势最大的进行打击，若相同则选择距离更近的进行打击）\n\t\tif identity == \"offensive\":\n\t\t\t# 相对距离威胁\n\t\t\tr = a_position - position\n\t\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\t\tSr = np.exp(-dist / self.R0)\n\t\t\tS_offensive = 10 * Mammo*Mhealth * Sammo*Shealth * Sr  # 乘了系数10以致于S不过分小\n\t\t\treturn S_offensive\n\t\t# 防守方优势计算（优先打击距离夺控点近的无人车）\n\t\tif identity == \"defensive\":\n\t\t\tSr_temp = 0\n\t\t\tfor critical_point in self.critical_points:\n\t\t\t\tr = critical_point - position\n\t\t\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\t\t\tSr = np.exp(-dist / self.R0)\n\t\t\t\tif Sr >= Sr_temp:\n\t\t\t\t\tSr_temp = Sr\n\t\t\tS_defensive = 10 * Mammo * Mhealth * Sammo * Shealth * Sr_temp\n\t\t\treturn S_defensive\n\n\t# 计算无人机相对夺控点的态势，以此作为防守方是否进行驱离及选择谁对谁进行驱离的依据\n\tdef Drone2Point(self, p_position,p_ts, position, velocity):\n\t\t# 相对距离威胁\n\t\tp_position = np.array(p_position)\n\t\tposition = np.array(position)\n\t\tvelocity = np.array(velocity)\n\t\tr = p_position - position\n\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\tSpr = np.exp(-dist / 1)   # 此处的放缩系数采用与无人机参数相关的\n\t\t# 相对速度威胁\n\t\tV = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n\t\tSpv = self.SigmoidTen(V, 0.2)   # 此处的放缩系数采用与无人机参数相关的\n\t\t# 停留时间威胁\n\t\tSpt = self.SigmoidNine(p_ts, 0.5)  # 不能接受无人机停留3秒及以上\n\t\t# 计算综合态势\n\t\tSp = Spt + 0.6 * Spr + 0.2 * Spv  # 建议驱离阈值: Sp >= 0.5\n\t\tprint(Sp)\n\t\treturn Sp\n\t\n\tdef UAV2Point_id(self, attacker_dict, key_point):\n\t\t# 相对距离威胁\n\t\t#进攻方无人车信息\n\t\tally_agent_pos = [attacker_dict['X'], attacker_dict['Y'], attacker_dict['Z']]\n\t\tally_agent_blood = attacker_dict['blood']\n\t\tally_agent_velocityx = attacker_dict['vx']\n\t\tally_agent_velocityy = attacker_dict['vy']\n\t\tally_agent_ammo = attacker_dict['ammo']\n\t\tally_agent_velocity = [ally_agent_velocityx, ally_agent_velocityy, 0]\n   \n\t\tp_position = np.array(key_point)\n\t\tposition = np.array(ally_agent_pos)\n\t\tvelocity = np.array(ally_agent_velocity)\n\t\tr = p_position - position\n\t\t\n\t\tphi = math.degrees(math.atan2((ally_agent_pos[0] - key_point[0]), (ally_agent_pos[1] - key_point[1])))\n\t\tammo = ally_agent_ammo\n\t\thealth = ally_agent_blood\n\t\t# 相对速度威胁\n\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\tSr = np.exp(-dist/self.R0)\n\t\t# V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n\t\t#Sv = self.SigmoidTen(V, self.V0)\n\t\t# 偏航角威胁\n\t\t# Sphi = np.exp(-np.abs(phi - self.phi0))\n\t\t# 俯仰角威胁\n\t\t# Spsi = np.exp(-np.abs(psi - self.psi0))\n\t\t# 载荷威胁（增函数）载荷为0时威胁为0\n\t\t# Sammo = self.SigmoidNine(ammo, self.ammo0)\n\t\tSammo = 1\n\t\t# 强健度威胁（增函数） 血量为0时威胁为0\n\t\tSheal = self.SigmoidNine(health, self.heal0)\n\n\t\t# 总态势计算 (系数之和不一定为1，每个系数直接在此处修改）\n\t\t# 载荷威胁和强健度威胁此处用乘法，算法需要 [在打击范围内] 寻找总态势最小的点作为坐标点\n\t\t# S_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi + 0.0 * Spsi) * Sammo * Sheal\n\t\t# S_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi) * Sammo * Sheal\n\t\tS_sum = Sr * Sammo * Sheal\n\t\treturn S_sum\n\n\tdef UAV2UAV_id(self, identity, attacker_dict, defender_dict):\n\t\t#进攻方无人车信息\n\t\tally_agent_pos = [attacker_dict['X'], attacker_dict['Y'], attacker_dict['Z']]\n\t\tally_agent_blood = attacker_dict['blood']\n\t\tally_agent_ammo = attacker_dict['ammo']\n\n\t\tenemy_agent_pos = [defender_dict['X'], defender_dict['Y'], defender_dict['Z']]\n\t\tenemy_agent_blood = defender_dict['blood']\n\t\tenemy_agent_ammo = defender_dict['ammo']\n\n\t\ta_position = np.array(ally_agent_pos)\n\t\tposition = np.array(enemy_agent_pos)\n\t\ta_ammo = ally_agent_ammo\n\t\ta_health = ally_agent_blood\n\t\tammo = enemy_agent_ammo\n\t\thealth = enemy_agent_blood\n\n\t\t# 进攻方优势计算(选择优势最大的进行打击，若相同则选择距离更近的进行打击）\n\t\tif identity == \"offensive\":\n\t\t\t# 相对距离威胁\n\t\t\t# Mammo = self.SigmoidNine(a_ammo, self.ammo0)\n\t\t\tMammo = 1\n\t\t\tdel_health = 100 - health\n\t\t\tMhealth = health / 100\n\t\t\t# Mhealth = self.SigmoidNine(a_health, self.heal0)\n\t\t\t# 对方载荷及健康优势计算（减函数）\n\t\t\t# Sammo = np.exp(ammo / self.AMMO0)\n\t\t\tSammo = 1\n\t\t\t# Shealth = np.exp(health / self.HEAL0) \n\t\t\tdel_a_health = 100 - a_health\n\t\t\tShealth = a_health / 100\t\t   \n\t\t\tr = a_position - position\n\t\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\t\tSr = np.exp(-dist / self.R0)\n\t\t\tS_offensive =  1 * Mammo * Mhealth * Sammo * Shealth * Sr  # 乘了系数10以致于S不过分小\n\n\t\t\treturn S_offensive\n\t\t# 防守方优势计算（优先打击距离夺控点近的无人车）\n\t\tif identity == \"defensive\":\n\t\t\tSr_temp = 0\n\t\t\t# Mammo = self.SigmoidNine(ammo, self.ammo0)\n\t\t\tMammo = 1\n\t\t\t# Mhealth = self.SigmoidNine(health, self.heal0)\n\t\t\tdel_health = 100 - health\n\t\t\tMhealth = health / 100\n\t\t\t# 对方载荷及健康优势计算（减函数）\n\t\t\t# Sammo = np.exp(-a_ammo / self.AMMO0)\n\t\t\tSammo = 1\n\t\t\tdel_a_health = 100 - a_health\n\t\t\tShealth = a_health / 100\n\t\t\tfor critical_point in self.critical_points:\n\t\t\t\tr = critical_point - a_position\n\t\t\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\t\t\tSr = np.exp(-dist / self.R0)\n\t\t\t\tif Sr >= Sr_temp:\n\t\t\t\t\tSr_temp = Sr\n\t\t\tS_defensive =  Mammo * Mhealth * Sammo * Shealth * Sr_temp\n\t\t\treturn S_defensive\n\n\tdef Drone2Point_id(self, drone_data, key_point):\n\t\tdrone_pos = [drone_data['X'], drone_data['Y'], drone_data['Z']]\n\t\tdrone_blood = drone_data['blood']\n\t\tdrone_velocityx = drone_data['vx']\n\t\tdrone_velocityy = drone_data['vy']\n\t\tdrone_velocity = [drone_velocityx, drone_velocityy, 0]\n\t\t# 相对距离威胁\n\t\tp_position = np.array(key_point)\n\t\tposition = np.array(drone_pos)\n\t\tvelocity = np.array(drone_velocity)\n\t\tr = p_position - position\n\t\tdist = np.sqrt(np.sum(np.square(r)))\n\t\tSpr = np.exp(-dist / 1000)   # 此处的放缩系数采用与无人机参数相关的\n\t\t# 相对速度威胁\n\t\t# V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n\t\t# Spv = self.SigmoidTen(V, 0.2)   # 此处的放缩系数采用与无人机参数相关的\n\t\t# 停留时间威胁\n\t\t# Spt = self.SigmoidNine(p_ts, 0.5)  # 不能接受无人机停留3秒及以上\n\t\t# 计算综合态势\n\t\t# Sp = Spt + 0.6 * Spr + 0.2 * Spv  # 建议驱离阈值: Sp >= 0.5\n\t\tSp =  Spr \n\t\treturn Sp\n\n\n\t#计算防守方无人车相对于进攻方无人车的态势矩阵\n\t#矩阵横轴维度为进攻方无人车数量，纵轴维度为防守方无人车数量\n\tdef defend_to_attack(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n\t\t#无人车\n\t\tall_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tif 'blood' not in dict_value:\n\t\t\t\ttemp = agent_id\n\t\tall_friend_agents_data.pop(temp)  # 剔除无人机数据，只考虑地面无人车平台\n\n\t\t#进攻方无人车信息\n\t\tall_friend_agent_pos = []\n\t\tall_friend_agent_blood = []\n\t\tall_friend_agent_velocityx = []\n\t\tall_friend_agent_velocityy = []\n\t\tall_friend_agent_ammo = []\n\t\tall_friend_agent_ID = []\n\t\tall_friend_amount = 0\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tall_friend_agent_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n\t\t\t# all_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n\t\t\tall_friend_amount += 1\n\t\t\n\t\t#防守方无人车信息\n\t\tall_enemy_agent_pos = []\n\t\tall_enemy_agent_blood = []\n\t\tall_enemy_agent_velocityx = []\n\t\tall_enemy_agent_velocityy = []\n\t\tall_enemy_agent_ammo = []\n\t\tall_enemy_agent_ID = []\n\t\tall_enemy_amount = 0\n\t\tfor agent_id, dict_value in enemy_agents_data.items():\n\t\t\tall_enemy_agent_ID.append(agent_id) \n\t\t\t# all_enemy_agent_ammo.append(dict_value['ammo']) \n\t\t\tall_enemy_agent_velocityx.append(dict_value['velocityx'])\n\t\t\tall_enemy_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_enemy_agent_blood.append(dict_value['blood'])\n\t\t\tall_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n\t\t\tall_enemy_amount += 1\n\n\t\tevaluation = np.zeros((all_friend_amount, all_enemy_amount))\n\n\t\tfor i in range(all_friend_amount):\n\t\t\tfor j in range(all_enemy_amount):\n\t\t\t\tyaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n\t\t\t\t# UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n\t\t\t\tall_enemy_agent_velocity = [all_enemy_agent_velocityx[j], all_enemy_agent_velocityy[j], 0]\n\t\t\t\tevaluation[i][j] = self.UAV2UAV(\"offensive\", all_friend_agent_pos[i], 0, all_friend_agent_blood[i],\n\t\t\t\tall_enemy_agent_pos[j], all_enemy_agent_velocity, yaw, 0, 0, all_enemy_agent_blood[j])\n\n\t\treturn evaluation\n\n\t#计算进攻方无人车相对于防守方无人车的态势矩阵\n\t#矩阵横轴维度为防守方无人车数量，纵轴维度为进攻方无人车数量\n\tdef attack_to_defend(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n\t\t#无人车\n\t\tall_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tif 'blood' not in dict_value:\n\t\t\t\ttemp = agent_id\n\t\tall_friend_agents_data.pop(temp)  # 剔除无人机数据，只考虑地面无人车平台\n\n\t\t#进攻方无人车信息\n\t\tall_friend_agent_pos = []\n\t\tall_friend_agent_blood = []\n\t\tall_friend_agent_velocityx = []\n\t\tall_friend_agent_velocityy = []\n\t\tall_friend_agent_ammo = []\n\t\tall_friend_agent_ID = []\n\t\tall_friend_amount = 0\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tall_friend_agent_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n\t\t\t#all_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n\t\t\tall_friend_amount += 1\n\n\t\t#防守方无人车信息\n\t\tall_enemy_agent_pos = []\n\t\tall_enemy_agent_blood = []\n\t\tall_enemy_agent_velocityx = []\n\t\tall_enemy_agent_velocityy = []\n\t\tall_enemy_agent_ammo = []\n\t\tall_enemy_agent_ID = []\n\t\tall_enemy_amount = 0\n\t\tfor agent_id, dict_value in enemy_agents_data.items():\n\t\t\tall_enemy_agent_ID.append(agent_id) \n\t\t\t#all_enemy_agent_ammo.append(dict_value['ammo']) \n\t\t\tall_enemy_agent_velocityx.append(dict_value['velocityx'])\n\t\t\tall_enemy_agent_velocityy.append(dict_value['velocityy'])\n\t\t\tall_enemy_agent_blood.append(dict_value['blood'])\n\t\t\tall_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n\t\t\tall_enemy_amount += 1\n\n\t\tevaluation = np.zeros((all_enemy_amount, all_friend_amount))\n\n\t\tfor i in range(all_enemy_amount):\n\t\t\tfor j in range(all_friend_amount):\n\t\t\t\tyaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n\t\t\t\t# UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n\t\t\t\tall_friend_agent_velocity = [all_friend_agent_velocityx[j], all_friend_agent_velocityy[j], 0]\n\t\t\t\tevaluation[i][j] = self.UAV2UAV(\"defensive\", all_enemy_agent_pos[i], 0, all_enemy_agent_blood[i], \n\t\t\t\tall_friend_agent_pos[j], all_friend_agent_velocity, yaw, 0, 0, all_friend_agent_blood[j])\n\n\t\treturn evaluation\n\n\t#计算无人机对于夺控点位置的态势矩阵\n\t#矩阵横轴代表夺控点，纵轴代表无人机\n\tdef uav_to_defend(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n\t\tall_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tif 'blood' not in dict_value:\n\t\t\t\ttemp1 = agent_id\n\t\t\t\ttemp2 = dict_value\n\t\t\n\t\tdrone_data = {}\n\t\tdrone_data[temp1] = temp2\n\n\t\t#无人机信息\n\t\tdrone_pos = []\n\t\tdrone_velocityx = []\n\t\tdrone_velocityy = []\n\t\tdrone_ID = []\n\t\tdrone_amount = 0\n\t\tfor agent_id, dict_value in drone_data.items():\n\t\t\tdrone_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n\t\t\tdrone_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tdrone_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tdrone_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n\t\t\tdrone_amount += 1\n\n\t\t#夺控点位置\n\t\tkey_point_amount = 0\n\t\tkey_point_pos = []\n\t\tfor key_point in key_points:\n\t\t\tkey_point_pos.append(key_point)\n\t\t\tkey_point_amount += 1\n\n\t\tevaluation = np.zeros((key_point_amount, drone_amount))\n\n\t\tfor i in range(key_point_amount):\n\t\t\tfor j in range(drone_amount):\n\t\t\t\t# Drone2Point(self, p_position,p_ts, position, velocity)\n\t\t\t\t# print(self.Drone2Point(key_point_pos[i], 0, drone_pos[j], drone_velocity[j]))\n\t\t\t\tdrone_velocity = [drone_velocityx[j], drone_velocityy[j], 0]\n\t\t\t\tevaluation[i][j] = self.Drone2Point(key_point_pos[i], 0, drone_pos[j], drone_velocity)\n\t\t\t\t\n\n\t\treturn evaluation\n\t\n\t'''\n\t#计算无人车对于周围位置点的态势评估矩阵\n\t#\n\tdef attack_to_point(self_data, ally_agents_data, enemy_agents_data, key_points):\n\t\t#无人车\n\t\tall_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n\t\tall_friend_agents_data.pop(\"231\")  # 剔除无人机数据，只考虑地面无人车平台\n\n\t\t#进攻方无人车信息\n\t\tall_friend_agent_pos = []\n\t\tall_friend_agent_blood = []\n\t\tall_friend_agent_velocity = []\n\t\tall_friend_agent_ammo = []\n\t\tall_friend_agent_ID = []\n\t\tall_friend_amount = 0\n\t\tfor agent_id, dict_value in all_friend_agents_data.items():\n\t\t\tall_friend_agent_ID.append(dict_value['ID']) #编号接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_velocity.append(dict_value['velocity']) #速度接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n\t\t\tall_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n\t\t\tall_friend_amount += 1\n\n\t\tevaluation = np.zeros((all_friend_amount, all_enemy_amount))\n\n\t\tfor i in range(all_enemy_amount):\n\t\t\tfor j in range(all_friend_amount):\n\t\t\t\tyaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n\t\t\t\t#UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n\t\t\t\tevaluation[i][j] = self.UAV2UAV(\"defensive\", all_enemy_agent_pos[i], all_enemy_agent_ammo[i], all_enemy_agent_blood[i], \n\t\t\t\tall_friend_agent_pos[j], all_friend_agent_velocity[j], yaw, 0, all_friend_agent_ammo[j], all_friend_agent_blood[j])\n\n\t\treturn evaluation\n\t'''\n\t#态势评估主函数\n\tdef evaluate(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n\t\td2a = self.defend_to_attack(self_data, ally_agents_data, enemy_agents_data, key_points)\n\t\ta2d = self.attack_to_defend(self_data, ally_agents_data, enemy_agents_data, key_points)\n\t\tu2d = self.uav_to_defend(self_data, ally_agents_data, enemy_agents_data, key_points)\n\t\treturn d2a, a2d, u2d\n\n\n\n\ndef test():\n    evaluator = Evaluation_module()\n\n    \n\n\n# test\nif __name__ == '__main__':\n\t\n\ttest()"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/red_strategy.py",
    "content": "import numpy as np\nimport math\nfrom scipy.optimize import linear_sum_assignment\n\n\ndef defense_combat(self_data, ally_agents_data, enemy_agents_data, key_points, blue_alive, red_alive):\n    # 防守方red小车最大速度\n    red_car_max_vel = 600\n    # 进攻方blue小车最大速度\n    blue_car_max_vel = 600\n    # 进攻方blue无人机最大速度\n    blue_drone_max_vel = 600\n    # 进攻方无人机占领夺控点胜利时间\n    time_to_win = 2.0\n    # 驱离载荷作用范围\n    expel_range = 1200\n    # 无人车打击距离\n    fire_dist = 2000\n\n    all_enemy_agent_pos = []\n    for agent_id, dict_value in enemy_agents_data.items():\n        all_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n    \"\"\"\n    all_enemy_agent_yaw = []\n    for agent_id, dict_value in enemy_agents_data.items():\n        if agent_id != '231':\n            all_enemy_agent_yaw.append([dict_value['Yaw']])\n    \"\"\"\n\n    if '311' in self_data.keys():\n        friend_agents_data = dict(self_data, **ally_agents_data)\n    if '311' in ally_agents_data.keys():\n        friend_agents_data = dict(ally_agents_data, **self_data)\n    all_friend_agent_pos = []\n    for agent_id, dict_value in friend_agents_data.items():\n        all_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n    \"\"\"\n    all_friend_agent_yaw = []\n    for agent_id, dict_value in friend_agents_data.items():\n        all_friend_agent_yaw.append([dict_value['Yaw']])\n    \"\"\"\n\n    target_location = np.array(key_points)\n    red_car_current_pos = np.array(all_friend_agent_pos)\n    blue_car_current_pos = np.array(all_enemy_agent_pos[0:2])\n    blue_drone_current_pos = np.array([all_enemy_agent_pos[-1]])\n    #red_car_current_yaw = np.array(all_friend_agent_yaw)\n    #blue_car_current_yaw = np.array(all_enemy_agent_yaw)\n    \"\"\"\n    blue_alive = []\n    for agent_id, dict_value in enemy_agents_data.items():\n        if agent_id is not '231':\n            if dict_value['Blood'] == 0:\n                blue_alive.append(False)\n            else:\n                blue_alive.append(True)\n\n    red_alive = []\n    for agent_id, dict_value in friend_agents_data.items():\n        if dict_value['Blood'] == 0:\n            red_alive.append(False)\n        else:\n            red_alive.append(True)\n    \"\"\"\n\n    blue_drone_dist_to_go = np.array([[np.linalg.norm(blue_drone_current_pos[a][:2] - target_location[b][:2]) for b in range(2)]\n                                      for a in range(1)])\n    blue_drone_time_to_go = blue_drone_dist_to_go / blue_drone_max_vel\n    if blue_drone_time_to_go[0][0] < 0.2:\n        blue_drone_time_to_go[0][0] = -1000\n    if blue_drone_time_to_go[0][1] < 0.2:\n        blue_drone_time_to_go[0][1] = -1000\n\n    red_car_dist_to_go = np.array(\n        [[np.linalg.norm(red_car_current_pos[a][:2] - target_location[b][:2]) - expel_range for b in range(2)]\n         for a in range(2)])\n    red_car_time_to_go = red_car_dist_to_go / red_car_max_vel\n    if red_alive[0] is False:\n        red_car_time_to_go[0] = 100000\n    elif red_alive[1] is False:\n        red_car_time_to_go[1] = 100000\n\n    red_car_next_pos = red_car_current_pos\n    if 0:  #np.sum(red_alive) == 0:\n        target_id = np.array([1, 1])\n        #red_car_next_yaw = red_car_current_yaw\n        #red_car_next_fire_yaw = np.array([[0], [0]])\n        #red_car_fire_flag = [False, False]\n    else:\n        if np.sum(red_alive) == 2 and np.sum(blue_alive) == 2:\n            red_blue_car_relative_dist = np.array(\n                [[np.linalg.norm(red_car_current_pos[a][:2] - blue_car_current_pos[b][:2]) for b in\n                  range(2)] for a in range(2)])\n            _, col_index = linear_sum_assignment(red_blue_car_relative_dist)\n            target_pos = blue_car_current_pos[col_index]\n            target_id = col_index\n        elif np.sum(red_alive) == 1 and np.sum(blue_alive) == 2:\n            if red_alive[0] is True:\n                red_blue_car_relative_dist = np.array(\n                    [[np.linalg.norm(red_car_current_pos[0][:2] - blue_car_current_pos[b][:2]) for b in range(2)]])\n                target_pos = np.array(\n                    [blue_car_current_pos[np.argmin(red_blue_car_relative_dist)], red_car_current_pos[1]])\n            elif red_alive[1] is True:\n                red_blue_car_relative_dist = np.array(\n                    [[np.linalg.norm(red_car_current_pos[1][:2] - blue_car_current_pos[b][:2]) for b in range(2)]])\n                target_pos = np.array(\n                    [red_car_current_pos[0], blue_car_current_pos[np.argmin(red_blue_car_relative_dist)]])\n            target_id = np.array([np.argmin(red_blue_car_relative_dist), np.argmin(red_blue_car_relative_dist)])\n        elif np.sum(red_alive) == 2 and np.sum(blue_alive) == 1:\n            if blue_alive[0] is True:\n                target_pos = np.array([blue_car_current_pos[0], blue_car_current_pos[0]])\n                target_id = np.array([0, 0])\n            elif blue_alive[1] is True:\n                target_pos = np.array([blue_car_current_pos[1], blue_car_current_pos[1]])\n                target_id = np.array([1, 1])\n        elif np.sum(red_alive) == 1 and np.sum(blue_alive) == 1:\n            if red_alive[0] is True:\n                if blue_alive[0] is True:\n                    target_pos = np.array([blue_car_current_pos[0], red_car_current_pos[1]])\n                    target_id = np.array([0, 0])\n                elif blue_alive[1] is True:\n                    target_pos = np.array([blue_car_current_pos[1], red_car_current_pos[1]])\n                    target_id = np.array([1, 1])\n            elif red_alive[1] is True:\n                if blue_alive[0] is True:\n                    target_pos = np.array([red_car_current_pos[0], blue_car_current_pos[0]])\n                    target_id = np.array([0, 0])\n                elif blue_alive[1] is True:\n                    target_pos = np.array([red_car_current_pos[0], blue_car_current_pos[1]])\n                    target_id = np.array([1, 1])\n        else:\n            red_car_next_pos = red_car_current_pos\n            target_id = np.array([1, 1])\n\n        blue_success_time = blue_drone_time_to_go + time_to_win*0.0\n        if (blue_success_time - np.min(red_car_time_to_go, axis=0, keepdims=True) >= 0).all() and np.sum(\n                blue_alive) > 0:\n            # 'offense'\n            red_car_next_pos = target_pos\n            flag = 'offense'\n        else:\n            # 'defense'\n            target_defense_index = np.argmin(\n                blue_success_time - np.min(red_car_time_to_go, axis=0, keepdims=True))\n            if red_alive[0] is True and red_alive[1] is True:\n                #red_car_next_pos = np.array(\n                #   [target_location[target_defense_index], target_location[target_defense_index]])\n                red_car_next_pos[0][:2] = blue_drone_current_pos[0][0:2]\n                red_car_next_pos[1][:2] = blue_drone_current_pos[0][0:2]\n            elif red_alive[0] is True and red_alive[1] is False:\n                #red_car_next_pos = np.array(\n                #    [target_location[target_defense_index], red_car_current_pos[1]])\n                red_car_next_pos[0][:2] = blue_drone_current_pos[0][0:2]\n            elif red_alive[0] is False and red_alive[1] is True:\n                #red_car_next_pos = np.array(\n                #    [red_car_current_pos[0], target_location[target_defense_index]])\n                red_car_next_pos[1][:2] = blue_drone_current_pos[0][0:2]\n            flag = 'defense'\n        \"\"\"\n        agent_yaw = [0, 0]\n        fire_yaw = [0, 0]\n        fire_flag = [False, False]\n        for index in range(2):\n            relative_dist = np.linalg.norm(red_car_current_pos[index] - target_pos[index])\n            relative_yaw = math.degrees(math.atan2((target_pos[index][1] - red_car_current_pos[index][1]),\n                                                   (target_pos[index][0] - red_car_current_pos[index][0])))\n            if red_alive[index] is True:\n                if relative_dist < fire_dist:\n                    red_car_next_pos[index] = red_car_current_pos[index]\n\n                    if ((relative_yaw < red_car_current_yaw[index] - 90) or (\n                            relative_yaw > red_car_current_yaw[index] + 90)):  # 在车的基础上，旋转打击载荷\n                        agent_yaw[index] = relative_yaw  # 直接旋转车朝向\n                        fire_yaw[index] = 0\n                    else:\n                        agent_yaw[index] = red_car_current_yaw[index]\n                        fire_yaw[index] = relative_yaw - red_car_current_yaw[index]\n                    fire_flag[index] = True\n                else:\n                    fire_flag[index] = False\n                    agent_yaw[index] = red_car_current_yaw[index]\n                    fire_yaw[index] = 0\n            else:\n                fire_flag[index] = False\n                agent_yaw[index] = red_car_current_yaw[index]\n                fire_yaw[index] = 0\n\n        red_car_next_yaw = np.array(agent_yaw)\n        red_car_next_fire_yaw = np.array(fire_yaw)\n        \"\"\"\n\n\n\n        if '311' in self_data.keys():\n            return red_car_next_pos[0], target_id[0], flag\n            #return [red_car_next_pos[0], red_car_next_yaw [0], red_car_next_fire_yaw[0], fire_flag[0]]\n        else:\n            return red_car_next_pos[1], target_id[1], flag\n            #return [red_car_next_pos[1], red_car_next_yaw [1], red_car_next_fire_yaw[1], fire_flag[1]]\n\nif __name__ == '__main__':\n    # 211和221是进攻方blue小车， 231是进攻方blue无人机\n    # 311和321是防守方red小车\n    # 当前要决策的防守方red小车的信息\n    self_data = {'321': {'X': -3.0, 'Y': 2.5, 'Z': 0, 'Yaw': 10, 'Blood': 100}}\n    # 其余防守方red小车的信息\n    ally_agents_data = {'311': {'X': 0.0, 'Y': 1.5, 'Z': 0, 'Yaw': 20, 'Blood': 100}}\n    # 进攻方blue小车和无人机信息\n    enemy_agents_data = {'211': {'X': 1.5, 'Y': -2.0, 'Z': 0, 'Yaw': 30, 'Blood': 100},\n                         '221': {'X': -2.5, 'Y': -2.5, 'Z': 0, 'Yaw': 40, 'Blood': 100},\n                         '231': {'X': 0.7, 'Y': 3.3, 'Z': 1.5, 'Yaw': 0}}\n    # 夺控点信息\n    key_points = [[0.7, 3.3, 0], [-3.0, -0.7, 0]]\n    # 存活状态\n    blue_alive = [True, True]\n    red_alive = [True, True]\n    target_position, target_id, flag = defense_combat(self_data, ally_agents_data, enemy_agents_data, key_points, blue_alive, red_alive)\n    print(target_position)\n    print('\\r\\n')\n    print(target_id)\n    print('\\r\\n')\n    print(flag)\n\n\n\n\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/stance.py",
    "content": "import copy\nimport random\nimport numpy as np\nimport datetime\nimport time\nimport math\n\n#态势评估模块\n#接口输入：全局状态信息，包括进攻方无人车各种状态，防守方无人车各种状态以及地图状态\n\nclass Evaluation_module():\n    def __init__(self, critical_points=[[-3, -0.7, 0], [0.7, 3.3, 0]]):\n        self.R0 = 1.5 * 100  # 距离态势缩放因子\n        self.V0 = 0.1  # 速度态势缩放因子\n        self.phi0 = np.pi / 4   # 俯仰角态势系数\n        self.psi0 = 0  # 偏航角态势系数\n        self.ammo0 = 0.15  # 载荷态势缩放因子(增函数用)\n        self.heal0 = 0.15  # 血量态势缩放因子(增函数用)\n        self.AMMO0 = 0.5  # 载荷态势缩放因子(减函数用)\n        self.HEAL0 = 0.5  # 血量态势缩放因子(减函数用)\n\n        # 已知的环境信息\n        self.critical_points = critical_points  # 夺控点位置\n    \n    # 计算相对速度态势时使用的增函数，输出区间为(0,1)\n    def SigmoidTen(self, x, c):\n        y = np.exp(-x/c)\n        return 1/(1+10*y)\n    \n    # 计算停留时间态势时使用的增函数，输出区间为[0,0.9)\n    def SigmoidNine(self, x, c):\n        y = np.exp(-x/c)\n        return 1/(1+9*y) - 0.1\n    \n    # 计算无人车相对坐标点的态势，以备最佳点规划使用，输入为点的坐标和此无人车的信息\n    # 在进行最佳点规划时，会计算待打击目标附近的几个敌方无人车的态势之和，以此来计算最佳点\n    def UAV2Point(self, p_position, position, velocity, phi, psi, ammo, health):\n        # 相对距离威胁\n        p_position = np.array(p_position)\n        position = np.array(position)\n        velocity = np.array(velocity)\n        r = p_position - position\n        dist = np.sqrt(np.sum(np.square(r)))\n        Sr = np.exp(-dist/self.R0)\n        # 相对速度威胁\n        V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n        Sv = self.SigmoidTen(V, self.V0)\n        # 俯仰角威胁\n        Sphi = np.exp(-np.abs(phi - self.phi0))\n        # 偏航角威胁\n        Spsi = np.exp(-np.abs(psi - self.psi0))\n        # 载荷威胁（增函数）载荷为0时威胁为0\n        # Sammo = self.SigmoidNine(ammo, self.ammo0)\n        Sammo = 1\n        # 强健度威胁（增函数） 血量为0时威胁为0\n        Sheal = self.SigmoidNine(health, self.heal0)\n\n        # 总态势计算 (系数之和不一定为1，每个系数直接在此处修改）\n        # 载荷威胁和强健度威胁此处用乘法，算法需要 [在打击范围内] 寻找总态势最小的点作为坐标点\n        S_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi + 0.0 * Spsi) * Sammo * Sheal\n        return S_sum\n\n    # 计算无人车相对无人车(智能体)的态势（威胁），以作为选取打击对象的依据（选取威胁大的但是优势低的）\n    # 其中a_position等表示智能体无人车的参数，即计算态势时考虑的主体的参数\n    def UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity, phi, psi, ammo, health):\n        a_position = np.array(a_position)\n        position = np.array(position)\n        velocity = np.array(velocity)\n        # 能力比例系数，如我方无人车面对敌方无人车时为1.5, 敌方无人车面对我方无人车时为0.67\n        # 己方载荷及健康态势计算（增函数）\n        # Mammo = self.SigmoidNine(a_ammo, self.ammo0)\n        Mammo = 1\n        Mhealth = self.SigmoidNine(a_health, self.heal0)\n        # 对方载荷及健康优势计算（减函数）\n        # Sammo = np.exp(-ammo / self.AMMO0)\n        Sammo = 1\n        Shealth = np.exp(-health / self.HEAL0)\n        # 进攻方优势计算(选择优势最大的进行打击，若相同则选择距离更近的进行打击）\n        if identity == \"offensive\":\n            # 相对距离威胁\n            r = a_position - position\n            dist = np.sqrt(np.sum(np.square(r)))\n            Sr = np.exp(-dist / self.R0)\n            S_offensive = 10 * Mammo*Mhealth * Sammo*Shealth * Sr  # 乘了系数10以致于S不过分小\n            return S_offensive\n        # 防守方优势计算（优先打击距离夺控点近的无人车）\n        if identity == \"defensive\":\n            Sr_temp = 0\n            for critical_point in self.critical_points:\n                r = critical_point - position\n                dist = np.sqrt(np.sum(np.square(r)))\n                Sr = np.exp(-dist / self.R0)\n                if Sr >= Sr_temp:\n                    Sr_temp = Sr\n            S_defensive = 10 * Mammo * Mhealth * Sammo * Shealth * Sr_temp\n            return S_defensive\n\n    # 计算无人机相对夺控点的态势，以此作为防守方是否进行驱离及选择谁对谁进行驱离的依据\n    def Drone2Point(self, p_position,p_ts, position, velocity):\n        # 相对距离威胁\n        p_position = np.array(p_position)\n        position = np.array(position)\n        velocity = np.array(velocity)\n        r = p_position - position\n        dist = np.sqrt(np.sum(np.square(r)))\n        Spr = np.exp(-dist / 1)   # 此处的放缩系数采用与无人机参数相关的\n        # 相对速度威胁\n        V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n        Spv = self.SigmoidTen(V, 0.2)   # 此处的放缩系数采用与无人机参数相关的\n        # 停留时间威胁\n        Spt = self.SigmoidNine(p_ts, 0.5)  # 不能接受无人机停留3秒及以上\n        # 计算综合态势\n        Sp = Spt + 0.6 * Spr + 0.2 * Spv  # 建议驱离阈值: Sp >= 0.5\n        print(Sp)\n        return Sp\n    \n    def UAV2Point_id(self, attacker_dict, key_point):\n        # 相对距离威胁\n        #进攻方无人车信息\n        ally_agent_pos = [attacker_dict['X'], attacker_dict['Y'], attacker_dict['Z']]\n        ally_agent_blood = attacker_dict['blood']\n        ally_agent_velocityx = attacker_dict['vx']\n        ally_agent_velocityy = attacker_dict['vy']\n        ally_agent_ammo = attacker_dict['ammo']\n        ally_agent_velocity = [ally_agent_velocityx, ally_agent_velocityy, 0]\n   \n        p_position = np.array(key_point)\n        position = np.array(ally_agent_pos)\n        velocity = np.array(ally_agent_velocity)\n        r = p_position - position\n        \n        phi = math.degrees(math.atan2((ally_agent_pos[0] - key_point[0]), (ally_agent_pos[1] - key_point[1])))\n        ammo = ally_agent_ammo\n        health = ally_agent_blood\n        # 相对速度威胁\n        dist = np.sqrt(np.sum(np.square(r)))\n        Sr = np.exp(-dist/self.R0)\n        V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n        Sv = self.SigmoidTen(V, self.V0)\n        # 偏航角威胁\n        Sphi = np.exp(-np.abs(phi - self.phi0))\n        # 俯仰角威胁\n        # Spsi = np.exp(-np.abs(psi - self.psi0))\n        # 载荷威胁（增函数）载荷为0时威胁为0\n        Sammo = self.SigmoidNine(ammo, self.ammo0)\n        # 强健度威胁（增函数） 血量为0时威胁为0\n        Sheal = self.SigmoidNine(health, self.heal0)\n\n        # 总态势计算 (系数之和不一定为1，每个系数直接在此处修改）\n        # 载荷威胁和强健度威胁此处用乘法，算法需要 [在打击范围内] 寻找总态势最小的点作为坐标点\n        # S_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi + 0.0 * Spsi) * Sammo * Sheal\n        S_sum = (0.6 * Sr + 0.2 * Sv + 0.2 * Sphi) * Sammo * Sheal\n        return S_sum\n\n    def UAV2UAV_id(self, identity, attacker_dict, defender_dict):\n        #进攻方无人车信息\n        ally_agent_pos = [attacker_dict['X'], attacker_dict['Y'], attacker_dict['Z']]\n        ally_agent_blood = attacker_dict['blood']\n        # ally_agent_ammo = attacker_dict['ammo']\n\n        enemy_agent_pos = [defender_dict['X'], defender_dict['Y'], defender_dict['Z']]\n        enemy_agent_blood = defender_dict['blood']\n        # enemy_agent_ammo = defender_dict['ammo']\n\n        a_position = np.array(ally_agent_pos)\n        position = np.array(enemy_agent_pos)\n        # a_ammo = ally_agent_ammo\n        a_health = ally_agent_blood\n        # ammo = enemy_agent_ammo\n        health = enemy_agent_blood\n\n        # 进攻方优势计算(选择优势最大的进行打击，若相同则选择距离更近的进行打击）\n        if identity == \"offensive\":\n            # 相对距离威胁\n            # Mammo = self.SigmoidNine(a_ammo, self.ammo0)\n            # Mhealth = np.exp(a_health / self.heal0)\n            Mhealth = np.exp(a_health/100)\n            # 对方载荷及健康优势计算（减函数）\n            # Sammo = np.exp(-ammo / self.AMMO0)\n            # Shealth = np.exp(-health / self.HEAL0)\n            Shealth = np.exp(-health/100)\n            r = a_position - position\n            dist = np.sqrt(np.sum(np.square(r)))\n            # Sr = np.exp(-dist / self.R0)\n            Sr = np.exp(-dist / 1000)\n            # S_offensive = 10 * Mammo*Mhealth * Sammo*Shealth * Sr  # 乘了系数10以致于S不过分小\n            S_offensive = 0.1 * Mhealth * Shealth * Sr  # 乘了系数10以致于S不过分小\n\n            return S_offensive\n        # 防守方优势计算（优先打击距离夺控点近的无人车）\n        if identity == \"defensive\":\n            Sr_temp = 0\n            Mammo = self.SigmoidNine(ammo, self.ammo0)\n            Mhealth = self.SigmoidNine(health, self.heal0)\n            # 对方载荷及健康优势计算（减函数）\n            Sammo = np.exp(-a_ammo / self.AMMO0)\n            Shealth = np.exp(-a_health / self.HEAL0)\n            for critical_point in self.critical_points:\n                r = critical_point - position\n                dist = np.sqrt(np.sum(np.square(r)))\n                Sr = np.exp(-dist / self.R0)\n                if Sr >= Sr_temp:\n                    Sr_temp = Sr\n            S_defensive = 10 * Mammo * Mhealth * Sammo * Shealth * Sr_temp\n            return S_defensive\n\n    def Drone2Point_id(self, drone_data, key_point):\n        drone_pos = [drone_data['X'], drone_data['Y']]\n        # drone_blood = drone_data['blood']\n        # drone_velocityx = drone_data['vx']\n        # drone_velocityy = drone_data['vy']\n        # drone_velocity = [drone_velocityx, drone_velocityy, 0]\n        # 相对距离威胁\n        p_position = np.array(key_point)\n        position = np.array(drone_pos)\n        # velocity = np.array(drone_velocity)\n        r = p_position - position\n        dist = np.sqrt(np.sum(np.square(r)))\n        Spr = np.exp(-dist / 100)   # 此处的放缩系数采用与无人机参数相关的\n        # 相对速度威胁\n        # V = np.dot(r, velocity) / dist  # 求速度在连线朝向上的投影\n        # Spv = self.SigmoidTen(V, 0.2)   # 此处的放缩系数采用与无人机参数相关的\n        # 停留时间威胁\n        # Spt = self.SigmoidNine(p_ts, 0.5)  # 不能接受无人机停留3秒及以上\n        # 计算综合态势\n        # Sp = Spt + 0.6 * Spr + 0.2 * Spv  # 建议驱离阈值: Sp >= 0.5\n        # Sp = 0.6 * Spr + 0.4 * Spv\n        Sp = Spr\n        return Sp\n\n\n    #计算防守方无人车相对于进攻方无人车的态势矩阵\n    #矩阵横轴维度为进攻方无人车数量，纵轴维度为防守方无人车数量\n    def defend_to_attack(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n        #无人车\n        all_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n        for agent_id, dict_value in all_friend_agents_data.items():\n            if 'blood' not in dict_value:\n                temp = agent_id\n        all_friend_agents_data.pop(temp)  # 剔除无人机数据，只考虑地面无人车平台\n\n        #进攻方无人车信息\n        all_friend_agent_pos = []\n        all_friend_agent_blood = []\n        all_friend_agent_velocityx = []\n        all_friend_agent_velocityy = []\n        all_friend_agent_ammo = []\n        all_friend_agent_ID = []\n        all_friend_amount = 0\n        for agent_id, dict_value in all_friend_agents_data.items():\n            all_friend_agent_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n            # all_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n            all_friend_amount += 1\n        \n        #防守方无人车信息\n        all_enemy_agent_pos = []\n        all_enemy_agent_blood = []\n        all_enemy_agent_velocityx = []\n        all_enemy_agent_velocityy = []\n        all_enemy_agent_ammo = []\n        all_enemy_agent_ID = []\n        all_enemy_amount = 0\n        for agent_id, dict_value in enemy_agents_data.items():\n            all_enemy_agent_ID.append(agent_id) \n            # all_enemy_agent_ammo.append(dict_value['ammo']) \n            all_enemy_agent_velocityx.append(dict_value['velocityx'])\n            all_enemy_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_enemy_agent_blood.append(dict_value['blood'])\n            all_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n            all_enemy_amount += 1\n\n        evaluation = np.zeros((all_friend_amount, all_enemy_amount))\n\n        for i in range(all_friend_amount):\n            for j in range(all_enemy_amount):\n                yaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n                # UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n                all_enemy_agent_velocity = [all_enemy_agent_velocityx[j], all_enemy_agent_velocityy[j], 0]\n                evaluation[i][j] = self.UAV2UAV(\"offensive\", all_friend_agent_pos[i], 0, all_friend_agent_blood[i],\n                all_enemy_agent_pos[j], all_enemy_agent_velocity, yaw, 0, 0, all_enemy_agent_blood[j])\n\n        return evaluation\n\n    #计算进攻方无人车相对于防守方无人车的态势矩阵\n    #矩阵横轴维度为防守方无人车数量，纵轴维度为进攻方无人车数量\n    def attack_to_defend(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n        #无人车\n        all_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n        for agent_id, dict_value in all_friend_agents_data.items():\n            if 'blood' not in dict_value:\n                temp = agent_id\n        all_friend_agents_data.pop(temp)  # 剔除无人机数据，只考虑地面无人车平台\n\n        #进攻方无人车信息\n        all_friend_agent_pos = []\n        all_friend_agent_blood = []\n        all_friend_agent_velocityx = []\n        all_friend_agent_velocityy = []\n        all_friend_agent_ammo = []\n        all_friend_agent_ID = []\n        all_friend_amount = 0\n        for agent_id, dict_value in all_friend_agents_data.items():\n            all_friend_agent_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n            #all_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n            all_friend_amount += 1\n\n        #防守方无人车信息\n        all_enemy_agent_pos = []\n        all_enemy_agent_blood = []\n        all_enemy_agent_velocityx = []\n        all_enemy_agent_velocityy = []\n        all_enemy_agent_ammo = []\n        all_enemy_agent_ID = []\n        all_enemy_amount = 0\n        for agent_id, dict_value in enemy_agents_data.items():\n            all_enemy_agent_ID.append(agent_id) \n            #all_enemy_agent_ammo.append(dict_value['ammo']) \n            all_enemy_agent_velocityx.append(dict_value['velocityx'])\n            all_enemy_agent_velocityy.append(dict_value['velocityy'])\n            all_enemy_agent_blood.append(dict_value['blood'])\n            all_enemy_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']])\n            all_enemy_amount += 1\n\n        evaluation = np.zeros((all_enemy_amount, all_friend_amount))\n\n        for i in range(all_enemy_amount):\n            for j in range(all_friend_amount):\n                yaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n                # UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n                all_friend_agent_velocity = [all_friend_agent_velocityx[j], all_friend_agent_velocityy[j], 0]\n                evaluation[i][j] = self.UAV2UAV(\"defensive\", all_enemy_agent_pos[i], 0, all_enemy_agent_blood[i], \n                all_friend_agent_pos[j], all_friend_agent_velocity, yaw, 0, 0, all_friend_agent_blood[j])\n\n        return evaluation\n\n    #计算无人机对于夺控点位置的态势矩阵\n    #矩阵横轴代表夺控点，纵轴代表无人机\n    def uav_to_defend(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n        all_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n        for agent_id, dict_value in all_friend_agents_data.items():\n            if 'blood' not in dict_value:\n                temp1 = agent_id\n                temp2 = dict_value\n        \n        drone_data = {}\n        drone_data[temp1] = temp2\n\n        #无人机信息\n        drone_pos = []\n        drone_velocityx = []\n        drone_velocityy = []\n        drone_ID = []\n        drone_amount = 0\n        for agent_id, dict_value in drone_data.items():\n            drone_ID.append(agent_id) #编号接口，形式参照丘老师代码，正确性存疑\n            drone_velocityx.append(dict_value['velocityx']) #速度接口，形式参照丘老师代码，正确性存疑\n            drone_velocityy.append(dict_value['velocityy']) #速度接口，形式参照丘老师代码，正确性存疑\n            drone_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n            drone_amount += 1\n\n        #夺控点位置\n        key_point_amount = 0\n        key_point_pos = []\n        for key_point in key_points:\n            key_point_pos.append(key_point)\n            key_point_amount += 1\n\n        evaluation = np.zeros((key_point_amount, drone_amount))\n\n        for i in range(key_point_amount):\n            for j in range(drone_amount):\n                # Drone2Point(self, p_position,p_ts, position, velocity)\n                # print(self.Drone2Point(key_point_pos[i], 0, drone_pos[j], drone_velocity[j]))\n                drone_velocity = [drone_velocityx[j], drone_velocityy[j], 0]\n                evaluation[i][j] = self.Drone2Point(key_point_pos[i], 0, drone_pos[j], drone_velocity)\n                \n\n        return evaluation\n    \n    '''\n    #计算无人车对于周围位置点的态势评估矩阵\n    #\n    def attack_to_point(self_data, ally_agents_data, enemy_agents_data, key_points):\n        #无人车\n        all_friend_agents_data = dict(self_data, **ally_agents_data)  # 进攻方所有智能体数据\n        all_friend_agents_data.pop(\"231\")  # 剔除无人机数据，只考虑地面无人车平台\n\n        #进攻方无人车信息\n        all_friend_agent_pos = []\n        all_friend_agent_blood = []\n        all_friend_agent_velocity = []\n        all_friend_agent_ammo = []\n        all_friend_agent_ID = []\n        all_friend_amount = 0\n        for agent_id, dict_value in all_friend_agents_data.items():\n            all_friend_agent_ID.append(dict_value['ID']) #编号接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_ammo.append(dict_value['ammo']) #载荷接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_velocity.append(dict_value['velocity']) #速度接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_blood.append(dict_value['blood']) #血量接口，形式参照丘老师代码，正确性存疑\n            all_friend_agent_pos.append([dict_value['X'], dict_value['Y'], dict_value['Z']]) #位置接口，参照丘老师代码编写\n            all_friend_amount += 1\n\n        evaluation = np.zeros((all_friend_amount, all_enemy_amount))\n\n        for i in range(all_enemy_amount):\n            for j in range(all_friend_amount):\n                yaw = math.degrees(math.atan2((all_enemy_agent_pos[j][0] - all_friend_agent_pos[i][0]), (all_enemy_agent_pos[j][1] - all_friend_agent_pos[i][1])))\n                #UAV2UAV(self, identity, a_position,a_ammo,a_health, position,velocity,phi,psi,ammo,health)\n                evaluation[i][j] = self.UAV2UAV(\"defensive\", all_enemy_agent_pos[i], all_enemy_agent_ammo[i], all_enemy_agent_blood[i], \n                all_friend_agent_pos[j], all_friend_agent_velocity[j], yaw, 0, all_friend_agent_ammo[j], all_friend_agent_blood[j])\n\n        return evaluation\n    '''\n    #态势评估主函数\n    def evaluate(self, self_data, ally_agents_data, enemy_agents_data, key_points):\n        d2a = self.defend_to_attack(self_data, ally_agents_data, enemy_agents_data, key_points)\n        a2d = self.attack_to_defend(self_data, ally_agents_data, enemy_agents_data, key_points)\n        u2d = self.uav_to_defend(self_data, ally_agents_data, enemy_agents_data, key_points)\n        return d2a, a2d, u2d\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/uhmap_bb.py",
    "content": "import copy\nfrom math import sqrt\nimport numpy as np\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom config import GlobalConfig\n\nclass DummyAlgConfig():\n    reserve = \"\"\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\nclass DummyAlgorithmT2(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n\n            AirCarrier = State_Recall['Latest-Team-Info'][thread]['dataArr'][AirCarrierUID]\n            if AirCarrier['agentAlive']:\n                assert 'RLA_UAV' in AirCarrier['type'] \n                landmarks = State_Recall['Latest-Team-Info'][thread]['dataGlobal']['keyObjArr']\n\n                squredis = lambda a,b: sqrt(\n                    (a['agentLocation']['x']-b['location']['x'])**2 + \n                    (a['agentLocation']['y']-b['location']['y'])**2 + \n                    (a['agentLocation']['z']-b['location']['z'])**2 )\n                AirCarrirSquareDisToEachLandmark = [squredis(AirCarrier, landmark) for landmark in landmarks]\n                nearLandmark = np.argmin(AirCarrirSquareDisToEachLandmark)\n\n                pos_lm = np.array([\n                    landmarks[nearLandmark]['location']['x'],\n                    landmarks[nearLandmark]['location']['y'],\n                    landmarks[nearLandmark]['location']['z'],\n                ])\n\n                pos_ac_proj = np.array([\n                    AirCarrier['agentLocation']['x'],\n                    AirCarrier['agentLocation']['y'],\n                    landmarks[nearLandmark]['location']['z'],\n                ])\n\n                unit_2ac_prj = (pos_ac_proj - pos_lm) / np.linalg.norm(pos_ac_proj - pos_lm)\n                p = unit_2ac_prj*400 + pos_lm\n\n      \n                actions[thread, :] = encode_action_as_digits('PatrolMoving', 'N/A', x=p[0], y=p[1], z=p[2], UID=None, T=None, T_index=None)\n            else:\n                actions[thread, :] = encode_action_as_digits('N/A', 'N/A', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            \n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\nclass DummyAlgorithmT1(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            landmarks = State_Recall['Latest-Team-Info'][thread]['dataGlobal']['keyObjArr']\n            px = landmarks[0]['location']['x']\n            py = landmarks[0]['location']['y']\n            for a in range(self.n_agent):\n                if not State_Recall['Latest-Team-Info'][thread]['dataArr'][a]['agentAlive']: continue\n                pz = State_Recall['Latest-Team-Info'][thread]['dataArr'][a]['agentLocation']['z']\n                \n                actions[thread, a] = encode_action_as_digits('SpecificMoving', 'N/A', x=px, y=py, z=pz, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\n\nclass DummyAlgorithmIdle(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n\n            # AirCarrier = State_Recall['Latest-Team-Info'][thread]['dataArr'][AirCarrierUID]\n            # if AirCarrier['agentAlive']:\n            #     assert 'RLA_UAV' in AirCarrier['type'] \n            #     landmarks = State_Recall['Latest-Team-Info'][thread]['dataGlobal']['keyObjArr']\n\n            #     squredis = lambda a,b: sqrt(\n            #         (a['agentLocation']['x']-b['location']['x'])**2 + \n            #         (a['agentLocation']['y']-b['location']['y'])**2 + \n            #         (a['agentLocation']['z']-b['location']['z'])**2 )\n            #     AirCarrirSquareDisToEachLandmark = [squredis(AirCarrier, landmark) for landmark in landmarks]\n            #     nearLandmark = np.argmin(AirCarrirSquareDisToEachLandmark)\n\n            #     px = landmarks[nearLandmark]['location']['x']\n            #     py = landmarks[nearLandmark]['location']['y']\n            #     pz = landmarks[nearLandmark]['location']['z']\n            #     actions[thread, :] = encode_action_as_digits('PatrolMoving', 'N/A', x=px, y=py, z=pz, UID=None, T=None, T_index=None)\n            # else:\n            #     actions[thread, :] = encode_action_as_digits('N/A', 'N/A', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            \n\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                actions[thread, :] = encode_action_as_digits('N/A', 'N/A', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            else:\n                actions[thread, :] = encode_action_as_digits('Idle', 'StaticAlert', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/uhmap_island.py",
    "content": "from cmath import isinf, pi\nfrom turtle import done\nimport numpy as np\nimport math\nfrom MISSION.uhmap.actionset_v3 import strActionToDigits, ActDigitLen\nfrom config import GlobalConfig\n\nclass DummyAlgConfig():\n    reserve = \"\"\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.team = team\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team_agent_uid = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[team]\n        self.demo_type = GlobalConfig.ScenarioConfig.DemoType\n        if self.demo_type == 'AirShow' or 'AirAttack':\n            self.phase = 1\n        if self.demo_type == 'AirAttack':\n            self.TargetPosition = []\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        \n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\nclass DummyAlgorithmIdle(DummyAlgorithmBase):\n    \n\n    '''\n        福建省东北角大致方位  (-17500,-19500)\n        福建省西南角大致方位  (-22500,-5000)\n        注意：0°对应x轴正方向,90°对应y轴正方向\n        plane_rotaion = [方位角，俯仰角，翻滚角]\n    '''\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, ActDigitLen))\n        \n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            # 此处代码仅做demo用\n            act_dic = ActionDictionary\n            if self.demo_type == \"AirShow\":\n                '''\n                飞行场景设计：\n                初始位置: x/y按照索引设定, 初始高度一致为1000米, 初始俯仰角为0, 初始滚转角为0, 初始偏航角按照索引设定\n                1.上升到统一高度5000m, 欧拉角全部设为0\n                2.方位角先变到-90°, 后变到0°, 在此期间高度上升至10000m\n                3.方位角变到180°\n                4.方位角先变到-90°, 后变到180°, 在此期间高度下降至5000m\n                5.方位角变到0°\n                6.重复2~5动作\n                '''\n                \n                for id in range(self.n_agent):\n                    cruise_height = 15000\n                    cruise_speed = 600\n                    plane_location = State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentLocationArr']\n                    plane_rotaion = State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentRotationArr']\n                    \n                    if self.phase == 1:\n                        if np.abs(0 - plane_rotaion[1]) < 1:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 2)\n                            print(\"Change Height!\")\n                        else:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 6)\n                            print(\"Change Direction!\")\n                        if np.abs(5000 - plane_location[2]) < 0.1 and np.abs(0 - plane_rotaion[0]) < 1:\n                            self.phase += 1\n                            print(\"Stage1 Done\")\n\n                    elif self.phase == 2:\n                        print(\"Stage2!\")\n                        if np.abs(0 - plane_rotaion[1]) < 1:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 3)\n                        else:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 12)\n                        if np.abs(-90 - plane_rotaion[0]) < 1:\n                            self.phase += 0.5\n                    elif self.phase == 2.5:\n                        actions[thread, id] = act_dic.select_act('PlaneAgent', 6)\n                        if np.abs(10000 - plane_location[2]) < 0.1 and np.abs(0 - plane_rotaion[0]) < 1:\n                            self.phase += 0.5\n                            print(\"Stage2 Done\")\n\n\n                    elif self.phase == 3:\n                        actions[thread, id] = act_dic.select_act('PlaneAgent', 10)\n                        if np.abs(180 - plane_rotaion[0]) < 1:\n                            self.phase += 1\n                            print(\"Stage3 Done\")\n                    \n                    elif self.phase == 4:\n                        print(\"Stage4!\")\n                        if np.abs(0 - plane_rotaion[1]) < 1:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 2)\n                        else:\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', 12)\n                        if np.abs(-90 - plane_rotaion[0]) < 1:\n                            self.phase += 0.5\n                    elif self.phase == 4.5:\n                        print(self.phase)\n                        actions[thread, id] = act_dic.select_act('PlaneAgent', 10)\n                        if np.abs(5000 - plane_location[2]) < 10 and np.abs(180 - plane_rotaion[0]) < 1:\n                            self.phase += 0.5\n                            print(\"Stage4 Done\")\n                    \n                    elif self.phase == 5:\n                        actions[thread, id] = act_dic.select_act('PlaneAgent', 6)\n                        if np.abs(0 - plane_rotaion[0]) < 1:\n                            self.phase = 2\n                            print(\"Stage5 Done\")\n                    \n\n\n            elif self.demo_type == \"AirAttack\":\n                '''\n                全流程demo演示:\n                1. 飞机一直跟踪至目标方位，并上升到巡航高度(暂定15000m)\n                2. 在开始上升后，开始加速动作，一直到巡航速度(暂定600m/s)\n                3. 到达巡航高度后，飞机进行巡航，慢慢抵达目标\n                4. 接近目标后，降低高度到打击高度(暂定5000m) (预计打击半径暂定为50,000m)\n                5. 到达指定高度后，始终将目标方向作为期望航迹方位角方向\n                6. 到达目标后，与目标进行交互打击，若目标被摧毁则在原地盘旋\n                7. 打击后汇合\n  \n                '''\n\n                # 获取目标坐标及参数设置\n                if self.phase <= 1:\n                    for id in range(100,105):\n                        self.TargetPosition.append(State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentLocationArr'])\n                cruise_height = 15000\n                cruise_speed = 600\n                attack_height = 5000\n                ready_radius = 50000\n                # 执行飞行器脚本\n                for id in range(self.n_agent - 5):\n                    if State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentAlive']:\n                        plane_location = State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentLocationArr']\n                        plane_rotaion = State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['agentRotationArr']\n                        \n                        # 分配目标\n                        index = math.floor(id / (self.n_agent - 5) * 5)\n                        target_location = np.array((self.TargetPosition[index]))\n\n                        delta_location =  target_location - plane_location\n                        target_pitch = self.DeltaLocation2Angle(delta_location[0], delta_location[1])\n                        \n                        # 1.\n                        if self.phase == 1:\n                            if np.abs(cruise_height - plane_location[2]) < 5:\n                                self.phase += 1\n                                print(\"Stage1 Done\")\n                            elif np.abs(0 - plane_rotaion[1]) < 1:\n                                action_num = self.TargetHeight2Action(cruise_height)\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n                                print(\"Change Height to 15000m!\")\n                            else:\n                                # 跟踪目标位置\n                                delta_location =  target_location - plane_location\n                                target_pitch = self.DeltaLocation2Angle(delta_location[0], delta_location[1]) \n                                action_num = self.TargetAngle2Action(target_pitch)\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n                        \n                        # 2.\n                        if self.phase == 2:\n                            speed = float(State_Recall['Latest-Team-Info'][thread]['dataArr'][id]['rSVD1'])\n                            if np.abs(cruise_speed - speed) < 1:\n                                self.phase += 1\n                                print(\"Stage2 Done\")\n                            else:\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', 14)\n\n                        # 3. \n                        if self.phase == 3:\n                            delta_location =  target_location - plane_location\n                            if np.sqrt(np.sum(np.square((delta_location[0], delta_location[1])))) <= ready_radius:\n                                self.phase += 1\n                                print(\"Stage3 Done\")\n                            else:\n                                # 跟踪目标位置\n                                delta_location =  target_location - plane_location\n                                target_pitch = self.DeltaLocation2Angle(delta_location[0], delta_location[1]) \n                                action_num = self.TargetAngle2Action(target_pitch)\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n                        \n                        # 4.\n                        if self.phase == 4:\n                            if np.abs(attack_height - plane_location[2]) < 5:\n                                self.phase += 1\n                                print(\"Stage4 Done\")\n                            elif np.abs(0 - plane_rotaion[1]) < 1:\n                                action_num = self.TargetHeight2Action(attack_height)\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n                                print(\"Change Height to 5000m!\")\n                            else:\n                                # 跟踪目标位置\n                                delta_location =  target_location - plane_location\n                                target_pitch = self.DeltaLocation2Angle(delta_location[0], delta_location[1]) \n                                action_num = self.TargetAngle2Action(target_pitch)\n                                actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n                        \n                        # 5.\n                        if self.phase == 5:\n                            # 跟踪目标位置\n                            delta_location =  target_location - plane_location\n                            target_pitch = self.DeltaLocation2Angle(delta_location[0], delta_location[1]) \n                            action_num = self.TargetAngle2Action(target_pitch)\n                            actions[thread, id] = act_dic.select_act('PlaneAgent', action_num)\n\n\n                        # 5.接近目标，对目标发射导弹\n                        # dist_2D = np.sqrt(np.sum(np.square((delta_location[0], delta_location[1]))))\n                        # if dist_2D < 10000:\n                        #     actions[thread, id] = strActionToDigits('ActionSet3::LaunchMissile;NONE')\n\n                        # print(target_pitch)\n                        # actions[thread, :] = strActionToDigits('ActionSet3::ChangeDirection;{}'.format(target_pitch))\n                        # print(State_Recall['Latest-Team-Info'][thread]['dataArr'][0]['agentRotationArr']) \n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n    def DeltaLocation2Angle(self, delta_x, delta_y):\n        '''\n        将输入的距离差向量转换为方位角度\n        此处为角度制\n        '''\n        # assert len(delta_location) == 2 or 3\n        # delta_x = delta_location[0]\n        # delta_y = delta_location[1]\n        if delta_x == 0 and delta_y != 0:\n            theta = 90 if delta_y > 0 else -90\n        else:\n            abs_theta = np.arctan(np.abs(delta_y) / np.abs(delta_x)) * 180 / pi\n\n            if delta_x > 0 and delta_y >= 0:\n                theta = abs_theta\n            elif delta_x < 0 and delta_y >= 0:\n                theta = 180 - abs_theta\n            elif delta_x > 0 and delta_y < 0:\n                theta = - abs_theta\n            elif delta_x < 0 and delta_y < 0:    \n                theta = abs_theta - 180\n        return theta\n        \n    def TargetAngle2Action(self, target_yaw):\n        '''\n        将输入的期望角度转化为对应的离散动作\n        此处为角度制\n        '''\n        action_yaw_set = np.array([0, 45, 90, 135, 180, -135, -90, -45])\n        delta_action_yaw_set = np.abs(action_yaw_set - target_yaw)\n\n        output_num = None\n        for i, element in enumerate(delta_action_yaw_set):\n            if element <= 22.5 or element >= (180+135+22.5):\n                output_num = i\n                break\n        # if output_num is None:\n        #     print('离散方位角动作设置或者程序逻辑有问题！')\n        #     print(target_yaw)\n        #     print(delta_action_yaw_set)\n\n        assert output_num is not None, '离散方位角动作设置或者程序逻辑有问题！'\n        return output_num + 6\n    def TargetHeight2Action(self, target_height):\n        '''\n        将输入的期望高度转化为对应的离散动作\n        '''\n        action_height_set = np.array([1000, 5000, 10000, 15000, 20000])\n        delta_action_height_set = np.abs(action_height_set - target_height)\n        height_threshold = np.abs(action_height_set[-1] - action_height_set[-2]) / 2\n\n        output_num = None\n        for i, element in enumerate(delta_action_height_set):\n            if element <= height_threshold:\n                output_num = i\n                break\n\n        assert output_num is not None, '离散高度动作设置或者程序逻辑有问题！'\n        return output_num + 1\n\n\n\nclass ActionDictionary():\n    '''\n        Height Space(5): 20000m, 15000m, 10000m, 5000m, 1000m\n        Direction Space(8): 45°, 90°, 135°, 180°, -135°, -90°, -45°, 0°\n        Speed Space(2): Positive, Negative\n    '''\n    # Direction Space(16): 22.5°, 45°, 67.5°, 90°, 112.5°, 135°, 157.5°, 180°, -157.5°, -135°, -112.5°, -90°, -67.5°, -45°, -22.5°, 0°\n    # Speed Space(10): 150, 200, 250, 300, 350, 400, 450, 500, 550, 600\n    \n    dictionary_args = [\n    'N/A;N/A',                           # 0\n    \n    'ChangeHeight;1000',                 # 1\n    'ChangeHeight;5000',                 # 2\n    'ChangeHeight;10000',                # 3\n    'ChangeHeight;15000',                # 4\n    'ChangeHeight;20000',                # 5\n\n    'ChangeDirection;0',                 # 6\n    'ChangeDirection;45',                # 7\n    'ChangeDirection;90',                # 8\n    'ChangeDirection;135',               # 9\n    'ChangeDirection;180',               # 10\n    'ChangeDirection;-135',              # 11\n    'ChangeDirection;-90',               # 12\n    'ChangeDirection;-45',               # 13\n\n    'ChangeSpeed;Positive',               # 14\n    'ChangeSpeed;Negative',               # 15\n    \n    ]\n\n\n    @staticmethod\n    def select_act(type, a):\n        if type =='PlaneAgent':\n            args = ActionDictionary.dictionary_args[a]\n            return strActionToDigits(f'ActionSet3::{args}')\n\n    @staticmethod     \n    def get_avail_act():\n        pass\n\n\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/uhmap_ls.py",
    "content": "import copy\nimport numpy as np\nfrom UTIL.tensor_ops import distance_mat_between\nfrom scipy.optimize import linear_sum_assignment\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom config import GlobalConfig\n\nclass DummyAlgConfig():\n    reserve = \"\"\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.team = team\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team_agent_uid = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[team]\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        \n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\nclass DummyAlgorithmSeqFire(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            # 如果,该线程没有停止\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                # 如果该线程刚刚reset\n                opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n                opp_uid_range = list(copy.deepcopy(opp_uid_range))\n                np.random.shuffle(opp_uid_range)\n                self.attack_order[thread] = opp_uid_range\n            # 当前的Episode步数\n            step_cnt = State_Recall['Current-Obs-Step'][thread]\n            # 当前的info\n            info = State_Recall['Latest-Team-Info']\n            raw_info = State_Recall['Latest-Team-Info'][thread]['dataArr']\n            \n            # 判断agent是否存活\n            def uid_alive(uid):\n                return raw_info[uid]['agentAlive']\n\n            for uid in self.attack_order[thread]:\n                if uid_alive(uid):\n                    # 如果该敌方存活，则集火攻击（:）\n                    actions[thread, :] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=uid, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\n\nclass DummyAlgorithmIdle(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                actions[thread, :] = encode_action_as_digits('Idle', 'AggressivePersue', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            else:\n                actions[thread, :] = encode_action_as_digits('N/A', 'N/A', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\nclass DummyAlgorithmMarch(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n\n        if not hasattr(self, 'march_direction'):\n            self.march_direction = '+Y'\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                a_agent_uid = self.team_agent_uid[0]\n                self.march_direction = '+Y' if State_Recall['Latest-Team-Info'][thread]['dataArr'][a_agent_uid]['agentLocation']['y'] <0 else '-Y'\n                actions[thread, :] = encode_action_as_digits('Idle', 'AggressivePersue', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n            else:\n\n                if self.march_direction == '+Y':\n                    actions[thread, :] = encode_action_as_digits('PatrolMoving', 'Dir+Y', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n                else:\n                    actions[thread, :] = encode_action_as_digits('PatrolMoving', 'Dir-Y', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\n\n\n\n\n\ndef assign_opponent(opp_pos_arr, opp_id_arr, leader_pos_arr, leader_id_arr):\n    result = {}\n    dis_mat = distance_mat_between(leader_pos_arr, opp_pos_arr)\n    dis_mat[dis_mat == np.inf] = 1e10\n    indices, assignments = linear_sum_assignment(dis_mat)\n    for i, j, a in zip(range(len(indices)), indices, assignments):\n        assert i == j\n        result[leader_id_arr[i]] = opp_id_arr[a]\n    return result\n\nclass DummyAlgorithmLinedAttack(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        n_active_thread = sum(ENV_ACTIVE)\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            actions[thread] = self.decide_each_thread(\n                thread = thread,\n                step_cnt = State_Recall['Current-Obs-Step'][thread],\n                raw_info = State_Recall['Latest-Team-Info'][thread]['dataArr'],\n                Env_Suffered_Reset = State_Recall['Env-Suffered-Reset'][thread]\n            )\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n    # 判断agent是否存活\n    def uid_alive(raw_info, uid):\n        return raw_info[uid]['agentAlive']\n\n    def decide_each_thread(self, **kwargs):\n        act_each_agent = np.zeros(shape=( self.n_agent, 8 ))\n        self_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        Env_Suffered_Reset = kwargs['Env_Suffered_Reset']\n        thread = kwargs['thread']\n        # 当前的Episode步数\n        step_cnt = kwargs['step_cnt']\n        raw_info = kwargs['raw_info']\n\n        # # 如果,该线程没有停止\n        # if Env_Suffered_Reset:\n        #     # 如果该线程刚刚reset\n        #     opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n        #     opp_uid_range = list(copy.deepcopy(opp_uid_range))\n        #     np.random.shuffle(opp_uid_range)\n        #     self.attack_order[thread] = opp_uid_range\n\n        opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n        pos_arr_2d = np.array([_info['agentLocationArr'][:2] for _info in raw_info])\n        opp_pos_arr = pos_arr_2d[opp_uid_range]\n\n        self_air_uid_range = [info['uId'] for info in raw_info if info['agentAlive'] and  info['agentTeam'] == self.team and info['type']=='RLA_UAV_Support']\n        N_leader = len(self_air_uid_range)\n        self_ground_uid_range = [info['uId'] for info in raw_info if info['agentAlive'] and info['agentTeam'] == self.team and info['type']!='RLA_UAV_Support']\n        if N_leader > 0:\n            self_air_pos_arr = pos_arr_2d[self_air_uid_range]\n            assignments = assign_opponent(\n                opp_pos_arr=opp_pos_arr, \n                opp_id_arr=opp_uid_range, \n                leader_pos_arr = self_air_pos_arr, \n                leader_id_arr=self_air_uid_range\n            )\n\n\n\n            for group in range(N_leader):\n                attack_uid = assignments[self_air_uid_range[group]]\n                group_member_uids = [uid for uid in self_ground_uid_range if uid%N_leader==group]\n\n                for group_member_uid in group_member_uids:\n                    agent_team_index = raw_info[group_member_uid]['indexInTeam']\n                    act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n                leader_uid = self_air_uid_range[group]\n                agent_team_index = raw_info[leader_uid]['indexInTeam']\n                z_leader = raw_info[leader_uid]['agentLocationArr'][2]\n                if len(group_member_uids) > 0:\n                    team_center_pos = pos_arr_2d[group_member_uid]\n                    act_each_agent[agent_team_index] = encode_action_as_digits('PatrolMoving', 'N/A', x=team_center_pos[0], y=team_center_pos[1], z=z_leader, UID=None, T=None, T_index=None)\n                else:\n                    act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n            return act_each_agent\n        else:\n            center_pos_kd = pos_arr_2d[self_ground_uid_range].mean(0, keepdims=True)\n            dis = distance_mat_between(center_pos_kd, opp_pos_arr)\n            target_index = np.argmin(dis.squeeze())\n            attack_uid = opp_uid_range[target_index]\n\n            group_member_uids = self_ground_uid_range\n\n            for group_member_uid in group_member_uids:\n                agent_team_index = raw_info[group_member_uid]['indexInTeam']\n                act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', \n                    x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n            return act_each_agent\n\ndef vector_shift_towards(pos, toward_pos, offset):\n    delta = toward_pos - pos \n    delta = delta / (np.linalg.norm(delta) + 1e-10)\n    return pos + delta * offset\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ALGORITHM/script_ai/uhmap_ls_mp.py",
    "content": "import copy, atexit\nimport numpy as np\nfrom UTIL.tensor_ops import distance_mat_between\nfrom scipy.optimize import linear_sum_assignment\nfrom MISSION.uhmap.actset_lookup import encode_action_as_digits\nfrom config import GlobalConfig\n\nclass DummyAlgConfig():\n    reserve = \"\"\n\nclass DummyAlgorithmBase():\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        self.n_agent = n_agent\n        self.n_thread = n_thread\n        self.team = team\n        self.ScenarioConfig = GlobalConfig.ScenarioConfig\n        self.attack_order = {}\n        self.team_agent_uid = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[team]\n\n    def forward(self, inp, state, mask=None):\n        raise NotImplementedError\n\n    def to(self, device):\n        return self\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8))\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        \n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\nclass DummyAlgorithmSeqFire(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        \n        n_active_thread = sum(ENV_ACTIVE)\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            # 如果,该线程没有停止\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                # 如果该线程刚刚reset\n                opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n                opp_uid_range = list(copy.deepcopy(opp_uid_range))\n                np.random.shuffle(opp_uid_range)\n                self.attack_order[thread] = opp_uid_range\n            # 当前的Episode步数\n            step_cnt = State_Recall['Current-Obs-Step'][thread]\n            # 当前的info\n            info = State_Recall['Latest-Team-Info']\n            raw_info = State_Recall['Latest-Team-Info'][thread]['dataArr']\n            \n            # 判断agent是否存活\n            def uid_alive(uid):\n                return raw_info[uid]['agentAlive']\n\n            for uid in self.attack_order[thread]:\n                if uid_alive(uid):\n                    # 如果该敌方存活，则集火攻击（:）\n                    actions[thread, :] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=uid, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\n\nclass DummyAlgorithmIdle(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                actions[thread, :] = encode_action_as_digits('Idle', 'AggressivePersue', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n            else:\n                actions[thread, :] = encode_action_as_digits('N/A', 'N/A', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\nclass DummyAlgorithmMarch(DummyAlgorithmBase):\n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n\n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n\n        n_active_thread = sum(ENV_ACTIVE)\n        AirCarrierUID = 2\n\n        # assert len(State_Recall['Latest-Obs']) == n_active_thread, ('make sure we have the right batch of obs')\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n\n        if not hasattr(self, 'march_direction'):\n            self.march_direction = '+Y'\n\n        for thread in range(self.n_thread):\n            if ENV_PAUSE[thread]: \n                # 如果,该线程停止，不做任何处理\n                continue\n\n            if State_Recall['Env-Suffered-Reset'][thread]:\n                a_agent_uid = self.team_agent_uid[0]\n                self.march_direction = '+Y' if State_Recall['Latest-Team-Info'][thread]['dataArr'][a_agent_uid]['agentLocation']['y'] <0 else '-Y'\n                actions[thread, :] = encode_action_as_digits('Idle', 'AggressivePersue', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n            else:\n\n                if self.march_direction == '+Y':\n                    actions[thread, :] = encode_action_as_digits('PatrolMoving', 'Dir+Y', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n                else:\n                    actions[thread, :] = encode_action_as_digits('PatrolMoving', 'Dir-Y', x=None, y=None, z=None, UID=None, T=None, T_index=None)\n\n\n\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n\n\n\n\n\n\n\ndef assign_opponent(opp_pos_arr, opp_id_arr, leader_pos_arr, leader_id_arr):\n    result = {}\n    dis_mat = distance_mat_between(leader_pos_arr, opp_pos_arr)\n    dis_mat[dis_mat == np.inf] = 1e10\n    indices, assignments = linear_sum_assignment(dis_mat)\n    for i, j, a in zip(range(len(indices)), indices, assignments):\n        assert i == j\n        result[leader_id_arr[i]] = opp_id_arr[a]\n    return result\n\n\n\n\nclass ThreadDecisionMaker():\n    \n    def apply_context(self, kwargs):\n        for k in kwargs:\n            setattr(self, k, kwargs[k])\n    \n    def decide_each_thread(self, kwargs):\n        act_each_agent = np.zeros(shape=( self.n_agent, 8 ))\n        if kwargs['env_pause']: return act_each_agent\n        self_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[self.team]\n\n        Env_Suffered_Reset = kwargs['Env_Suffered_Reset']\n        thread = kwargs['thread']\n        # 当前的Episode步数\n        step_cnt = kwargs['step_cnt']\n        raw_info = kwargs['raw_info']\n         \n        # # 如果,该线程没有停止\n        # if Env_Suffered_Reset:\n        #     # 如果该线程刚刚reset\n        #     opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n        #     opp_uid_range = list(copy.deepcopy(opp_uid_range))\n        #     np.random.shuffle(opp_uid_range)\n        #     self.attack_order[thread] = opp_uid_range\n\n        opp_uid_range = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM[1-self.team]\n        pos_arr_2d = np.array([_info['agentLocationArr'][:2] for _info in raw_info])\n        opp_pos_arr = pos_arr_2d[opp_uid_range]\n\n        self_air_uid_range = [info['uId'] for info in raw_info if info['agentAlive'] and  info['agentTeam'] == self.team and info['type']=='RLA_UAV_Support']\n        N_leader = len(self_air_uid_range)\n        self_ground_uid_range = [info['uId'] for info in raw_info if info['agentAlive'] and info['agentTeam'] == self.team and info['type']!='RLA_UAV_Support']\n        if N_leader > 0:\n            self_air_pos_arr = pos_arr_2d[self_air_uid_range]\n            assignments = assign_opponent(\n                opp_pos_arr=opp_pos_arr, \n                opp_id_arr=opp_uid_range, \n                leader_pos_arr = self_air_pos_arr, \n                leader_id_arr=self_air_uid_range\n            )\n\n\n\n            for group in range(N_leader):\n                attack_uid = assignments[self_air_uid_range[group]]\n                group_member_uids = [uid for uid in self_ground_uid_range if uid%N_leader==group]\n\n                for group_member_uid in group_member_uids:\n                    agent_team_index = raw_info[group_member_uid]['indexInTeam']\n                    act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n                leader_uid = self_air_uid_range[group]\n                agent_team_index = raw_info[leader_uid]['indexInTeam']\n                z_leader = raw_info[leader_uid]['agentLocation']['z']\n                if len(group_member_uids) > 0:\n                    team_center_pos = pos_arr_2d[group_member_uid]\n                    act_each_agent[agent_team_index] = encode_action_as_digits('PatrolMoving', 'N/A', x=team_center_pos[0], y=team_center_pos[1], z=z_leader, UID=None, T=None, T_index=None)\n                else:\n                    act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n            return act_each_agent\n        else:\n            center_pos_kd = pos_arr_2d[self_ground_uid_range].mean(0, keepdims=True)\n            dis = distance_mat_between(center_pos_kd, opp_pos_arr)\n            target_index = np.argmin(dis.squeeze())\n            attack_uid = opp_uid_range[target_index]\n\n            group_member_uids = self_ground_uid_range\n\n            for group_member_uid in group_member_uids:\n                agent_team_index = raw_info[group_member_uid]['indexInTeam']\n                act_each_agent[agent_team_index] = encode_action_as_digits('SpecificAttacking', 'N/A', \n                    x=None, y=None, z=None, UID=attack_uid, T=None, T_index=None)\n\n            return act_each_agent\n\n\n\nclass DummyAlgorithmLinedAttack(DummyAlgorithmBase):\n    def __init__(self, n_agent, n_thread, space, mcv=None, team=None):\n        super().__init__(n_agent, n_thread, space, mcv, team)\n        \n        sync_state = [self.__dict__.copy()]*self.n_thread\n        # multi-thread decision making\n        from UTIL.shm_pool import SmartPool\n        self.process_pool = SmartPool(fold=1, proc_num=self.n_thread, base_seed=0)\n        self.process_pool.add_target(name='DT%d'%self.team, lam=ThreadDecisionMaker)\n        atexit.register(self.process_pool.party_over)  # failsafe, handles shm leak\n        self.process_pool.exec_target(\n            name='DT%d'%self.team, \n            dowhat='apply_context',\n            args_list=sync_state\n        )\n    \n    def interact_with_env(self, State_Recall):\n        assert State_Recall['Latest-Obs'] is not None, ('make sure obs is ok')\n        \n        ENV_PAUSE = State_Recall['ENV-PAUSE']\n        ENV_ACTIVE = ~ENV_PAUSE\n\n        assert self.n_thread == len(ENV_ACTIVE), ('the number of thread is wrong?')\n        n_active_thread = sum(ENV_ACTIVE)\n\n        actions = np.zeros(shape=(self.n_thread, self.n_agent, 8 ))\n\n        kwargs_L = [{\n            \"env_pause\": ENV_PAUSE[thread],\n            \"thread\" : thread,\n            \"step_cnt\" : State_Recall['Current-Obs-Step'][thread],\n            \"raw_info\" : State_Recall['Latest-Team-Info'][thread]['dataArr'],\n            \"Env_Suffered_Reset\" : State_Recall['Env-Suffered-Reset'][thread]\n        } for thread in range(self.n_thread)]\n\n\n\n        actions = self.process_pool.exec_target(\n            name='DT%d'%self.team, \n            dowhat='decide_each_thread',\n            args_list=kwargs_L\n        )\n        actions = np.stack(actions)\n        # set actions of in-active threads to NaN (will be done again in multi_team.py, this line is not necessary)\n        actions[ENV_PAUSE] = np.nan\n        # swap (self.n_thread, self.n_agent) -> (self.n_agent, self.n_thread) \n        actions = np.swapaxes(actions, 0, 1)\n        return actions, {}\n\n    # 判断agent是否存活\n    def uid_alive(raw_info, uid):\n        return raw_info[uid]['agentAlive']\n\n\n\ndef vector_shift_towards(pos, toward_pos, offset):\n    delta = toward_pos - pos \n    delta = delta / (np.linalg.norm(delta) + 1e-10)\n    return pos + delta * offset\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Ankur Deka\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/common/base_env.py",
    "content": "import numpy as np\nclass BaseEnv(object):\n    def __init__(self, rank) -> None:\n        self.observation_space = None\n        self.action_space = None\n        self.rank = rank\n\n    def step(self, act):\n        # obs: a Tensor with shape (n_agent, ...)\n        # reward: a Tensor with shape (n_agent, 1) or (n_team, 1)\n        # done: a Bool\n        # info: a dict\n        raise NotImplementedError\n        # Warning: if you have only one team and RewardAsUnity, \n        # you must make sure that reward has shape=[n_team=1, 1]\n        # e.g. \n        # >> RewardForTheOnlyTeam = +1\n        # >> RewardForAllTeams = np.array([RewardForTheOnlyTeam, ])\n        # >> return (ob, RewardForAllTeams, done, info)\n        return (ob, RewardForAllTeams,  done, info)  # choose this if RewardAsUnity\n        return (ob, RewardForAllAgents, done, info)  # choose this if not RewardAsUnity\n\n    def reset(self):\n        # obs: a Tensor with shape (n_agent, ...)\n        # info: a dict\n        raise NotImplementedError\n        return ob, info\n\n\nclass RawObsArray(object):\n    raw_obs_size = {}   # shared\n    def __init__(self, key='default'):\n        self.key = key\n        if self.key not in self.raw_obs_size:\n            self.guards_group = []\n            self.nosize = True\n        else:\n            self.guards_group = np.zeros(shape=(self.raw_obs_size[self.key]), dtype=np.float32)\n            self.nosize = False\n            self.p = 0\n\n    def append(self, buf):\n        if self.nosize:\n            self.guards_group.append(buf)\n        else:\n            L = len(buf)\n            self.guards_group[self.p:self.p+L] = buf[:]\n            self.p += L\n\n    def get(self):\n        if self.nosize:\n            self.guards_group = np.concatenate(self.guards_group)\n            self.raw_obs_size[self.key] = len(self.guards_group)\n        return self.guards_group\n\n    def get_group_size(self):\n        return len(self.guards_group)\n\n    def get_raw_obs_size(self):\n        assert self.key in self.raw_obs_size\n        return self.raw_obs_size[self.key]"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/env_router.py",
    "content": "import_path_ref = {\n    \"collective_assult\": (\"MISSION.collective_assult.collective_assult_parallel_run\",          'ScenarioConfig'),\n    \"dca_multiteam\": (\"MISSION.dca_multiteam.collective_assult_parallel_run\",                   'ScenarioConfig'),\n    \"collective_assult_debug\": (\"MISSION.collective_assult_debug.collective_assult_parallel_run\", 'ScenarioConfig'),\n    \"air_fight\": (\"MISSION.air_fight.environment.air_fight_compat\",                            'ScenarioConfig'),\n    \"native_gym\": (\"MISSION.native_gym.native_gym_config\",                                     'ScenarioConfig'),\n    \"starcraft2\": (\"MISSION.starcraft.sc2_env_wrapper\",                                        'ScenarioConfig'),\n    \"sc2\": (\"MISSION.starcraft.sc2_env_wrapper\",                                               'ScenarioConfig'),\n    \"unity_game\": (\"MISSION.unity_game.unity_game_wrapper\",                                    'ScenarioConfig'),\n    \"sr_tasks->cargo\": (\"MISSION.sr_tasks.multiagent.scenarios.cargo\",                         'ScenarioConfig'),\n    \"sr_tasks->hunter_invader\": (\"MISSION.sr_tasks.multiagent.scenarios.hunter_invader\",       'ScenarioConfig'),\n    \"sr_tasks->hunter_invader3d\": (\"MISSION.sr_tasks.multiagent.scenarios.hunter_invader3d\",   'ScenarioConfig'),\n    \"sr_tasks->hunter_invader3d_v2\": (\"MISSION.sr_tasks.multiagent.scenarios.hunter_invader3d_v2\",'ScenarioConfig'),\n    \"bvr\": (\"MISSION.bvr_sim.init_env\",                                                        'ScenarioConfig'),\n    \"mathgame\": (\"MISSION.math_game.env\",                                                      'ScenarioConfig'),\n    \"uhmap\": (\"MISSION.uhmap.uhmap_env_wrapper\",                                               'ScenarioConfig'),\n}\n\nenv_init_function_ref = {\n    \"collective_assult\": (\"MISSION.collective_assult.collective_assult_parallel_run\",          'make_collective_assult_env'),\n    \"dca_multiteam\": (\"MISSION.dca_multiteam.collective_assult_parallel_run\",                  'make_collective_assult_env'),\n    \"collective_assult_debug\": (\"MISSION.collective_assult_debug.collective_assult_parallel_run\", 'make_collective_assult_env'),\n    \"air_fight\": (\"MISSION.air_fight.environment.air_fight_compat\",                            'make_air_fight_env'),\n    \"native_gym\": (\"MISSION.native_gym.native_gym_config\",                                     'env_init_function'),\n    \"starcraft2\": (\"MISSION.starcraft.sc2_env_wrapper\",                                        'make_sc2_env'),\n    \"sc2\": (\"MISSION.starcraft.sc2_env_wrapper\",                                               'make_sc2_env'),\n    \"unity_game\": (\"MISSION.unity_game.unity_game_wrapper\",                                    'make_env'),\n    \"sr_tasks\": (\"MISSION.sr_tasks.multiagent.scenario\",                                       'sr_tasks_env'),\n    \"bvr\": (\"MISSION.bvr_sim.init_env\",                                                        'make_bvr_env'),\n    \"mathgame\": (\"MISSION.math_game.env\",                                                      'make_math_env'),\n    \"uhmap\": (\"MISSION.uhmap.uhmap_env_wrapper\",                                               'make_uhmap_env'),\n}\n\n##################################################################################################################################\n##################################################################################################################################\nfrom config import GlobalConfig\nimport importlib, os\nfrom UTIL.colorful import print亮蓝\n\n\n\ndef load_ScenarioConfig():\n    if GlobalConfig.env_name not in import_path_ref:\n        assert False, ('need to find path of ScenarioConfig')\n    import_path, ScenarioConfig = import_path_ref[GlobalConfig.env_name]\n    GlobalConfig.ScenarioConfig = getattr(importlib.import_module(import_path), ScenarioConfig)\n\n\ndef make_env_function(env_name, rank):\n    load_ScenarioConfig()\n    ref_env_name = env_name\n\n    if 'native_gym' in env_name:\n        assert '->' in env_name\n        ref_env_name, env_name = env_name.split('->')\n    elif 'sr_tasks' in env_name:\n        assert '->' in env_name\n        ref_env_name, env_name = env_name.split('->')\n\n    import_path, func_name = env_init_function_ref[ref_env_name]\n    env_init_function = getattr(importlib.import_module(import_path), func_name)\n    return lambda: env_init_function(env_name, rank)\n\n\n\ndef make_parallel_envs(process_pool, marker=''):\n    from UTIL.shm_env import SuperpoolEnv\n    from config import GlobalConfig\n    from MISSION.env_router import load_ScenarioConfig\n    load_ScenarioConfig()\n    \n    env_args_dict_list = [({\n        'env_name':GlobalConfig.env_name, \n        'proc_index':i if 'test' not in marker else -(i+1), \n        'marker':marker\n    },) for i in range(GlobalConfig.num_threads)]\n\n    if GlobalConfig.env_name == 'air_fight':\n        # This particular env has a dll file \n        # that must be loaded in main process\n        # 艹tmd有个dll必须在主进程加载\n        from MISSION.air_fight.environment.pytransform import pyarmor_runtime\n        pyarmor_runtime()\n\n    if GlobalConfig.env_name == 'bvr':\n        # 1、如果没用hmp的docker，请设置好 YOUR_ROOT_PASSWORD，不止这一处，请全局搜索\"YOUR_ROOT_PASSWORD\"替换所有\n        # 2、用docker的sock挂载到容器中，方法在SetupDocker.md中\n        print亮蓝('[env_router]: here goes the docker in docker check.')\n        YOUR_ROOT_PASSWORD = 'clara'  # the sudo password\n        os.system(\"echo %s|sudo -S date\"%YOUR_ROOT_PASSWORD) # get sudo power\n        res = os.popen(\"sudo docker ps\").read()\n        if \"CONTAINER ID\" not in res:\n            print亮蓝('[env_router]: Error checking docker in docker, can not control host docker interface!')\n            raise \"Error checking docker in docker, can not control host docker interface!\"\n        pass\n\n    if GlobalConfig.env_name == 'collective_assult_debug':\n        # This particular env has a cython file that needs to be compiled in main process\n        # that must be loaded in main process\n        from MISSION.collective_assult_debug.cython_func import laser_hit_improve3\n    if GlobalConfig.env_name == 'dca_multiteam':\n        # This particular env has a cython file that needs to be compiled in main process\n        # that must be loaded in main process\n        from MISSION.dca_multiteam.cython_func import laser_hit_improve3\n    if GlobalConfig.env_name == 'uhmap':\n        # This particular env has a cython file that needs to be compiled in main process\n        # that must be loaded in main process\n        from MISSION.uhmap.SubTasks.cython_func import tear_number_apart\n    \n    if GlobalConfig.num_threads > 1:\n        envs = SuperpoolEnv(process_pool, env_args_dict_list)\n    else:\n        envs = SuperpoolEnv(process_pool, env_args_dict_list)\n\n    return envs\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/readme.md",
    "content": "# Task Configuration Core Fields:\n\n## Parameter Internal Relationship\n* You may notice some configuration field ends with ```_cv```, they are parameters chained with other parameters. For example, when changing the ```map```, the limit of ```episode_length``` and the number of agents ```N_AGENT_EACH_TEAM``` are implicated and also need to be changed. \nTo make it simple, we add ```episode_length_cv``` and ```N_AGENT_EACH_TEAM_cv``` to record this link with lambda function.\n\n* When parameters (e.g. ```map```) that are bind to other parameters are changed, \nthe Transparent Parameter Control (TPC) module will scan and parse variables with twin variables that end with ```_cv```, and automatically modify their values. (refer to ./UTIL/config_args.py)\n\nGenerally, you can safely ignore them and only pay attention to fields below.\n\n## Fields\n|  Field   | Value  | Explaination  | zh Explaination  |\n|  ----    | ----   | ----     |  ----  |\n| N_TEAM   | ```int```    | the number of agent teams in the tasks, information cannot be shared between different team | 队伍数量，每个队伍被一个ALGORITHM模块控制，队伍之间不可共享信息。大多数任务中，队伍之间是敌对关系  |\n| N_AGENT_EACH_TEAM    | ```list (of int)``` | the number of agents in each team | 每个队伍的智能体数量  |\n| AGENT_ID_EACH_TEAM   | ```list of list (of int)``` | the ID of agents in each team, double layer list, must agree with N_AGENT_EACH_TEAM! | 每个队伍的智能体的ID，双层列表，必须与N_AGENT_EACH_TEAM对应!  |\n| TEAM_NAMES    | ```list (of string)``` | use which ALGORITHM to control each team, fill the path of chosen algorithm and its main class name, e.g.```\"ALGORITHM.conc.foundation->ReinforceAlgorithmFoundation\"``` | 选择每支队伍的控制算法，填写控制算法主模块的路径和类名|\n| RewardAsUnity    | ```bool``` | Shared reward, or each agent has individual reward signal | 每个队伍的智能体共享集体奖励（True），或者每个队伍的智能体都独享个体奖励（False）  |\n| ObsAsUnity    | ```bool``` | Agents do not has individual observation, only shared collective observation | 没有个体观测值，整个群体的观测值获取方式如同单智能体问题一样  |\n| StateProvided    | ```bool``` | Whether the global state is provided in training. If True, the Algorithm can access both ```obs``` and ```state``` during training  | 是否在训练过程中提供全局state  |\n\n\n\n\n# * How to Introduce a New Mission Environment\n\n### Step 1: Declare Mission Info (how many agents and actions, maximum episode steps et.al.)\n- make a folder under ```./MISSION```, e.g. ```./MISSION/uhmap.```\n- make a py file, e.g. ```./MISSION/uhmap/uhmap_env_wrapper.py```\n- in ```uhmap_env_wrapper.py```, copy and paste following template:\n\n```python\nfrom UTIL.config_args import ChainVar\n\n# please register this ScenarioConfig into MISSION/env_router.py\nclass ScenarioConfig(object):  \n    '''\n        ScenarioConfig: This config class will be 'injected' with new settings from JSONC.\n        (E.g., override configs with ```python main.py --cfg example.jsonc```)\n        (As the name indicated, ChainVars will change WITH vars it 'chained_with' during config injection)\n        (please see UTIL.config_args to find out how this advanced trick works out.)\n    '''\n    n_team1agent = 5\n\n    # <Part 1> Needed by the hmp core #\n    N_TEAM = 1\n\n    N_AGENT_EACH_TEAM = [n_team1agent,]\n    N_AGENT_EACH_TEAM_cv = ChainVar(lambda n_team1agent: [n_team1agent,], chained_with=['n_team1agent'])\n\n    AGENT_ID_EACH_TEAM = [range(0,n_team1agent),]\n    AGENT_ID_EACH_TEAM_cv = ChainVar(lambda n_team1agent: [range(0,n_team1agent),], chained_with=['n_team1agent'])\n\n    TEAM_NAMES = ['ALGORITHM.None->None',]\n\n    '''\n        ## If the length of action array == the number of teams, set ActAsUnity to True\n        ## If the length of action array == the number of agents, set ActAsUnity to False\n    '''\n    ActAsUnity = False\n\n    '''\n        ## If the length of reward array == the number of agents, set RewardAsUnity to False\n        ## If the length of reward array == 1, set RewardAsUnity to True\n    '''\n    RewardAsUnity = True\n\n    '''\n        ## If the length of obs array == the number of agents, set ObsAsUnity to False\n        ## If the length of obs array == the number of teams, set ObsAsUnity to True\n    '''\n    ObsAsUnity = False\n\n    # <Part 2> Needed by env itself #\n    MaxEpisodeStep = 100\n    render = False\n\n    # <Part 3> Needed by some ALGORITHM #\n    StateProvided = False\n    AvailActProvided = False\n    EntityOriented = False\n\n    n_actions = 2\n    obs_vec_length = 10\n\n```\n\n### Step 2: Writing Environment\n\n- For convenience, please copy and paste ```class BaseEnv(object)``` into your script:\n```python\nclass BaseEnv(object):\n    def __init__(self, rank) -> None:\n        self.observation_space = None\n        self.action_space = None\n        self.rank = rank\n\n    def step(self, act):\n        # obs: a Tensor with shape (n_agent, ...)\n        # reward: a Tensor with shape (n_agent, 1) or (n_team, 1)\n        # done: a Bool\n        # info: a dict\n        raise NotImplementedError\n        # Warning: if you have only one team and RewardAsUnity, \n        # you must make sure that reward has shape=[n_team=1, 1]\n        # e.g. \n        # >> RewardForTheOnlyTeam = +1\n        # >> RewardForAllTeams = np.array([RewardForTheOnlyTeam, ])\n        # >> return (ob, RewardForAllTeams, done, info)\n        return (ob, RewardForAllTeams,  done, info)  # choose this if RewardAsUnity\n        return (ob, RewardForAllAgents, done, info)  # choose this if not RewardAsUnity\n\n    def reset(self):\n        # obs: a Tensor with shape (n_agent, ...)\n        # done: a Bool\n        raise NotImplementedError\n        return ob, info\n\n```\n\n- Then create a class that inherit from it (```class UhmapEnv(BaseEnv)```):\n```python\nclass UhmapEnv(BaseEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.id = rank\n        self.render = ScenarioConfig.render and (self.id==0)\n        self.n_agents = ScenarioConfig.n_team1agent\n        # self.observation_space = ?\n        # self.action_space = ?\n        if ScenarioConfig.StateProvided:\n            # self.observation_space['state_shape'] = ?\n            pass\n        if self.render:\n            # render init\n            pass\n```\n- Next, it is time to write your own code of ```step()``` and ```reset()``` function.\nThere is little we can help about that, as it is your custom environment after all.\n\n### Step 3: Write a Function to Initialize the Environment\nA empty function getting a instance of environment, it will used in step 4. \nBut don'y worry, two lines of code will do:\n```python\n# please register this into MISSION/env_router.py\ndef make_uhmap_env(env_id, rank):\n    return UhmapEnv(rank)\n```\n\n### Step 4: Make Everything Kiss Together\nThis step will make HMP aware of the existence of this new MISSION.\n- Open ```MISSION/env_router.py```\n- Add the path of environment's configuration in ```import_path_ref```\n``` python\nimport_path_ref = {\n    \"uhmap\": (\"MISSION.uhmap.uhmap_env_wrapper\", 'ScenarioConfig'),\n}   \n```\n- Add the path of environment's init function in ```env_init_function_ref```, e.g.:\n``` python\nenv_init_function_ref = {\n    \"uhmap\": (\"MISSION.uhmap.uhmap_env_wrapper\", \"make_uhmap_env\"),\n}   \n```\n\n### Step 5: Write a Config Override to Start Experiment\nCreate a ```exp.jsonc``` or ```json``` file, \ncopy and paste following content, and please pay attention to lines marked with ```***```, they are the most important ones:\n```jsonc\n{\n    // config HMP core\n    \"config.py->GlobalConfig\": {\n        \"note\": \"uhmp-dev\",\n        \"env_name\": \"uhmap\", // *** the selection of MISSION\n        \"env_path\": \"MISSION.uhmap\", // *** confirm the path of env (a fail safe)\n        \"draw_mode\": \"Img\",\n        \"num_threads\": \"1\",\n        \"report_reward_interval\": \"1\",\n        \"test_interval\": \"128\",\n        \"test_epoch\": \"4\",\n        \"device\": \"cuda\",\n        \"max_n_episode\": 500000,\n        \"fold\": \"4\",\n        \"backup_files\": [\n        ]\n    },\n\n    // config MISSION\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {  // *** must kiss with \"env_name\" and \"env_path\"\n        // remember this? declared in ScenarioConfig class in ./MISSION/math_game/uhmap.py.\n        \"n_team1agent\": 4,\n        \"n_actions\": 10,\n        \"StateProvided\": false,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.conc_4hist.foundation->ReinforceAlgorithmFoundation\"  // *** select ALGORITHMs\n        ]\n    },\n\n    // config ALGORITHMs\n    \"ALGORITHM.conc_4hist.foundation.py->AlgorithmConfig\": { // must kiss with \"TEAM_NAMES\"\n        \"train_traj_needed\": \"16\",\n        \"prevent_batchsize_oom\": \"True\",\n        \"n_focus_on\": 3,\n        \"lr\": 0.0005,\n        \"ppo_epoch\": 24,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"gamma\": 0.99\n    }\n}\n```\n\nAt last, run experiment with ```python main.py --cfg ./path-to-exp-json/exp.jsonc```.\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/SubtaskCommonFn.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .cython_func import tear_num_arr\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\n\n\n\nclass UhmapCommonFn(UhmapEnv):\n\n    def reset(self):\n        \"\"\"\n            Reset function, it delivers reset command to unreal engine to spawn all agents\n            环境复位,每个episode的开始会执行一次此函数中会初始化所有智能体\n        \"\"\"\n        super().reset()\n        self.t = 0\n        pos_ro = np.random.rand()*2*np.pi\n        # spawn agents\n        AgentSettingArray = []\n\n        # count the number of agent in each team\n        n_team_agent = {}\n        for i, agent_info in enumerate(self.SubTaskConfig.agent_list):\n            team = agent_info['team']\n            if team not in n_team_agent: n_team_agent[team] = 0\n            self.SubTaskConfig.agent_list[i]['uid'] = i\n            self.SubTaskConfig.agent_list[i]['tid'] = n_team_agent[team]\n            n_team_agent[team] += 1\n            \n        self.n_team_agent = n_team_agent\n        # push agent init info one by one\n        for i, agent_info in enumerate(self.SubTaskConfig.agent_list):\n            team = agent_info['team']\n            agent_info['n_team_agent'] = n_team_agent[team]\n            init_fn = getattr(self, agent_info['init_fn_name'])\n            AgentSettingArray.append(init_fn(agent_info, pos_ro))\n\n        self.agents  = [Agent(team=a['team'], team_id=a['tid'], uid=a['uid']) for a in self.SubTaskConfig.agent_list]\n        \n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'NumAgents' : len(self.SubTaskConfig.agent_list),\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        # assert resp['dataGlobal']['levelName'] == 'UhmapLargeScale'\n\n        assert len(resp['dataArr']) == len(AgentSettingArray), \"Illegal agent initial position. 非法的智能体初始化位置，一部分智能体没有生成.\"\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n        \"\"\"\n            step 函数,act中包含了所有agent的决策\n        \"\"\"\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if self.SubTaskConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif self.SubTaskConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n        elif self.SubTaskConfig.ActionFormat == 'ASCII':            \n            act_send = [digitsToStrAction(a) for a in act]\n        else:\n            act_send = [digitsToStrAction(a) for a in act]\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        \"\"\"\n            解析环境返回的一些关键事件,\n            如智能体阵亡,某队伍胜利等等。\n            关键事件需要在ue中进行定义.\n            该设计极大地简化了python端奖励的设计流程,\n            减小了python端的运算量。\n        \"\"\"\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            # if event_parsed['Event'] == 'Destroyed':\n            #     team = self.find_agent_by_uid(event_parsed['UID']).team\n            #     reward[team]    -= 0.05    # this team\n            #     reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                WaterdropWin = False\n                WaterdropRank = False\n                WaterdropReward = 0\n                ShipWin = -1\n                ShipRank = -1\n                ShipReward = 0\n                EndReason = event_parsed['EndReason']\n                # According to MISSION\\uhmap\\SubTasks\\UhmapWaterdropConf.py, team 0 is Ship team, team 1 is Waterdrop team\n                if EndReason == \"ShipNumLessThanTheshold\" or EndReason == \"Team_0_AllDead\":\n                    WaterdropWin = True; WaterdropRank = 0; WaterdropReward = 1\n                    ShipWin = False; ShipRank = 1; ShipReward = -1\n                elif EndReason == \"TimeMaxCntReached\" or EndReason == \"Team_1_AllDead\":\n                    WaterdropWin = False; WaterdropRank = 1; WaterdropReward = -1\n                    ShipWin = True; ShipRank = 0; ShipReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [ShipRank, WaterdropRank], \"end_reason\": EndReason}\n\n                reward = [ShipReward, WaterdropReward]\n        # print(reward)\n        return reward, WinningResult\n\n    def step_skip(self):\n        \"\"\"\n            跳过一次决策,无用的函数\n        \"\"\"\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n    def find_agent_by_uid(self, uid):\n        \"\"\"\n            用uid查找智能体(带缓存加速机制)\n        \"\"\"\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        \"\"\"\n            粗解析智能体的观测,例如把死智能体的位置替换为inf(无穷远),\n            将智能体的agentLocation从字典形式转变为更简洁的(x,y,z)tuple形式\n        \"\"\"\n        assert resp['valid']\n        resp['dataGlobal']['distanceMat'] = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n        \n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n\n        info_dict = resp\n        for info in info_dict['dataArr']: \n            alive = info['agentAlive']\n\n            if alive:\n                agentLocation = info.pop('agentLocation')\n                agentRotation = info.pop('agentRotation')\n                agentVelocity = info.pop('agentVelocity')\n                agentScale = info.pop('agentScale')\n                info['agentLocationArr'] = (agentLocation['x'], agentLocation['y'], agentLocation['z'])\n                info['agentVelocityArr'] = (agentVelocity['x'], agentVelocity['y'], agentVelocity['z'])\n                info['agentRotationArr'] = (agentRotation['yaw'], agentRotation['pitch'], agentRotation['roll'])\n                info['agentScaleArr'] = (agentScale['x'], agentScale['y'], agentScale['z'])\n                info.pop('previousAction')\n                info.pop('availActions')\n                # info.pop('rSVD1')\n                info.pop('interaction')\n            else:\n                inf = float('inf')\n                info['agentLocationArr'] = (inf, inf, inf)\n                info['agentVelocityArr'] = (inf, inf, inf)\n                info['agentRotationArr'] = (inf, inf, inf)\n\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_ship(self, agent_info, pos_ro):\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        tid = agent_info['tid'] # tid 是智能体在队伍中的编号 \n        uid = agent_info['uid'] # uid 是智能体在仿真中的唯一编号\n        \n        x = -2000\n        y = (tid * 1000) # tid 是智能体在队伍中的编号 \n        z = 500 # \n\n        agent_property = copy.deepcopy(self.SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  500,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # custom args\n                'RSVD1': '',\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n\n\n    def init_waterdrop(self, agent_info, pos_ro):\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = +2000\n        y = (tid * 200)\n        z = 500 # \n\n        agent_property = copy.deepcopy(self.SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  1000,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # custom args\n                'RSVD1': '-MyCustomArg1=abc -MyCustomArg2=12345',\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapAdversial.py",
    "content": "import json, copy, re, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapAdversialConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\nfrom .SubtaskCommonFn import UhmapCommonFn\n\n\n\n\nclass UhmapAdversial(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.05    # this team\n                reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    agents_left_each_team = [0 for _ in range(self.n_teams)]\n                    for a in self.agents:\n                        if a.alive: agents_left_each_team[a.team] += 1\n                    WinTeam = np.argmax(agents_left_each_team)\n\n                    # <<1>> The alive agent number is EQUAL\n                    if agents_left_each_team[WinTeam] == agents_left_each_team[1-WinTeam]:\n                        hp_each_team = [0 for _ in range(self.n_teams)]\n                        for a in self.agents:\n                            if a.alive: hp_each_team[a.team] += a.hp\n                        WinTeam = np.argmax(hp_each_team)\n\n                        # <<2>> The alive agent HP sum is EQUAL\n                        if hp_each_team[WinTeam] == hp_each_team[1-WinTeam]:\n                            WinTeam = -1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += 1\n                    reward[1-WinTeam] -= 1\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-1 for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 1\n        self.N_Obj = len(self.key_obj)\n        if self.N_Obj > 0:\n            OBJ_UID_OFFSET = 32768\n\n            obs_arr = RawObsArray(key = 'GameObj')\n\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])\n            OBS_GameObj = OBS_GameObj[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_ground(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed': 600,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.75,  'y': 0.75, 'z': 0.75, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 45,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\": 2500,\n                \"GuardRange\":      1700,\n                \"FireRange\":       1400,\n                # debugging\n                'RSVD1': '',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;AsFarAsPossible',\n                # agent hp\n                'AgentHp':np.random.randint(low=90,high=110),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_ground_tank(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  400,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.75,  'y': 0.75, 'z': 0.75, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 75,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000,\n                \"GuardRange\":       1400,\n                \"FireRange\":        750 ,\n                # debugging\n                'RSVD1': '',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;AsFarAsPossible',\n                # agent hp\n                'AgentHp':np.random.randint(low=180,high=220),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_air(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = 2000 * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 1000\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  900,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.75,  'y': 0.75, 'z': 0.75, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 10,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 3,\n                # open fire range\n                \"PerceptionRange\":  2500,\n                \"GuardRange\":       1800,\n                \"FireRange\":        1700,\n                # debugging\n                'RSVD1': '-ring1=2500 -ring2=1800 -ring3=1700',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp':np.random.randint(low=40,high=60),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapAdversialConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n            { \"team\": 0,    \"type\": \"RLA_UAV_Support\",  \"init_fn_name\": \"init_air\"   },\n            { \"team\": 0,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 0,    \"type\": \"RLA_UAV_Support\",  \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,    \"type\": \"RLA_UAV_Support\",  \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR\",          \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,    \"type\": \"RLA_CAR_Laser\",    \"init_fn_name\": \"init_ground_tank\"},\n            { \"team\": 1,    \"type\": \"RLA_UAV_Support\",  \"init_fn_name\": \"init_air\"   }\n    ]\n\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'Multi-Digit'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapAttackPost.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapAttackPostConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\n\ndef init_position_helper(x_max, x_min, y_max, y_min, total, this):\n    n_col = np.ceil(np.sqrt(np.abs(x_max-x_min) * total / np.abs(y_max-y_min)))\n    n_row = np.ceil(total / n_col)\n\n    which_row = this // n_col\n    which_col = this % n_col\n\n    x = x_min + (which_col/n_col)*(x_max-x_min)\n    y = y_min + (which_row/n_row)*(y_max-y_min)\n    return x, y\n\n\nclass UhmapAttackPost(UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n    def reset(self):\n        \"\"\"\n            Reset function, it delivers reset command to unreal engine to spawn all agents\n            环境复位,每个episode的开始会执行一次此函数中会初始化所有智能体\n        \"\"\"\n        super().reset()\n        self.t = 0\n        pos_ro = np.random.rand()*2*np.pi\n        # spawn agents\n        AgentSettingArray = []\n\n        # count the number of agent in each team\n        n_team_agent = {}\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            if team not in n_team_agent: n_team_agent[team] = 0\n            SubTaskConfig.agent_list[i]['uid'] = i\n            SubTaskConfig.agent_list[i]['tid'] = n_team_agent[team]\n            n_team_agent[team] += 1\n\n        # push agent init info one by one\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            agent_info['n_team_agent'] = n_team_agent[team]\n            init_fn = getattr(self, agent_info['init_fn_name'])\n            AgentSettingArray.append(init_fn(agent_info, pos_ro))\n\n        self.agents  = [Agent(team=a['team'], team_id=a['tid'], uid=a['uid']) for a in SubTaskConfig.agent_list]\n        \n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'NumAgents' : len(SubTaskConfig.agent_list),\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        # assert resp['dataGlobal']['levelName'] == 'UhmapLargeScale'\n\n        assert len(resp['dataArr']) == len(AgentSettingArray)\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n        \"\"\"\n            step 函数,act中包含了所有agent的决策\n        \"\"\"\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if ScenarioConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif ScenarioConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n        elif ScenarioConfig.ActionFormat == 'ASCII':            \n            act_send = [digitsToStrAction(a) for a in act]\n        else:\n            raise \"ActionFormat is wrong!\"\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        \"\"\"\n            解析环境返回的一些关键事件,\n            如智能体阵亡,某队伍胜利等等。\n            关键事件需要在ue中进行定义.\n            该设计极大地简化了python端奖励的设计流程,\n            减小了python端的运算量。\n        \"\"\"\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            # if event_parsed['Event'] == 'Destroyed':\n            #     team = self.find_agent_by_uid(event_parsed['UID']).team\n            #     reward[team]    -= 0.05    # this team\n            #     reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                PredatorWin = False\n                PredatorRank = False\n                PredatorReward = 0\n                PreyWin = -1\n                PreyRank = -1\n                PreyReward = 0\n                EndReason = event_parsed['EndReason']\n                # According to MISSION\\uhmap\\SubTasks\\UhmapAttackPostConf.py, team 0 is prey team, team 1 is predator team\n                if EndReason == \"AllPreyCaught\" or EndReason == \"Team_0_AllDead\":\n                    PredatorWin = True; PredatorRank = 0; PredatorReward = 1\n                    PreyWin = False; PreyRank = 1; PreyReward = -1\n                elif EndReason == \"TimeMaxCntReached\" or EndReason == \"Team_1_AllDead\":\n                    PredatorWin = False; PredatorRank = 1; PredatorReward = -1\n                    PreyWin = True; PreyRank = 0; PreyReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [PreyRank, PredatorRank], \"end_reason\": EndReason}\n\n                reward = [PreyReward, PredatorReward]\n        # print(reward)\n        return reward, WinningResult\n\n    def step_skip(self):\n        \"\"\"\n            跳过一次决策,无用的函数\n        \"\"\"\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n    def find_agent_by_uid(self, uid):\n        \"\"\"\n            用uid查找智能体(带缓存加速机制)\n        \"\"\"\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        \"\"\"\n            粗解析智能体的观测,例如把死智能体的位置替换为inf(无穷远),\n            将智能体的agentLocation从字典形式转变为更简洁的(x,y,z)tuple形式\n        \"\"\"\n        assert resp['valid']\n        resp['dataGlobal']['distanceMat'] = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n        \n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n\n        info_dict = resp\n        for info in info_dict['dataArr']: \n            alive = info['agentAlive']\n\n            if alive:\n                agentLocation = info.pop('agentLocation')\n                agentRotation = info.pop('agentRotation')\n                agentVelocity = info.pop('agentVelocity')\n                agentScale = info.pop('agentScale')\n                info['agentLocationArr'] = (agentLocation['x'], agentLocation['y'], agentLocation['z'])\n                info['agentVelocityArr'] = (agentVelocity['x'], agentVelocity['y'], agentVelocity['z'])\n                info['agentRotationArr'] = (agentRotation['yaw'], agentRotation['pitch'], agentRotation['roll'])\n                info['agentScaleArr'] = (agentScale['x'], agentScale['y'], agentScale['z'])\n                info.pop('previousAction')\n                info.pop('availActions')\n                # info.pop('rSVD1')\n                info.pop('interaction')\n            else:\n                inf = float('inf')\n                info['agentLocationArr'] = (inf, inf, inf)\n                info['agentVelocityArr'] = (inf, inf, inf)\n                info['agentRotationArr'] = (inf, inf, inf)\n\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n    def init_defence(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        x = 0\n        y = tid * 1000\n        z = 0\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  0,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # 预留参数接口\n            'RSVD1':'-LaserDmg=70',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n\n    def init_attack(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        x,y = init_position_helper(x_max=3500.0, x_min=10000.0, y_max=-4470.0, y_min=4470.0, total=8, this=tid)   # 脚本-白给\n        # x,y = init_position_helper(x_max=3500.0, x_min=5000.0, y_max=-4470.0, y_min=4470.0, total=8, this=tid)  # 脚本-击杀\n        z = 500\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  1000,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapAttackPostConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n\n        \n        { 'team':1, 'type':'Lv3_DefenceTower',  'init_fn_name':'init_defence',  },\n\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapBreakingBad.py",
    "content": "import json, os, subprocess, time, copy, re, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at, distance_mat_between\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\n\nclass SubTaskConfig():\n    empty = \"\"\n\nclass UhmapBreakingBad(UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n                \n    def reset(self):\n        super().reset()\n        \n        self.t = 0\n\n        AgentPropertyDefaults.update({\n            'MaxMoveSpeed': 600,\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # also influence object mass, please change it with causion!\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            \"ExplodeDmg\": 20,           # ms explode dmg. test ok\n        })\n\n        # 500 is slightly above the ground,\n        # but agent will be spawn to ground automatically\n        ####################### spawn all ###########################\n        AgentSettingArray = []\n        agent_uid_cnt = 0\n        # \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        for i in range(ScenarioConfig.N_AGENT_EACH_TEAM[0]-1):  # For attacking, drones on the ground\n            x = 3254.0\n            y = 3891.0 + i *100\n            z = 500 \n            agent_property = copy.deepcopy(AgentPropertyDefaults)\n            agent_property.update({\n                    'ClassName': 'RLA_CAR',         # FString ClassName = \"\";\n                    'AgentTeam': 0,                 # int AgentTeam = 0;\n                    'IndexInTeam': i,               # int IndexInTeam = 0;\n                    'UID': agent_uid_cnt,           # int UID = 0;\n                    'MaxMoveSpeed': 600,\n                    \"ExplodeDmg\": 10,\n                    \"DodgeProb\": 0.1,\n                    'AgentHp': 100,\n                    \"WeaponCD\": 1,\n                    'Color':'(R=0,G=1,B=0,A=1)',\n                    'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                })\n            AgentSettingArray.append(agent_property); agent_uid_cnt += 1\n\n\n        x = 4000.0\n        y = 4000.0\n        z = 1000\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'ClassName': 'RLA_UAV_VIP',         # FString ClassName = \"\";\n                'AgentTeam': 0,                 # int AgentTeam = 0;\n                'IndexInTeam': agent_uid_cnt,   # under most situations IndexInTeam=agent_uid_cnt for team 0\n                'UID': agent_uid_cnt,           # int UID = 0;\n                'MaxMoveSpeed': 1000,\n                \"DodgeProb\": 0.5,\n                \"ExplodeDmg\": 10,\n                'AgentHp': 1,\n                \"WeaponCD\": 10000000000,\n                'Color':'(R=0,G=1,B=0,A=1)',\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n        })\n        AgentSettingArray.append(agent_property); agent_uid_cnt += 1\n\n        # \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        for i in range(ScenarioConfig.N_AGENT_EACH_TEAM[1]):\n            x = 0 + 500*(i+1)  *  (-1)**(i+1)\n            y = 0\n            z = 500\n            agent_property = copy.deepcopy(AgentPropertyDefaults)\n            agent_property.update({\n                    'ClassName': 'RLA_CAR_RED',\n                    'AgentTeam': 1,\n                    'IndexInTeam': i,\n                    'UID': agent_uid_cnt,\n                    'MaxMoveSpeed': 700,\n                    \"DodgeProb\": 0.1,\n                    'AgentHp':100,\n                    \"ExplodeDmg\": 10,\n                    \"WeaponCD\": 0.5,\n                    'Color':'(R=1,G=0,B=0,A=1)',\n                    'InitLocation': { 'x': x, 'y': y, 'z': z, },\n            })\n            AgentSettingArray.append(agent_property); agent_uid_cnt += 1\n\n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        assert resp['dataGlobal']['levelName'] == 'UhmapBreakingBad'\n        assert len(resp['dataArr']) == len(AgentSettingArray)\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if ScenarioConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif ScenarioConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        WIN_OR_LOSE_REWARD = 5\n        DRAW_REWARD = 2.5\n        KILL_REWARD = 0.1\n        BE_KILLED_REWARD = 0\n\n        reward = np.array([0.0]*self.n_teams,dtype=float)\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n\n        # reward according to distance to either of the landmarks\n        landmarks_pos3darr = np.array([[\n            lm['location']['x'], lm['location']['y'], lm['location']['z']\n            ] for lm in resp['dataGlobal']['keyObjArr']])\n        agent_pos3darr = np.array([agent.pos3d for agent in self.agents])\n        res = distance_mat_between(agent_pos3darr, landmarks_pos3darr)\n        penalty = -np.min(res, axis = -1) / 100000\n        reward += np.array([sum(penalty[ ScenarioConfig.AGENT_ID_EACH_TEAM[i] ]) for i in range(self.n_teams)])\n\n        # reward according to event (including win or lose event)\n        for event in events: \n            event_parsed = self.parse_event(event)\n            \n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= BE_KILLED_REWARD     # this team\n                reward[1-team]  += KILL_REWARD          # opp team\n\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    WinTeam = 1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += WIN_OR_LOSE_REWARD\n                    reward[1-WinTeam] -= WIN_OR_LOSE_REWARD\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-DRAW_REWARD for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    def step_skip(self):\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n\n    def find_agent_by_uid(self, uid):\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        assert resp['valid']\n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n            # print('pass')\n        info_dict = resp\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        pointer = 0\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 4\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]\n            )\n            obs_arr.append([\n                agent.index,\n                agent.team,\n                agent.alive,\n                agent.uid_remote,\n            ])\n            obs_arr.append(\n                agent.pos3d\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 5\n        self.N_Obj = len(self.key_obj)\n\n        OBJ_UID_OFFSET = 32768\n\n        obs_arr = RawObsArray(key = 'GameObj')\n\n        for i, obj in enumerate(self.key_obj):\n            assert obj['uId'] - OBJ_UID_OFFSET == i\n            obs_arr.append(\n                -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n            )\n            obs_arr.append([\n                obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                -1,                             #agent.team,\n                True,                           #agent.alive,\n                obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n            ]+\n            [\n                obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n            ]+\n            [\n                obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n            ]+\n            [\n                -1,                         # hp\n                obj['rotation']['yaw'],     # yaw \n                0,                          # max_speed\n            ])\n        OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n        OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n        OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n'''\n            obs_arr.append(\n                self.uid_binary[i]\n            )\n            obs_arr.append([\n                agent.index,\n                agent.team,\n                agent.alive,\n                agent.uid_remote,\n            ])\n            obs_arr.append(\n                agent.pos3d\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n\n\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapBreakingBadConf.py",
    "content": "\nclass SubTaskConfig():\n    empty = \"\"\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'Multi-Digit'"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapCarrier.py",
    "content": "import json, copy, re, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapCarrierConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\nfrom .SubtaskCommonFn import UhmapCommonFn\n\n\n\n\nclass UhmapCarrier(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.05    # this team\n                reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    agents_left_each_team = [0 for _ in range(self.n_teams)]\n                    for a in self.agents:\n                        if a.alive: agents_left_each_team[a.team] += 1\n                    WinTeam = np.argmax(agents_left_each_team)\n\n                    # <<1>> The alive agent number is EQUAL\n                    if agents_left_each_team[WinTeam] == agents_left_each_team[1-WinTeam]:\n                        hp_each_team = [0 for _ in range(self.n_teams)]\n                        for a in self.agents:\n                            if a.alive: hp_each_team[a.team] += a.hp\n                        WinTeam = np.argmax(hp_each_team)\n\n                        # <<2>> The alive agent HP sum is EQUAL\n                        if hp_each_team[WinTeam] == hp_each_team[1-WinTeam]:\n                            WinTeam = -1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += 1\n                    reward[1-WinTeam] -= 1\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-1 for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 1\n        self.N_Obj = len(self.key_obj)\n        if self.N_Obj > 0:\n            OBJ_UID_OFFSET = 32768\n\n            obs_arr = RawObsArray(key = 'GameObj')\n\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])\n            OBS_GameObj = OBS_GameObj[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n    def init_drone(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  400,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.7,  'y': 0.7, 'z': 0.7, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 75,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000,\n                \"GuardRange\":       1400,\n                \"FireRange\":        1300 ,\n                # debugging\n                'RSVD1': f'-CarrierName=T0-0 -NumDrone={self.n_team_agent[team]-1}' if team==0 else f'-CarrierName=T1-0 -NumDrone={self.n_team_agent[team]-1}',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp': 110,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_carrier(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = 2000 * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 1000\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  900,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1.0,  'y': 1.0, 'z': 1.0, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 100,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 3,\n                # open fire range\n                \"PerceptionRange\":  5000,\n                \"GuardRange\":       4800,\n                \"FireRange\":        4800,\n                # debugging\n                'RSVD1': '',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp': 500,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapCarrierConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n            { \"team\": 0,    \"type\": \"Carrier\",          \"init_fn_name\": \"init_carrier\"     },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 0,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n\n\n            { \"team\": 1,    \"type\": \"Carrier\",          \"init_fn_name\": \"init_carrier\"     },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n            { \"team\": 1,    \"type\": \"SmallDrone\",       \"init_fn_name\": \"init_drone\"   },\n\n    ]\n\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapEscape.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapEscapeConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\n\ndef init_position_helper(x_max, x_min, y_max, y_min, total, this):\n    n_col = np.ceil(np.sqrt(np.abs(x_max-x_min) * total / np.abs(y_max-y_min)))\n    n_row = np.ceil(total / n_col)\n\n    which_row = this // n_col\n    which_col = this % n_col\n\n    x = x_min + (which_col/n_col)*(x_max-x_min)\n    y = y_min + (which_row/n_row)*(y_max-y_min)\n    return x, y\n\n\nclass UhmapEscape(UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n    def reset(self):\n        \"\"\"\n            Reset function, it delivers reset command to unreal engine to spawn all agents\n            环境复位,每个episode的开始会执行一次此函数中会初始化所有智能体\n        \"\"\"\n        super().reset()\n        self.t = 0\n        pos_ro = np.random.rand()*2*np.pi\n        # spawn agents\n        AgentSettingArray = []\n\n        # count the number of agent in each team\n        n_team_agent = {}\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            if team not in n_team_agent: n_team_agent[team] = 0\n            SubTaskConfig.agent_list[i]['uid'] = i\n            SubTaskConfig.agent_list[i]['tid'] = n_team_agent[team]\n            n_team_agent[team] += 1\n\n        # push agent init info one by one\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            agent_info['n_team_agent'] = n_team_agent[team]\n            init_fn = getattr(self, agent_info['init_fn_name'])\n            AgentSettingArray.append(init_fn(agent_info, pos_ro))\n\n        self.agents  = [Agent(team=a['team'], team_id=a['tid'], uid=a['uid']) for a in SubTaskConfig.agent_list]\n        \n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'NumAgents' : len(SubTaskConfig.agent_list),\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        # assert resp['dataGlobal']['levelName'] == 'UhmapLargeScale'\n\n        assert len(resp['dataArr']) == len(AgentSettingArray)\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n        \"\"\"\n            step 函数,act中包含了所有agent的决策\n        \"\"\"\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if ScenarioConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif ScenarioConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n        elif ScenarioConfig.ActionFormat == 'ASCII':            \n            act_send = [digitsToStrAction(a) for a in act]\n        else:\n            raise \"ActionFormat is wrong!\"\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        \"\"\"\n            解析环境返回的一些关键事件,\n            如智能体阵亡,某队伍胜利等等。\n            关键事件需要在ue中进行定义.\n            该设计极大地简化了python端奖励的设计流程,\n            减小了python端的运算量。\n        \"\"\"\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.10    # this team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                DefenderWin = False\n                DefenderRank = False\n                DefenderReward = 0\n                AttackerWin = -1\n                AttackerRank = -1\n                AttackerReward = 0\n                EndReason = event_parsed['EndReason']\n                # print(EndReason)\n                # According to MISSION\\uhmap\\SubTasks\\UhmapAttackPostConf.py, team 0 is Attacker team, team 1 is Defender team\n                if EndReason == \"Team_0_AllDead\":\n                    DefenderWin = True; DefenderRank = 0; DefenderReward = 1\n                    AttackerWin = False; AttackerRank = 1; AttackerReward = -1\n                elif EndReason == \"TimeMaxCntReached\":\n                    DefenderWin = True; DefenderRank = 0; DefenderReward = 1\n                    AttackerWin = False; AttackerRank = 1; AttackerReward = -1\n                elif EndReason == \"Team_1_AllDead\":\n                    DefenderWin = False; DefenderRank = 1; DefenderReward = -1\n                    AttackerWin = True; AttackerRank = 0; AttackerReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [AttackerRank, DefenderRank], \"end_reason\": EndReason}\n\n                reward = [AttackerReward, DefenderReward]\n        # print(reward)\n        return reward, WinningResult\n\n    def step_skip(self):\n        \"\"\"\n            跳过一次决策,无用的函数\n        \"\"\"\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n    def find_agent_by_uid(self, uid):\n        \"\"\"\n            用uid查找智能体(带缓存加速机制)\n        \"\"\"\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        \"\"\"\n            粗解析智能体的观测,例如把死智能体的位置替换为inf(无穷远),\n            将智能体的agentLocation从字典形式转变为更简洁的(x,y,z)tuple形式\n        \"\"\"\n        assert resp['valid']\n        resp['dataGlobal']['distanceMat'] = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n        \n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n\n        info_dict = resp\n        for info in info_dict['dataArr']: \n            alive = info['agentAlive']\n\n            if alive:\n                agentLocation = info.pop('agentLocation')\n                agentRotation = info.pop('agentRotation')\n                agentVelocity = info.pop('agentVelocity')\n                agentScale = info.pop('agentScale')\n                info['agentLocationArr'] = (agentLocation['x'], agentLocation['y'], agentLocation['z'])\n                info['agentVelocityArr'] = (agentVelocity['x'], agentVelocity['y'], agentVelocity['z'])\n                info['agentRotationArr'] = (agentRotation['yaw'], agentRotation['pitch'], agentRotation['roll'])\n                info['agentScaleArr'] = (agentScale['x'], agentScale['y'], agentScale['z'])\n                info.pop('previousAction')\n                info.pop('availActions')\n                # info.pop('rSVD1')\n                info.pop('interaction')\n            else:\n                inf = float('inf')\n                info['agentLocationArr'] = (inf, inf, inf)\n                info['agentVelocityArr'] = (inf, inf, inf)\n                info['agentRotationArr'] = (inf, inf, inf)\n\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        dis_mat[~alive_all,:] = +np.inf\n        dis_mat[:,~alive_all] = +np.inf\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n    def init_defence(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        n_team_agent = agent_info['n_team_agent']\n        assert n_team_agent == 4\n        if tid == 0:\n            x,y,z = 13670.704102, 2762.254395, 338.760925\n        if tid == 1:\n            x,y,z = 13670.704102, -2483.998047, 338.760925\n        if tid == 2:\n            x,y,z = -3478.34668, 2516.466553, 338.752136\n        if tid == 3:\n            x,y,z = -3478.34668, -2729.785889, 338.752136\n\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  360,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # 预留参数接口\n            'RSVD1':'-LaserDmg=50',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n\n    def init_attack(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        n_team_agent = agent_info['n_team_agent']\n        x,y = init_position_helper(x_max=6514.513672, x_min=2216.777832, y_max=2531.608398, y_min=-3842.151123, total=n_team_agent, this=tid)\n        z = 500\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  1000,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapEscapeConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n        { 'team':0, 'type':'PosAttacker',       'init_fn_name':'init_attack',  },\n\n        \n        { 'team':1, 'type':'Lv3_DefenceTank',  'init_fn_name':'init_defence',  },\n        { 'team':1, 'type':'Lv3_DefenceTank',  'init_fn_name':'init_defence',  },\n        { 'team':1, 'type':'Lv3_DefenceTank',  'init_fn_name':'init_defence',  },\n        { 'team':1, 'type':'Lv3_DefenceTank',  'init_fn_name':'init_defence',  },\n\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapFormation.py",
    "content": "import json, copy, re, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapFormationConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\nfrom .SubtaskCommonFn import UhmapCommonFn\n\n\n\n\nclass UhmapFormation(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.05    # this team\n                reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    agents_left_each_team = [0 for _ in range(self.n_teams)]\n                    for a in self.agents:\n                        if a.alive: agents_left_each_team[a.team] += 1\n                    WinTeam = np.argmax(agents_left_each_team)\n\n                    # <<1>> The alive agent number is EQUAL\n                    if agents_left_each_team[WinTeam] == agents_left_each_team[1-WinTeam]:\n                        hp_each_team = [0 for _ in range(self.n_teams)]\n                        for a in self.agents:\n                            if a.alive: hp_each_team[a.team] += a.hp\n                        WinTeam = np.argmax(hp_each_team)\n\n                        # <<2>> The alive agent HP sum is EQUAL\n                        if hp_each_team[WinTeam] == hp_each_team[1-WinTeam]:\n                            WinTeam = -1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += 1\n                    reward[1-WinTeam] -= 1\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-1 for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 5\n        self.N_Obj = len(self.key_obj)\n        if self.N_Obj > 0:\n            OBJ_UID_OFFSET = 32768\n\n            obs_arr = RawObsArray(key = 'GameObj')\n\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])\n            OBS_GameObj = OBS_GameObj[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n    def init_drone(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  400,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.7,  'y': 0.7, 'z': 0.7, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 75,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000,\n                \"GuardRange\":       1400,\n                \"FireRange\":        1300 ,\n                # debugging\n                'RSVD1': f'-CarrierName=T0-0 -NumDrone={self.n_team_agent[team]-1}' if team==0 else f'-CarrierName=T1-0 -NumDrone={self.n_team_agent[team]-1}',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp': 110,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapFormationConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 0,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n\n\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n        { \"team\": 1,   \"type\": \"Lv3_MomentumAgentWithHp\",   \"init_fn_name\": \"init_drone\"   },\n\n    ]\n\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapHuge.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapHugeConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\nfrom .SubtaskCommonFn import UhmapCommonFn\n\n\n\n\nclass UhmapHuge(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.05    # this team\n                reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    agents_left_each_team = [0 for _ in range(self.n_teams)]\n                    for a in self.agents:\n                        if a.alive: agents_left_each_team[a.team] += 1\n                    WinTeam = np.argmax(agents_left_each_team)\n\n                    # <<1>> The alive agent number is EQUAL\n                    if agents_left_each_team[WinTeam] == agents_left_each_team[1-WinTeam]:\n                        hp_each_team = [0 for _ in range(self.n_teams)]\n                        for a in self.agents:\n                            if a.alive: hp_each_team[a.team] += a.hp\n                        WinTeam = np.argmax(hp_each_team)\n\n                        # <<2>> The alive agent HP sum is EQUAL\n                        if hp_each_team[WinTeam] == hp_each_team[1-WinTeam]:\n                            WinTeam = -1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += 1\n                    reward[1-WinTeam] -= 1\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-1 for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 1\n        self.N_Obj = len(self.key_obj)\n\n        OBJ_UID_OFFSET = 32768\n\n        obs_arr = RawObsArray(key = 'GameObj')\n\n        for i, obj in enumerate(self.key_obj):\n            assert obj['uId'] - OBJ_UID_OFFSET == i\n            obs_arr.append(\n                -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n            )\n            obs_arr.append([\n                obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                -1,                             #agent.team,\n                True,                           #agent.alive,\n                obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n            ])\n            # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n            obs_arr.append(\n                [\n                    obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                ]\n                # tear_num_arr([\n                #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                # ], 6, ScenarioConfig.ObsBreakBase, 0)\n            )\n            \n            obs_arr.append([\n                obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n            ]+\n            [\n                -1,                         # hp\n                obj['rotation']['yaw'],     # yaw \n                0,                          # max_speed\n            ])\n        OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n        OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n        OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_ground(self, agent_info, pos_ro):\n        N_COL = 4\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 50\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  720          if agent_class == 'RLA_CAR_Laser' else 600,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.5,  'y': 0.5, 'z': 0.5, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 20,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000       if agent_class == 'RLA_CAR_Laser' else 2500,\n                \"GuardRange\":       1400       if agent_class == 'RLA_CAR_Laser' else 1700,\n                \"FireRange\":        750        if agent_class == 'RLA_CAR_Laser' else 1400,\n                # debugging\n                'RSVD1': '-Ring1=2000 -Ring2=1400 -Ring3=750' if agent_class == 'RLA_CAR_Laser' else '-Ring1=2500 -Ring2=1700 -Ring3=1400',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;AsFarAsPossible',\n                # agent hp\n                'AgentHp':np.random.randint(low=95,high=105) if agent_class == 'RLA_CAR_Laser' else np.random.randint(low=145,high=155),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_air(self, agent_info, pos_ro):\n        N_COL = 4\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 50\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = 2000 * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 1000\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  900,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 0.5,  'y': 0.5, 'z': 0.5, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 10,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 3,\n                # open fire range\n                \"PerceptionRange\":  2500,\n                \"GuardRange\":       1800,\n                \"FireRange\":        1700,\n                # debugging\n                'RSVD1': '-ring1=2500 -ring2=1800 -ring3=1700',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp':50,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapHugeConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0,  'tid':0,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':1,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':2,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':3,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':4,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':5,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':6,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':7,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':8,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':9,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        \n        { 'team':1,  'tid':0,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':1,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':2,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':3,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':4,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':5,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':6,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':7,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':8,  'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':9,  'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n    ]\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'Multi-Digit'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapIntercept.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapInterceptConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\n\nclass UhmapIntercept(UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n    def reset(self):\n        \"\"\"\n            Reset function, it delivers reset command to unreal engine to spawn all agents\n            环境复位,每个episode的开始会执行一次此函数中会初始化所有智能体\n        \"\"\"\n        super().reset()\n        self.t = 0\n        pos_ro = np.random.rand()*2*np.pi\n        # spawn agents\n        AgentSettingArray = []\n\n        # count the number of agent in each team\n        n_team_agent = {}\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            if team not in n_team_agent: n_team_agent[team] = 0\n            SubTaskConfig.agent_list[i]['uid'] = i\n            SubTaskConfig.agent_list[i]['tid'] = n_team_agent[team]\n            n_team_agent[team] += 1\n\n        # push agent init info one by one\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            agent_info['n_team_agent'] = n_team_agent[team]\n            init_fn = getattr(self, agent_info['init_fn_name'])\n            AgentSettingArray.append(init_fn(agent_info, pos_ro))\n\n        self.agents  = [Agent(team=a['team'], team_id=a['tid'], uid=a['uid']) for a in SubTaskConfig.agent_list]\n        \n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'NumAgents' : len(SubTaskConfig.agent_list),\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        # assert resp['dataGlobal']['levelName'] == 'UhmapLargeScale'\n\n        assert len(resp['dataArr']) == len(AgentSettingArray)\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n        \"\"\"\n            step 函数,act中包含了所有agent的决策\n        \"\"\"\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if ScenarioConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif ScenarioConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n        elif ScenarioConfig.ActionFormat == 'ASCII':            \n            act_send = [digitsToStrAction(a) for a in act]\n        else:\n            raise \"ActionFormat is wrong!\"\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        \"\"\"\n            解析环境返回的一些关键事件,\n            如智能体阵亡,某队伍胜利等等。\n            关键事件需要在ue中进行定义.\n            该设计极大地简化了python端奖励的设计流程,\n            减小了python端的运算量。\n        \"\"\"\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            # if event_parsed['Event'] == 'Destroyed':\n            #     team = self.find_agent_by_uid(event_parsed['UID']).team\n            #     reward[team]    -= 0.05    # this team\n            #     reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                PredatorWin = False\n                PredatorRank = False\n                PredatorReward = 0\n                PreyWin = -1\n                PreyRank = -1\n                PreyReward = 0\n                EndReason = event_parsed['EndReason']\n                # According to MISSION\\uhmap\\SubTasks\\UhmapInterceptConf.py, team 0 is prey team, team 1 is predator team\n                if EndReason == \"AllPreyCaught\" or EndReason == \"Team_0_AllDead\":\n                    PredatorWin = True; PredatorRank = 0; PredatorReward = 1\n                    PreyWin = False; PreyRank = 1; PreyReward = -1\n                elif EndReason == \"TimeMaxCntReached\" or EndReason == \"Team_1_AllDead\":\n                    PredatorWin = False; PredatorRank = 1; PredatorReward = -1\n                    PreyWin = True; PreyRank = 0; PreyReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [PreyRank, PredatorRank], \"end_reason\": EndReason}\n\n                reward = [PreyReward, PredatorReward]\n        # print(reward)\n        return reward, WinningResult\n\n    def step_skip(self):\n        \"\"\"\n            跳过一次决策,无用的函数\n        \"\"\"\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n    def find_agent_by_uid(self, uid):\n        \"\"\"\n            用uid查找智能体(带缓存加速机制)\n        \"\"\"\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        \"\"\"\n            粗解析智能体的观测,例如把死智能体的位置替换为inf(无穷远),\n            将智能体的agentLocation从字典形式转变为更简洁的(x,y,z)tuple形式\n        \"\"\"\n        assert resp['valid']\n        resp['dataGlobal']['distanceMat'] = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n        \n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n\n        info_dict = resp\n        for info in info_dict['dataArr']: \n            alive = info['agentAlive']\n\n            if alive:\n                agentLocation = info.pop('agentLocation')\n                agentRotation = info.pop('agentRotation')\n                agentVelocity = info.pop('agentVelocity')\n                agentScale = info.pop('agentScale')\n                info['agentLocationArr'] = (agentLocation['x'], agentLocation['y'], agentLocation['z'])\n                info['agentVelocityArr'] = (agentVelocity['x'], agentVelocity['y'], agentVelocity['z'])\n                info['agentRotationArr'] = (agentRotation['yaw'], agentRotation['pitch'], agentRotation['roll'])\n                info['agentScaleArr'] = (agentScale['x'], agentScale['y'], agentScale['z'])\n                info.pop('previousAction')\n                info.pop('availActions')\n                # info.pop('rSVD1')\n                info.pop('interaction')\n            else:\n                inf = float('inf')\n                info['agentLocationArr'] = (inf, inf, inf)\n                info['agentVelocityArr'] = (inf, inf, inf)\n                info['agentRotationArr'] = (inf, inf, inf)\n\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_landmark(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        x = 0\n        y = tid * 1000\n        z = 500\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  0,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n    def init_defender(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        x = 100\n        y = tid * 1000\n        z = 500\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  500,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n\n    def init_attacker(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        x = 4000\n        y = np.random.rand() * 1000\n        z = 500\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  1000,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapInterceptConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'Landmark',   'init_fn_name':'init_landmark',  },\n        { 'team':0, 'type':'Landmark',   'init_fn_name':'init_landmark',  },\n        { 'team':0, 'type':'Landmark',   'init_fn_name':'init_landmark',  },\n        { 'team':0, 'type':'Defender',   'init_fn_name':'init_defender',  },\n        { 'team':0, 'type':'Defender',   'init_fn_name':'init_defender',  },\n\n        \n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n        { 'team':1, 'type':'Attacker',  'init_fn_name':'init_attacker',  },\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapJustAnIsland.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapJustAnIslandConf import SubTaskConfig\nfrom .SubtaskCommonFn import UhmapCommonFn\nfrom .cython_func import tear_num_arr\n\n\n\nclass UhmapJustAnIsland(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                # WinTeam = int(event_parsed['WinTeam'])\n                WinningResult = {\n                    # 每个队伍的排名,可以指定例如[1, 0, 2],代表一队第2名,二队第1名,三队第3名\n                    # 如果没有任何队伍取得胜利,可以指定例如[-1, -1, -1]\n                    # 如果有两只队伍成绩并列,可以指定例如[0, 2, 0, 2], 代表一队三队并列第1名,二队四队并列第3名\n                    \"team_ranking\": [-1, ],   \n                    \"end_reason\": EndReason\n                }\n                assert len(WinningResult[\"team_ranking\"]) == ScenarioConfig.N_TEAM\n        # print(reward)\n        return reward, WinningResult\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_ground(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  720          if agent_class == 'RLA_CAR_Laser' else 600,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 20,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000       if agent_class == 'RLA_CAR_Laser' else 2500,\n                \"GuardRange\":       1400       if agent_class == 'RLA_CAR_Laser' else 1700,\n                \"FireRange\":        750        if agent_class == 'RLA_CAR_Laser' else 1400,\n                # debugging\n                'RSVD1': '-Ring1=2000 -Ring2=1400 -Ring3=750' if agent_class == 'RLA_CAR_Laser' else '-Ring1=2500 -Ring2=1700 -Ring3=1400',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;AsFarAsPossible',\n                # agent hp\n                'AgentHp':np.random.randint(low=95,high=105) if agent_class == 'RLA_CAR_Laser' else np.random.randint(low=145,high=155),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_air(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = agent_info['n_team_agent']\n\n\n\n        # 此处初始化代码仅做demo使用\n        if ScenarioConfig.DemoType == \"AirShow\":\n            # tid = agent_info['tid']\n            N_ROW = 10\n            uid = agent_info['uid']\n            tid = agent_info['uid'] if team == 0 else None\n            assert tid is not None, 'Lost tid!'\n\n            if tid < n_team_agent / 2:\n                y = -10000 + (tid%N_ROW) * 5000\n                x = -58000 - (tid//N_ROW) * 5000\n                z = 3000\n            else: \n                y = 30000 + (tid - n_team_agent/2)%N_ROW * 5000   \n                x = -58000 - ((tid-n_team_agent/2)//N_ROW) * 5000\n                z = 6000\n            \n            yaw = 90 if team==0 else -90\n\n        elif ScenarioConfig.DemoType == \"AirAttack\":\n            # tid = agent_info['tid']\n            N_ROW = 10\n            uid = agent_info['uid']\n            tid = agent_info['uid'] if team == 0 else None\n            assert tid is not None, 'Lost tid!'\n\n            '''\n            福建省东北角大致方位  (-70000,-200000)\n            福建省西南角大致方位  (-225000,-50000)\n            '''\n\n            if tid < n_team_agent / 2:\n                y = -200000 + (tid%N_ROW) * 2000\n                x = -70000 - (tid//N_ROW) * 2000\n                z = 3000\n            else: \n                y = -50000 + (tid - n_team_agent/2)%N_ROW * 2000   \n                x = -225000 - ((tid-n_team_agent/2)//N_ROW) * 2000\n                z = 3000\n            \n            yaw = 90 if team==0 else -90\n\n        else:\n            pass\n            print(\"未指定位置初始化！\")\n\n\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                # 'DebugAgent': False,\n                # # max drive/fly speed\n                # 'MaxMoveSpeed':  900,\n                # # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # # probability of escaping dmg 闪避\n                # \"DodgeProb\": 0.0,\n                # # ms explode dmg\n                # \"ExplodeDmg\": 10,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # # Weapon CD\n                # 'WeaponCD': 3,\n                # # open fire range\n                # \"PerceptionRange\":  2500,\n                # \"GuardRange\":       1800,\n                # \"FireRange\":        1700,\n                # # debugging\n                'RSVD1': '-maxAcc=10 -numMissile=5',\n                # # regular\n                # 'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # # agent hp\n                # 'AgentHp':50,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # # show color\n                # 'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n\n    def init_target(self, agent_info, pos_ro):\n            agent_class = agent_info['type']\n            team = agent_info['team']\n            n_team_agent = 5\n\n            # tid = agent_info['tid']\n            tid = agent_info['uid'] if team == 0 else None\n            uid = agent_info['uid']\n            assert tid is not None, 'Lost ID!'\n\n            # initial positions (within 5 regions)\n            if tid == 100:\n                x = np.random.uniform(140000, 200000)\n                y = np.random.uniform(-162460, -102460)\n                z = 2550\n\n            if tid == 101:\n                x = np.random.uniform(52120, 100120)\n                y = np.random.uniform(-100970, -36970)\n                z = 2550\n\n            if tid == 102:\n                x = np.random.uniform(52050, 95050)\n                y = np.random.uniform(-32220, 47780)\n                z = 2550\n\n            if tid == 103:\n                x = np.random.uniform(36870, 96870)\n                y = np.random.uniform(49000, 112560)\n                z = 2550\n\n            if tid == 104:\n                x = np.random.uniform(22802, 89820)\n                y = np.random.uniform(117310, 157310)\n                z = 2550\n            \n            # # initial air-defense\n            # if tid == 105:\n            #     x = np.random.uniform(140000, 200000)\n            #     y = np.random.uniform(-162460, -102460)\n            #     z = 2550\n\n            # if tid == 106:\n            #     x = np.random.uniform(52120, 100120)\n            #     y = np.random.uniform(-100970, -36970)\n            #     z = 2550\n\n            # if tid == 107:\n            #     x = np.random.uniform(52050, 95050)\n            #     y = np.random.uniform(-32220, 47780)\n            #     z = 2550\n\n            # if tid == 108:\n            #     x = np.random.uniform(36870, 96870)\n            #     y = np.random.uniform(49000, 112560)\n            #     z = 2550\n\n            # if tid == 109:\n            #     x = np.random.uniform(22802, 89820)\n            #     y = np.random.uniform(117310, 157310)\n            #     z = 2550\n\n\n\n            agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n            agent_property.update({\n                    # 'DebugAgent': False,\n                    # # max drive/fly speed\n                    # 'MaxMoveSpeed':  900,\n                    # # also influence object mass, please change it with causion!\n                    'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                    # # probability of escaping dmg 闪避\n                    # \"DodgeProb\": 0.0,\n                    # # ms explode dmg\n                    # \"ExplodeDmg\": 10,           \n                    # team belonging\n                    'AgentTeam': team,\n                    # choose ue class to init\n                    'ClassName': agent_class,\n                    # # Weapon CD\n                    # 'WeaponCD': 3,\n                    # # open fire range\n                    # \"PerceptionRange\":  2500,\n                    # \"GuardRange\":       1800,\n                    # \"FireRange\":        1700,\n                    # # debugging\n                    'RSVD1': '-ring1=2500 -ring2=1800 -ring3=1700 -value=10',\n                    # # regular\n                    # 'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                    # # agent hp\n                    # 'AgentHp':50,\n                    # the rank of agent inside the team\n                    'IndexInTeam': tid, \n                    # the unique identity of this agent in simulation system\n                    'UID': uid, \n                    # # show color\n                    # 'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                    # initial location\n                    'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                    # initial facing direction et.al.\n                    # 'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n            }),\n            return agent_property"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapJustAnIslandConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0,  'tid':0,  'uid':0,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':1,  'uid':1,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':2,  'uid':2,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':3,  'uid':3,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':4,  'uid':4,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':5,  'uid':5,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':6,  'uid':6,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':7,  'uid':7,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':8,  'uid':8,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n        { 'team':0,  'tid':9,  'uid':9,  'n_team_agent':10, 'type':'PlaneAgent',  'init_fn_name':'init_air',  },\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapLargeScale.py",
    "content": "import json, copy, re, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actset_lookup import digit2act_dictionary, AgentPropertyDefaults\nfrom ..actset_lookup import decode_action_as_string, decode_action_as_string\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapLargeScaleConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\nfrom .SubtaskCommonFn import UhmapCommonFn\n\n\n\n\nclass UhmapLargeScale(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n\n    def extract_key_gameobj(self, resp):\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n\n    def gen_reward_and_win(self, resp):\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'Destroyed':\n                team = self.find_agent_by_uid(event_parsed['UID']).team\n                reward[team]    -= 0.05    # this team\n                reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                EndReason = event_parsed['EndReason']\n                WinTeam = int(event_parsed['WinTeam'])\n                if WinTeam<0: # end due to timeout\n                    agents_left_each_team = [0 for _ in range(self.n_teams)]\n                    for a in self.agents:\n                        if a.alive: agents_left_each_team[a.team] += 1\n                    WinTeam = np.argmax(agents_left_each_team)\n\n                    # <<1>> The alive agent number is EQUAL\n                    if agents_left_each_team[WinTeam] == agents_left_each_team[1-WinTeam]:\n                        hp_each_team = [0 for _ in range(self.n_teams)]\n                        for a in self.agents:\n                            if a.alive: hp_each_team[a.team] += a.hp\n                        WinTeam = np.argmax(hp_each_team)\n\n                        # <<2>> The alive agent HP sum is EQUAL\n                        if hp_each_team[WinTeam] == hp_each_team[1-WinTeam]:\n                            WinTeam = -1\n\n                if WinTeam >= 0:\n                    WinningResult = {\n                        \"team_ranking\": [0,1] if WinTeam==0 else [1,0],\n                        \"end_reason\": EndReason\n                    }\n                    reward[WinTeam] += 1\n                    reward[1-WinTeam] -= 1\n                else:\n                    WinningResult = {\n                        \"team_ranking\": [-1, -1],\n                        \"end_reason\": EndReason\n                    }\n                    reward = [-1 for _ in range(self.n_teams)]\n        # print(reward)\n        return reward, WinningResult\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 1500\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(\n            self.n_agents, \n            MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, \n            CORE_DIM\n            ))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 1\n        self.N_Obj = len(self.key_obj)\n        if self.N_Obj > 0:\n            OBJ_UID_OFFSET = 32768\n\n            obs_arr = RawObsArray(key = 'GameObj')\n\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])\n            OBS_GameObj = OBS_GameObj[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n    def init_ground(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  720          if agent_class == 'RLA_CAR_Laser' else 600,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 20,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 1,\n                # open fire range\n                \"PerceptionRange\":  2000       if agent_class == 'RLA_CAR_Laser' else 2500,\n                \"GuardRange\":       1400       if agent_class == 'RLA_CAR_Laser' else 1700,\n                \"FireRange\":        750        if agent_class == 'RLA_CAR_Laser' else 1400,\n                # debugging\n                'RSVD1': '-Ring1=2000 -Ring2=1400 -Ring3=750' if agent_class == 'RLA_CAR_Laser' else '-Ring1=2500 -Ring2=1700 -Ring3=1400',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;AsFarAsPossible',\n                # agent hp\n                'AgentHp':np.random.randint(low=95,high=105) if agent_class == 'RLA_CAR_Laser' else np.random.randint(low=145,high=155),\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n    def init_air(self, agent_info, pos_ro):\n        N_COL = 2\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 10\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = 2000 * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 1000\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  900,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # probability of escaping dmg 闪避\n                \"DodgeProb\": 0.0,\n                # ms explode dmg\n                \"ExplodeDmg\": 10,           \n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # Weapon CD\n                'WeaponCD': 3,\n                # open fire range\n                \"PerceptionRange\":  2500,\n                \"GuardRange\":       1800,\n                \"FireRange\":        1700,\n                # debugging\n                'RSVD1': '-ring1=2500 -ring2=1800 -ring3=1700',\n                # regular\n                'RSVD2': '-InitAct=ActionSet2::Idle;StaticAlert',\n                # agent hp\n                'AgentHp':50,\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapLargeScaleConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0,  'tid':0,  'uid':0,  'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':1,  'uid':1,  'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':2,  'uid':2,  'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':3,  'uid':3,  'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':4,  'uid':4,  'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':5,  'uid':5,  'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':6,  'uid':6,  'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':7,  'uid':7,  'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':8,  'uid':8,  'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':0,  'tid':9,  'uid':9,  'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        \n        { 'team':1,  'tid':0,  'uid':10, 'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':1,  'uid':11, 'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':2,  'uid':12, 'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':3,  'uid':13, 'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':4,  'uid':14, 'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':5,  'uid':15, 'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':6,  'uid':16, 'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':7,  'uid':17, 'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':8,  'uid':18, 'n_team_agent':10,   'type':'RLA_CAR_Laser',  'init_fn_name':'init_ground',  },\n        { 'team':1,  'tid':9,  'uid':19, 'n_team_agent':10,   'type':'RLA_CAR',        'init_fn_name':'init_ground',  },\n    ]\n\n    obs_vec_length = 23\n    obs_n_entity = 11\n    ActionFormat = 'Multi-Digit'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapPreyPredator.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapPreyPredatorConf import SubTaskConfig\nfrom .SubtaskCommonFn import UhmapCommonFn\nfrom .cython_func import tear_num_arr\n\n\n\n\nclass UhmapPreyPredator(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n\n    def init_ground(self, agent_info, pos_ro):\n        N_COL = 4\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        n_team_agent = 50\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        x = 0 + 800*(tid - n_team_agent//2) //N_COL\n        y = (400* (tid%N_COL) + 2000) * (-1)**(team+1)\n        x,y = np.matmul(np.array([x,y]), np.array([[np.cos(pos_ro), -np.sin(pos_ro)], [np.sin(pos_ro), np.cos(pos_ro)] ]))\n        z = 500 # 500 is slightly above the ground\n        yaw = 90 if team==0 else -90\n        assert np.abs(x) < 15000.0 and np.abs(y) < 15000.0\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  720          if agent_class == 'RLA_CAR_Laser' else 600,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # debugging\n                'RSVD1': '-Ring1=2000 -Ring2=1400 -Ring3=750',\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': yaw, },\n        }),\n        return agent_property\n\n\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            # if event_parsed['Event'] == 'Destroyed':\n            #     team = self.find_agent_by_uid(event_parsed['UID']).team\n            #     reward[team]    -= 0.05    # this team\n            #     reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                PredatorWin = False\n                PredatorRank = False\n                PredatorReward = 0\n                PreyWin = -1\n                PreyRank = -1\n                PreyReward = 0\n                EndReason = event_parsed['EndReason']\n                # According to MISSION\\uhmap\\SubTasks\\UhmapPreyPredatorConf.py, team 0 is prey team, team 1 is predator team\n                if EndReason == \"AllPreyCaught\" or EndReason == \"Team_0_AllDead\":\n                    PredatorWin = True; PredatorRank = 0; PredatorReward = 1\n                    PreyWin = False; PreyRank = 1; PreyReward = -1\n                elif EndReason == \"TimeMaxCntReached\" or EndReason == \"Team_1_AllDead\":\n                    PredatorWin = False; PredatorRank = 1; PredatorReward = -1\n                    PreyWin = True; PreyRank = 0; PreyReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [PreyRank, PredatorRank], \"end_reason\": EndReason}\n\n                reward = [PreyReward, PredatorReward]\n        # print(reward)\n        return reward, WinningResult\n\n\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapPreyPredatorConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        { 'team':0, 'type':'Prey',   'init_fn_name':'init_ground',  },\n        \n        { 'team':1, 'type':'Predator',  'init_fn_name':'init_ground',  },\n        { 'team':1, 'type':'Predator',  'init_fn_name':'init_ground',  },\n        { 'team':1, 'type':'Predator',  'init_fn_name':'init_ground',  },\n\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapReproduce.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapReproduceConf import SubTaskConfig\nfrom .cython_func import tear_num_arr\n\ndef init_position_helper(x_max, x_min, y_max, y_min, total, this):\n    n_col = np.ceil(np.sqrt(np.abs(x_max-x_min) * total / np.abs(y_max-y_min)))\n    n_row = np.ceil(total / n_col)\n\n    which_row = this // n_col\n    which_col = this % n_col\n\n    x = x_min + (which_col/n_col)*(x_max-x_min)\n    y = y_min + (which_row/n_row)*(y_max-y_min)\n    return x, y\n\n\nclass UhmapReproduce(UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n    def reset(self):\n        \"\"\"\n            Reset function, it delivers reset command to unreal engine to spawn all agents\n            环境复位,每个episode的开始会执行一次此函数中会初始化所有智能体\n        \"\"\"\n        super().reset()\n        self.t = 0\n        pos_ro = np.random.rand()*2*np.pi\n        # spawn agents\n        AgentSettingArray = []\n\n        # count the number of agent in each team\n        n_team_agent = {}\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            if team not in n_team_agent: n_team_agent[team] = 0\n            SubTaskConfig.agent_list[i]['uid'] = i\n            SubTaskConfig.agent_list[i]['tid'] = n_team_agent[team]\n            n_team_agent[team] += 1\n\n        # push agent init info one by one\n        for i, agent_info in enumerate(SubTaskConfig.agent_list):\n            team = agent_info['team']\n            agent_info['n_team_agent'] = n_team_agent[team]\n            init_fn = getattr(self, agent_info['init_fn_name'])\n            AgentSettingArray.append(init_fn(agent_info, pos_ro))\n\n        self.agents  = [Agent(team=a['team'], team_id=a['tid'], uid=a['uid']) for a in SubTaskConfig.agent_list]\n        \n        # refer to struct.cpp, FParsedDataInput\n        resp = self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'reset',\n            'NumAgents' : len(SubTaskConfig.agent_list),\n            'AgentSettingArray': AgentSettingArray,  # refer to struct.cpp, FAgentProperty\n            'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n            'TimeStep' : 0,\n            'Actions': None,\n        }))\n        resp = json.loads(resp)\n        # make sure the map (level in UE) is correct\n        # assert resp['dataGlobal']['levelName'] == 'UhmapLargeScale'\n\n        assert len(resp['dataArr']) == len(AgentSettingArray)\n        return self.parse_response_ob_info(resp)\n\n\n    def step(self, act):\n        \"\"\"\n            step 函数,act中包含了所有agent的决策\n        \"\"\"\n        assert len(act) == self.n_agents\n\n        # translate actions to the format recognized by unreal engine\n        if ScenarioConfig.ActionFormat == 'Single-Digit':\n            act_send = [digit2act_dictionary[a] for a in act]\n        elif ScenarioConfig.ActionFormat == 'Multi-Digit':\n            act_send = [decode_action_as_string(a) for a in act]\n        elif ScenarioConfig.ActionFormat == 'ASCII':            \n            act_send = [digitsToStrAction(a) for a in act]\n        else:\n            raise \"ActionFormat is wrong!\"\n\n        # simulation engine IO\n        resp = json.loads(self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'step',\n            'TimeStep': self.t,\n            'Actions': None,\n            'StringActions': act_send,\n        })))\n\n        # get obs for RL, info for script AI\n        ob, info = self.parse_response_ob_info(resp)\n\n        # generate reward, get the episode ending infomation\n        RewardForAllTeams, WinningResult = self.gen_reward_and_win(resp)\n        if WinningResult is not None: \n            info.update(WinningResult)\n            assert resp['dataGlobal']['episodeDone']\n            done = True\n        else:\n            done = False\n\n        if resp['dataGlobal']['timeCnt'] >= ScenarioConfig.MaxEpisodeStep:\n            assert done\n\n        return (ob, RewardForAllTeams, done, info)  # choose this if RewardAsUnity\n\n    def parse_event(self, event):\n        \"\"\"\n            解析环境返回的一些关键事件,\n            如智能体阵亡,某队伍胜利等等。\n            关键事件需要在ue中进行定义.\n            该设计极大地简化了python端奖励的设计流程,\n            减小了python端的运算量。\n        \"\"\"\n        if not hasattr(self, 'pattern'): self.pattern = re.compile(r'<([^<>]*)>([^<>]*)')\n        return {k:v for k,v  in re.findall(self.pattern, event)}\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            if event_parsed['Event'] == 'EndEpisode':\n                PreyRank = -1\n                PreyReward = 0\n                EndReason = event_parsed['EndReason']\n\n                WinningResult = {\"team_ranking\": [0], \"end_reason\": EndReason}\n\n                reward = [0]\n\n        return reward, WinningResult\n\n    def step_skip(self):\n        \"\"\"\n            跳过一次决策,无用的函数\n        \"\"\"\n        return self.client.send_and_wait_reply(json.dumps({\n            'valid': True,\n            'DataCmd': 'skip_frame',\n        }))\n\n    def find_agent_by_uid(self, uid):\n        \"\"\"\n            用uid查找智能体(带缓存加速机制)\n        \"\"\"\n        if not hasattr(self, 'uid_to_agent_dict'):\n            self.uid_to_agent_dict = {}\n            self.uid_to_agent_dict.update({agent.uid:agent for agent in self.agents}) \n            if isinstance(uid, str):\n                self.uid_to_agent_dict.update({str(agent.uid):agent for agent in self.agents}) \n        return self.uid_to_agent_dict[uid]\n\n\n\n    def parse_response_ob_info(self, resp):\n        \"\"\"\n            粗解析智能体的观测,例如把死智能体的位置替换为inf(无穷远),\n            将智能体的agentLocation从字典形式转变为更简洁的(x,y,z)tuple形式\n        \"\"\"\n        assert resp['valid']\n        resp['dataGlobal']['distanceMat'] = np.array(resp['dataGlobal']['distanceMat']['flat_arr']).reshape(self.n_agents,self.n_agents)\n        \n        if len(resp['dataGlobal']['events'])>0:\n            tmp = [kv.split('>') for kv in resp['dataGlobal']['events'][0].split('<') if kv]\n            info_parse = {t[0]:t[1] for t in tmp}\n\n        info_dict = resp\n        for info in info_dict['dataArr']: \n            alive = info['agentAlive']\n\n            if alive:\n                agentLocation = info.pop('agentLocation')\n                agentRotation = info.pop('agentRotation')\n                agentVelocity = info.pop('agentVelocity')\n                agentScale = info.pop('agentScale')\n                info['agentLocationArr'] = (agentLocation['x'], agentLocation['y'], agentLocation['z'])\n                info['agentVelocityArr'] = (agentVelocity['x'], agentVelocity['y'], agentVelocity['z'])\n                info['agentRotationArr'] = (agentRotation['yaw'], agentRotation['pitch'], agentRotation['roll'])\n                info['agentScaleArr'] = (agentScale['x'], agentScale['y'], agentScale['z'])\n                info.pop('previousAction')\n                info.pop('availActions')\n                # info.pop('rSVD1')\n                info.pop('interaction')\n            else:\n                inf = float('inf')\n                info['agentLocationArr'] = (inf, inf, inf)\n                info['agentVelocityArr'] = (inf, inf, inf)\n                info['agentRotationArr'] = (inf, inf, inf)\n\n        info = resp['dataArr']\n        for i, agent_info in enumerate(info):\n            self.agents[i].update_agent_attrs(agent_info)\n\n        self.key_obj = self.extract_key_gameobj(resp)\n\n        # return ob, info\n        return self.make_obs(resp), info_dict\n\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n\n    def init_attack(self, agent_info, pos_ro):\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        agent_class = agent_info['type']\n        n_agent = agent_info['n_team_agent']\n        x,z = init_position_helper(x_max=400, x_min=-400, y_max=900, y_min=1300, total=n_agent, this=tid)   # 脚本-白给\n        # x,y = init_position_helper(x_max=3500.0, x_min=5000.0, y_max=-4470.0, y_min=4470.0, total=8, this=tid)  # 脚本-击杀\n        y = -1091.19458\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n            'DebugAgent': False,\n            # max drive/fly speed\n            'MaxMoveSpeed':  1000,\n            # also influence object mass, please change it with causion!\n            'AgentScale'  : { 'x': 0.25,  'y': 0.25, 'z': 0.25, },\n            \"DodgeProb\": 0.0,           # probability of escaping dmg 闪避概率, test ok\n            # team belonging\n            'AgentTeam': team,\n            # choose ue class to init\n            'ClassName': agent_class,\n            # debugging\n            'RSVD1': '',\n            # the rank of agent inside the team\n            'IndexInTeam': tid, \n            # the unique identity of this agent in simulation system\n            'UID': uid, \n            # show color\n            'Color':'(R=0,G=1,B=0,A=1)',\n            # initial location\n            'InitLocation': { 'x': x,  'y': y, 'z': z, },\n            # initial facing direction et.al.\n            'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapReproduceConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'DummyAgent',       'init_fn_name':'init_attack',  } for i in range(200)\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapWaterdrop.py",
    "content": "import json, copy, re, os, inspect, os\nimport numpy as np\nfrom UTIL.tensor_ops import my_view, repeat_at\nfrom ...common.base_env import RawObsArray\nfrom ..actionset_v3 import digitsToStrAction\nfrom ..agent import Agent\nfrom ..uhmap_env_wrapper import UhmapEnv, ScenarioConfig\nfrom .UhmapWaterdropConf import SubTaskConfig\nfrom .SubtaskCommonFn import UhmapCommonFn\nfrom .cython_func import tear_num_arr\n\n\n\n\nclass UhmapWaterdrop(UhmapCommonFn, UhmapEnv):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.observation_space = self.make_obs(get_shape=True)\n        self.SubTaskConfig = SubTaskConfig\n        inspect.getfile(SubTaskConfig)\n        assert os.path.basename(inspect.getfile(SubTaskConfig)) == type(self).__name__+'Conf.py', \\\n                ('make sure you have imported the correct SubTaskConfig class')\n\n    def init_ship(self, agent_info, pos_ro):\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        tid = agent_info['tid'] # tid 是智能体在队伍中的编号 \n        uid = agent_info['uid'] # uid 是智能体在仿真中的唯一编号\n        \n        x = -2000\n        y = (tid * 1000) # tid 是智能体在队伍中的编号 \n        z = 500 # \n\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  500,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # custom args\n                'RSVD1': '',\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n    def init_waterdrop(self, agent_info, pos_ro):\n        agent_class = agent_info['type']\n        team = agent_info['team']\n        tid = agent_info['tid']\n        uid = agent_info['uid']\n        \n        x = +2000\n        y = (tid * 200)\n        z = 500 # \n\n        agent_property = copy.deepcopy(SubTaskConfig.AgentPropertyDefaults)\n        agent_property.update({\n                'DebugAgent': False,\n                # max drive/fly speed\n                'MaxMoveSpeed':  1000,\n                # also influence object mass, please change it with causion!\n                'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },\n                # team belonging\n                'AgentTeam': team,\n                # choose ue class to init\n                'ClassName': agent_class,\n                # custom args\n                'RSVD1': '-MyCustomArg1=abc -MyCustomArg2=12345',\n                # the rank of agent inside the team\n                'IndexInTeam': tid, \n                # the unique identity of this agent in simulation system\n                'UID': uid, \n                # show color\n                'Color':'(R=0,G=1,B=0,A=1)' if team==0 else '(R=0,G=0,B=1,A=1)',\n                # initial location\n                'InitLocation': { 'x': x,  'y': y, 'z': z, },\n                # initial facing direction et.al.\n                'InitRotator': { 'pitch': 0,  'roll': 0, 'yaw': 0, },\n        }),\n        return agent_property\n\n    def extract_key_gameobj(self, resp):\n        \"\"\"\n            获取非智能体的仿真物件,例如重要landmark等\n        \"\"\"\n        keyObjArr = resp['dataGlobal']['keyObjArr']\n        return keyObjArr\n\n    def gen_reward_and_win(self, resp):\n        \"\"\"\n            奖励的设计在此定义,\n            (UE端编程死板,虽然预留了相关字段,\n            但请不要在UE端提供奖励的定义。)\n            建议:在UE端定义触发奖励的事件,如智能体阵亡、战术目标完成等,见parse_event\n        \"\"\"\n        reward = [0]*self.n_teams\n        events = resp['dataGlobal']['events']\n        WinningResult = None\n        for event in events: \n            event_parsed = self.parse_event(event)\n            # if event_parsed['Event'] == 'Destroyed':\n            #     team = self.find_agent_by_uid(event_parsed['UID']).team\n            #     reward[team]    -= 0.05    # this team\n            #     reward[1-team]  += 0.10    # opp team\n            if event_parsed['Event'] == 'EndEpisode':\n                # print([a.alive * a.hp for a in self.agents])\n                WaterdropWin = False\n                WaterdropRank = False\n                WaterdropReward = 0\n                ShipWin = -1\n                ShipRank = -1\n                ShipReward = 0\n                EndReason = event_parsed['EndReason']\n                # According to MISSION\\uhmap\\SubTasks\\UhmapWaterdropConf.py, team 0 is Ship team, team 1 is Waterdrop team\n                if EndReason == \"ShipNumLessThanTheshold\" or EndReason == \"Team_0_AllDead\":\n                    WaterdropWin = True; WaterdropRank = 0; WaterdropReward = 1\n                    ShipWin = False; ShipRank = 1; ShipReward = -1\n                elif EndReason == \"TimeMaxCntReached\" or EndReason == \"Team_1_AllDead\":\n                    WaterdropWin = False; WaterdropRank = 1; WaterdropReward = -1\n                    ShipWin = True; ShipRank = 0; ShipReward = 1\n                else:\n                    print('unexpected end reaon:', EndReason)\n                    \n                WinningResult = {\"team_ranking\": [ShipRank, WaterdropRank], \"end_reason\": EndReason}\n\n                reward = [ShipReward, WaterdropReward]\n        # print(reward)\n        return reward, WinningResult\n\n\n    @staticmethod\n    def item_random_mv(src,dst,prob,rand=False):\n        assert len(src.shape)==1; assert len(dst.shape)==1\n        if rand: np.random.shuffle(src)\n        len_src = len(src)\n        n_mv = (np.random.rand(len_src) < prob).sum()\n        item_mv = src[range(len_src-n_mv,len_src)]\n        src = src[range(0,0+len_src-n_mv)]\n        dst = np.concatenate((item_mv, dst))\n        return src, dst\n\n    @staticmethod\n    def get_binary_array(n_int, n_bits=8, dtype=np.float32):\n        arr = np.zeros((*n_int.shape, n_bits), dtype=dtype)\n        for i in range(n_bits):\n            arr[:, i] = (n_int%2==1).astype(int)\n            n_int = n_int / 2\n            n_int = n_int.astype(np.int8)\n        return arr\n\n\n\n    def make_obs(self, resp=None, get_shape=False):\n        # CORE_DIM = 38\n        CORE_DIM = 23\n        assert ScenarioConfig.obs_vec_length == CORE_DIM\n        if get_shape:\n            return CORE_DIM\n\n        # temporary parameters\n        OBS_RANGE_PYTHON_SIDE = 15000\n        MAX_NUM_OPP_OBS = 5\n        MAX_NUM_ALL_OBS = 5\n        \n        # get and calculate distance array\n        pos3d_arr = np.zeros(shape=(self.n_agents, 3), dtype=np.float32)\n        for i, agent in enumerate(self.agents): pos3d_arr[i] = agent.pos3d\n        # use the distance matrix calculated by unreal engine to accelerate\n        # dis_mat = distance_matrix(pos3d_arr)    # dis_mat is a matrix, shape = (n_agent, n_agent)\n        dis_mat = resp['dataGlobal']['distanceMat']\n        alive_all = np.array([agent.alive for agent in self.agents])\n        try:\n            dis_mat[~alive_all,:] = +np.inf\n            dis_mat[:,~alive_all] = +np.inf\n        except:\n            pass\n        # get team list\n        team_belonging = np.array([agent.team for agent in self.agents])\n\n        # gather the obs arr of all known agents\n        obs_arr = RawObsArray(key='Agent')\n\n        if not hasattr(self, \"uid_binary\"):\n            self.uid_binary = self.get_binary_array(np.arange(self.n_agents), 10)\n\n        for i, agent in enumerate(self.agents):\n            assert agent.location is not None\n            assert agent.uid == i\n\n            obs_arr.append(\n                self.uid_binary[i]  # 0~9\n            )\n            obs_arr.append([\n                agent.index,    # 10\n                agent.team,     # 11\n                agent.alive,    # 12\n                agent.uid_remote, # 13\n            ])\n            obs_arr.append( #[14,15,16,17,18,19]\n                agent.pos3d\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                # tear_num_arr(agent.pos3d, 6, ScenarioConfig.ObsBreakBase, 0) # 3 -- > 3*6 = 18 , 18-3=15, 23+15 = 38\n            )\n            obs_arr.append(\n                agent.vel3d\n            )\n            obs_arr.append([\n                agent.hp,\n                agent.yaw,\n                agent.max_speed,\n            ])\n        obs_ = obs_arr.get()\n        new_obs = my_view(obs_, [self.n_agents, -1])\n\n        assert CORE_DIM == new_obs.shape[-1]\n        OBS_ALL_AGENTS = np.zeros(shape=(self.n_agents, MAX_NUM_OPP_OBS+MAX_NUM_ALL_OBS, CORE_DIM))\n\n        # now arranging the individual obs\n        for i, agent in enumerate(self.agents):\n            if not agent.alive:\n                OBS_ALL_AGENTS[i, :] = np.nan\n                continue\n\n            # if alive\n            # scope <all>\n            dis2all = dis_mat[i, :]\n            is_ally = (team_belonging == agent.team)\n\n            # scope <opp/hostile>\n            a2h_dis = dis2all[~is_ally]\n            h_alive = alive_all[~is_ally]\n            h_feature = new_obs[~is_ally]\n            h_iden_sort  = np.argsort(a2h_dis)[:MAX_NUM_OPP_OBS]\n            a2h_dis_sorted = a2h_dis[h_iden_sort]\n            h_alive_sorted = h_alive[h_iden_sort]\n            h_vis_mask = (a2h_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & h_alive_sorted\n            \n            # scope <all>\n            h_vis_index = h_iden_sort[h_vis_mask]\n            h_invis_index = h_iden_sort[~h_vis_mask]\n            h_vis_index, h_invis_index = self.item_random_mv(src=h_vis_index, dst=h_invis_index,prob=0, rand=True)\n            h_ind = np.concatenate((h_vis_index, h_invis_index))\n            h_msk = np.concatenate((h_vis_index<0, h_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            a2h_feature_sort = h_feature[h_ind]\n            a2h_feature_sort[h_msk] = 0\n            if len(a2h_feature_sort)<MAX_NUM_OPP_OBS:\n                a2h_feature_sort = np.concatenate((\n                    a2h_feature_sort, \n                    np.ones(shape=(MAX_NUM_OPP_OBS-len(a2h_feature_sort), CORE_DIM))+np.nan), axis=0)\n\n            # scope <ally/friend>\n            a2f_dis = dis2all[is_ally]\n            f_alive = alive_all[is_ally]\n            f_feature = new_obs[is_ally]\n            f_iden_sort  = np.argsort(a2f_dis)[:MAX_NUM_ALL_OBS]\n            a2f_dis_sorted = a2f_dis[f_iden_sort]\n            f_alive_sorted = f_alive[f_iden_sort]\n            f_vis_mask = (a2f_dis_sorted <= OBS_RANGE_PYTHON_SIDE) & f_alive_sorted\n\n            # scope <all>\n            f_vis_index = f_iden_sort[f_vis_mask]\n            self_vis_index = f_vis_index[:1] # seperate self and ally\n            f_vis_index = f_vis_index[1:]    # seperate self and ally\n            f_invis_index = f_iden_sort[~f_vis_mask]\n            f_vis_index, f_invis_index = self.item_random_mv(src=f_vis_index, dst=f_invis_index,prob=0, rand=True)\n            f_ind = np.concatenate((self_vis_index, f_vis_index, f_invis_index))\n            f_msk = np.concatenate((self_vis_index<0, f_vis_index<0, f_invis_index>=0)) # \"<0\" project to False; \">=0\" project to True\n            self_ally_feature_sort = f_feature[f_ind]\n            self_ally_feature_sort[f_msk] = 0\n            if len(self_ally_feature_sort)<MAX_NUM_ALL_OBS:\n                self_ally_feature_sort = np.concatenate((\n                    self_ally_feature_sort, \n                    np.ones(shape=(MAX_NUM_ALL_OBS-len(self_ally_feature_sort), CORE_DIM))+np.nan\n                ), axis=0)\n            OBS_ALL_AGENTS[i,:] = np.concatenate((self_ally_feature_sort, a2h_feature_sort), axis = 0)\n\n\n        # the last part of observation is the list of core game objects\n        MAX_OBJ_NUM_ACCEPT = 0\n        self.N_Obj = len(self.key_obj)\n        if MAX_OBJ_NUM_ACCEPT!=0:\n            OBJ_UID_OFFSET = 32768\n            obs_arr = RawObsArray(key = 'GameObj')\n            for i, obj in enumerate(self.key_obj):\n                assert obj['uId'] - OBJ_UID_OFFSET == i\n                obs_arr.append(\n                    -self.uid_binary[i] # reverse uid binary, self.uid_binary[i]\n                )\n                obs_arr.append([\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.index,\n                    -1,                             #agent.team,\n                    True,                           #agent.alive,\n                    obj['uId'] - OBJ_UID_OFFSET,    #agent.uid_remote,\n                ])\n                # tear_num_arr(agent.pos3d, n_digits=6, base=10, mv_left=0)\n                obs_arr.append(\n                    [\n                        obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    ]\n                    # tear_num_arr([\n                    #     obj['location']['x'], obj['location']['y'], obj['location']['z']  # agent.pos3d\n                    # ], 6, ScenarioConfig.ObsBreakBase, 0)\n                )\n                \n                obs_arr.append([\n                    obj['velocity']['x'], obj['velocity']['y'], obj['velocity']['z']  # agent.vel3d\n                ]+\n                [\n                    -1,                         # hp\n                    obj['rotation']['yaw'],     # yaw \n                    0,                          # max_speed\n                ])\n            OBS_GameObj = my_view(obs_arr.get(), [len(self.key_obj), -1])[:MAX_OBJ_NUM_ACCEPT, :]\n            OBS_GameObj = repeat_at(OBS_GameObj, insert_dim=0, n_times=self.n_agents)\n            OBS_ALL_AGENTS = np.concatenate((OBS_ALL_AGENTS, OBS_GameObj), axis=1)\n\n        return OBS_ALL_AGENTS\n\n\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/UhmapWaterdropConf.py",
    "content": "\nclass SubTaskConfig():\n    agent_list = [\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },    # 0 \n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },\n        { 'team':0, 'type':'Ship',   'init_fn_name':'init_ship',  },    # 9\n        \n        { 'team':1, 'type':'Waterdrop',  'init_fn_name':'init_waterdrop',  }, # 10\n        { 'team':1, 'type':'Waterdrop',  'init_fn_name':'init_waterdrop',  }, # 11\n    ]\n\n\n    AgentPropertyDefaults = {\n        'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n        'DebugAgent': False,     \n        'AgentTeam': 0,             # int AgentTeam = 0;\n        'IndexInTeam': 0,           # int IndexInTeam = 0;\n        'UID': 0,                   # int UID = 0;\n        'MaxMoveSpeed': 600,        # move speed, test ok\n        'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n        'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n        'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n        'AgentHp':100,\n        \"WeaponCD\": 1,              # weapon fire rate\n        \"IsTeamReward\": True,\n        \"Type\": \"\",\n        \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n        \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n        \"FireRange\": 1000.0,        # <= 1500\n        \"GuardRange\": 1400.0,       # <= 1500\n        \"PerceptionRange\": 1500.0,       # <= 1500\n        'Color':'(R=0,G=1,B=0,A=1)',    # color\n        \"FireRange\": 1000,\n        'RSVD1':'',\n        'RSVD2':'',\n    }\n\n    obs_vec_length = 23\n    obs_n_entity = 10\n    ActionFormat = 'ASCII'\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/SubTasks/cython_func.pyx",
    "content": "import numpy as np\ncimport numpy as np\ncimport cython\nnp.import_array()\n\n\nctypedef fused DTYPE_all_t:\n    np.float32_t\n    np.float64_t\n    np.int64_t\n    np.int32_t  # to compat Wi\nctypedef fused DTYPE_t:\n    np.float32_t\n    np.float64_t\n\n\nctypedef fused DTYPE_intlong_t:\n    np.int64_t\n    np.int32_t  # to compat Windows\n    \n    \n    \n# x: input\n# n_digit: output dimension\n# base: 进制\n@cython.boundscheck(False)\n@cython.wraparound(False)\n@cython.nonecheck(False)\ndef tear_number_apart(np.float64_t x, DTYPE_intlong_t n_digit, DTYPE_intlong_t base=16, DTYPE_all_t mv_left=8):\n    cdef np.ndarray out = np.zeros(n_digit, dtype=float)\n    cdef int p = n_digit\n    cdef float tmp = 0\n    reverse = x < 0\n    cdef float m_init = base\n    if reverse: x = -x\n    m_init = m_init ** mv_left\n    x = x * m_init\n    for _ in range(n_digit):\n        p -= 1\n        if p==0: \n            out[p] = x\n            break\n        tmp = x % base\n        out[p] = tmp\n        x = x // base\n    if reverse: out = -out\n    return out\n\n\ndef tear_num_arr(arr, DTYPE_intlong_t n_digit, DTYPE_intlong_t base, DTYPE_all_t mv_left):\n    return np.concatenate([tear_number_apart(x, n_digit, base, mv_left) for x in arr], axis=0)\n\ndef comb_num_back(arr, n_digit, base, mv_left):\n    out = 0\n    tmp = base ** (n_digit - mv_left - 1)\n    for x in arr:\n        out += x * tmp\n        tmp = tmp/base\n        \n    return out\n\n\n'''\ntest <1>\n\nparts = tear_number_apart(255, n_digit=10, base=2, mv_left=1)\nprint(parts)\ncomb_num_back(parts, n_digit=10, base=2, mv_left=1)\n\n\ntest <2>\n\nparts = tear_number_apart(255.778, n_digit=10, base=10, mv_left=-1)\nprint(parts)\ncomb_num_back(parts, n_digit=10, base=10, mv_left=-1)\n\ntest <3>\n\nfor i in range(1000):\n    q = (np.random.rand() - 0.5)*1e3\n    parts = tear_number_apart(q, n_digit=10, base=10, mv_left=0)\n    print(q, parts)\n    res = np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q) < 1e-6\n    if not res:\n        print('??? np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q)', np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q))\n        assert False\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/actionset.py",
    "content": "import numpy as np\nActDigitLen = 100\ndef strActionToDigits(act_string):\n    t = [ord(c) for c in act_string]\n    d_len = len(t)\n    assert d_len <= ActDigitLen, (\"Action string is tooo long! Don't be wordy. Or you can increase ActDigitLen above.\")\n    pad = [-1 for _ in range(ActDigitLen-d_len)]\n    return (t+pad)\n\ndef digitsToStrAction(digits):\n    if all([a==0 for a in digits]): return 'ActionSet3::N/A;N/A'\n    arr = [chr(d) for d in digits.astype(int) if d >= 0]\n    return ''.join(arr)\n\n\"\"\"\n'ActionSet3::ChangeHeight;100'\n\"\"\""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/actionset_v3.py",
    "content": "import numpy as np\nActDigitLen = 100\ndef strActionToDigits(act_string):\n    t = [ord(c) for c in act_string]\n    d_len = len(t)\n    assert d_len <= ActDigitLen, (\"Action string is tooo long! Don't be wordy. Or you can increase ActDigitLen above.\")\n    pad = [-1 for _ in range(ActDigitLen-d_len)]\n    return (t+pad)\n\ndef digitsToStrAction(digits):\n    if all([a==0 for a in digits]): return 'ActionSet3::N/A;N/A'\n    arr = [chr(d) for d in digits.astype(int) if d >= 0]\n    return ''.join(arr)\n\n\"\"\"\n'ActionSet3::ChangeHeight;100'\n\"\"\""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/actset_lookup.py",
    "content": "import numpy as np\n\n# # # # # # # # # # # # # # # # # # # # # # #\n# # # # # Part 1, interface for RL # # # # # \n# # # # # # # # # # # # # # # # # # # # # # #\n\ndictionary_items = [\n    'ActionSet2::N/A;N/A',                  # 0\n    'ActionSet2::Idle;DynamicGuard'     ,   # 1\n    'ActionSet2::Idle;StaticAlert'      ,   # 2\n    'ActionSet2::Idle;AggressivePersue' ,   # 3\n    'ActionSet2::SpecificMoving;Dir+X'  ,   # 4\n    'ActionSet2::SpecificMoving;Dir+Y'  ,   # 5\n    'ActionSet2::SpecificMoving;Dir-X'  ,   # 6\n    'ActionSet2::SpecificMoving;Dir-Y'  ,   # 7\n    'ActionSet2::SpecificAttacking;T1-0',   # 8\n    'ActionSet2::SpecificAttacking;T1-1',   # 9\n    'ActionSet2::SpecificAttacking;T1-2',   # 10\n    'ActionSet2::SpecificAttacking;T1-3',   # 11\n    'ActionSet2::SpecificAttacking;T1-4',   # 12\n    \n    'ActionSet2::SpecificAttacking;T0-0',   # 13\n    'ActionSet2::SpecificAttacking;T0-1',   # 14\n    'ActionSet2::SpecificAttacking;T0-2',   # 15\n    'ActionSet2::SpecificAttacking;T0-3',   # 16\n    'ActionSet2::SpecificAttacking;T0-4',   # 17\n\n    'ActionSet2::PatrolMoving;Dir+X'    ,\n    'ActionSet2::PatrolMoving;Dir+Y'    ,\n    'ActionSet2::PatrolMoving;Dir-X'    ,\n    'ActionSet2::PatrolMoving;Dir-Y'    ,\n\n    'ActionSet2::Idle;AsFarAsPossible',  \n    'ActionSet2::Idle;StayWhenTargetInRange',  \n    'ActionSet2::Idle;StayWhenTargetInHalfRange' ,  \n]\n\ndictionary_n_actions = len(dictionary_items)\n\ndigit2act_dictionary = {\n    i: dictionary_items[i] for i, item in enumerate(dictionary_items)\n}\n\nact2digit_dictionary = {\n    dictionary_items[i]:i for i, item in enumerate(dictionary_items)\n}\n\n# # # # # # # # # # # # # # # # # # # # # # #\n# # # # # Part 2, translate actions # # # # # \n# # # # # # # # # # # # # # # # # # # # # # #\n\nagent_json2local_attrs = [\n            # json key       ----->    agent key\n            ('agentAlive',              'alive'),\n            ('agentTeam',               'team'),\n            ('indexInTeam',             'index'),\n            ('uId',                     'uid_remote'),\n            ('maxMoveSpeed',            'max_speed'),\n            ('agentLocationArr',        'location'),\n            ('agentRotationArr',        'rotation'),\n            ('agentScaleArr',           'scale3'),\n            ('agentVelocityArr',        'velocity'),\n            ('agentHp',                 'hp'),\n            ('weaponCD',                'weapon_cd'),\n            ('type',                    'type'),\n]\n\n\n    # 'ActionSet2::Idle;AsFarAsPossible',  \n    # 'ActionSet2::Idle;StayWhenTargetInRange',  \n    # 'ActionSet2::Idle;StayWhenTargetInHalfRange' ,  \n\ndef encode_action_as_digits(main_cmd, sub_cmd, x=None, y=None, z=None, UID=None, T=None, T_index=None):\n    main_cmd_encoder = {\n        \"Idle\"                  : 0, \n        \"SpecificMoving\"        : 1, \n        \"PatrolMoving\"          : 2, \n        \"SpecificAttacking\"     : 3, \n        \"N/A\"                   : 4, \n    }\n    sub_cmd_encoder = {\n        \"DynamicGuard\"              : 0 , \n        \"StaticAlert\"               : 1 , \n        \"AggressivePersue\"          : 2 , \n        \"SpecificAttacking\"         : 3 , \n        \"AsFarAsPossible\"           : 4 , \n        \"StayWhenTargetInRange\"     : 5 , \n        \"StayWhenTargetInHalfRange\" : 6 , \n        \"N/A\"                       : 7 , \n        'Dir+X'                     : 8 , \n        'Dir+X+Y'                   : 9 , \n        'Dir+Y'                     : 10,\n        'Dir-X+Y'                   : 11,\n        'Dir-X'                     : 12,\n        'Dir-X-Y'                   : 13,\n        'Dir-Y'                     : 14,\n        'Dir+X-Y'                   : 15,\n    }\n    return np.array([\n        main_cmd_encoder[main_cmd],\n        sub_cmd_encoder[sub_cmd],\n        x if x is not None                  else np.inf,\n        y if y is not None                  else np.inf,\n        z if z is not None                  else np.inf,\n        UID if UID is not None              else np.inf,\n        T if T is not None                  else np.inf,\n        T_index if T_index is not None      else np.inf\n    ])\n\n\ndef decode_action_as_string(digits):\n    main_cmd_decoder = {\n        0 :\"Idle\"                , \n        1 :\"SpecificMoving\"      , \n        2 :\"PatrolMoving\"        , \n        3 :\"SpecificAttacking\"   , \n        4 :\"N/A\"                 , \n    }\n    sub_cmd_decoder =  {\n        0  : \"DynamicGuard\"              , \n        1  : \"StaticAlert\"               , \n        2  : \"AggressivePersue\"          , \n        3  : \"SpecificAttacking\"         , \n        4  : \"AsFarAsPossible\"           , \n        5  : \"StayWhenTargetInRange\"     , \n        6  : \"StayWhenTargetInHalfRange\" , \n        7  : \"N/A\"                       , \n        8  : 'Dir+X'                     , \n        9  : 'Dir+X+Y'                   , \n        10 : 'Dir+Y'                     ,\n        11 : 'Dir-X+Y'                   ,\n        12 : 'Dir-X'                     ,\n        13 : 'Dir-X-Y'                   ,\n        14 : 'Dir-Y'                     ,\n        15 : 'Dir+X-Y'                   ,\n    }\n    main_cmd = main_cmd_decoder[digits[0]]\n    sub_cmd = sub_cmd_decoder[digits[1]]\n    x       = digits[2] if np.isfinite(digits[2]) else None\n    y       = digits[3] if np.isfinite(digits[3]) else None\n    z       = digits[4] if np.isfinite(digits[4]) else None\n    UID     = digits[5] if np.isfinite(digits[5]) else None\n    T       = digits[6] if np.isfinite(digits[6]) else None\n    T_index = digits[7] if np.isfinite(digits[7]) else None\n\n    if main_cmd == \"Idle\":\n        res = 'ActionSet2::Idle;%s'%sub_cmd\n        assert res in dictionary_items, '指令错误无法解析'\n    elif main_cmd == \"SpecificMoving\":\n        if sub_cmd == 'N/A':\n            res = 'ActionSet2::SpecificMoving;X=%f Y=%f Z=%f'%(x,y,z)\n        else:\n            res = 'ActionSet2::SpecificMoving;%s'%sub_cmd\n    elif main_cmd == \"PatrolMoving\":\n        if sub_cmd == 'N/A':\n            res = 'ActionSet2::PatrolMoving;X=%f Y=%f Z=%f'%(x,y,z)\n        else:\n            res = 'ActionSet2::PatrolMoving;%s'%sub_cmd\n    elif main_cmd == \"SpecificAttacking\":\n        # 'ActionSet2::SpecificAttacking;T1-3',\n        # 'ActionSet2::SpecificAttacking;UID-4',\n        assert sub_cmd == 'N/A', '指令错误无法解析'\n        if UID is not None:\n            res = 'ActionSet2::SpecificAttacking;UID-%d'%UID\n        else:\n            res = 'ActionSet2::SpecificAttacking;T%d-%d'%(T,T_index)\n    elif main_cmd == \"N/A\":\n        res = 'ActionSet2::N/A;N/A'\n    else:\n        print('指令错误无法解析')\n        assert False\n    return res\n\n\n# # # # # # # # # # # # # # # # # # # # # # #\n# # # # # Part 3, agent init defaults # # # #\n# # # # # # # # # # # # # # # # # # # # # # #\n\nAgentPropertyDefaults = {\n    'ClassName': 'RLA_CAR',     # FString ClassName = \"\";\n    'DebugAgent': False,     \n    'AgentTeam': 0,             # int AgentTeam = 0;\n    'IndexInTeam': 0,           # int IndexInTeam = 0;\n    'UID': 0,                   # int UID = 0;\n    'MaxMoveSpeed': 600,        # move speed, test ok\n    'InitLocation': { 'x': 0,  'y': 0, 'z': 0, },\n    'InitRotation': { 'x': 0,  'y': 0, 'z': 0, },\n    'AgentScale'  : { 'x': 1,  'y': 1, 'z': 1, },     # agent size, test ok\n    'InitVelocity': { 'x': 0,  'y': 0, 'z': 0, },\n    'AgentHp':100,\n    \"WeaponCD\": 1,              # weapon fire rate\n    \"IsTeamReward\": True,\n    \"Type\": \"\",\n    \"DodgeProb\": 0.8,           # probability of escaping dmg 闪避概率, test ok\n    \"ExplodeDmg\": 25,           # ms explode dmg. test ok\n    \"FireRange\": 1000.0,        # <= 1500\n    \"GuardRange\": 1400.0,       # <= 1500\n    \"PerceptionRange\": 1500.0,       # <= 1500\n    'Color':'(R=0,G=1,B=0,A=1)',    # color\n    \"FireRange\": 1000,\n    'RSVD1':'',\n    'RSVD2':'',\n}\n\n\n\n# # # # # # # # # # # # # # # # # # # # # # #\n# # # # # Part 3, framerate selection # # # #\n# # # # # # # # # # # # # # # # # # # # # # #\n# Check whether a number can be represented precisely by a float\ndef binary_friendly(x):\n    y_f16 = np.array(x, dtype=np.float16)\n    y_f64 = np.array(x, dtype=np.float64)\n    t = y_f64 - y_f16\n    assert t.dtype == np.float64\n    return (t==0)\n\n\n# '''\n# <Agent>T0-5<UID>5<Event>Destroyed \n# <Agent>T1-6<UID>16<Event>Destroyed \n# <Agent>T0-4<UID>4<Event>Destroyed \n# <Agent>T0-2<UID>2<Event>Destroyed \n# <Agent>T1-7<UID>17<Event>Destroyed \n# <Agent>T1-1<UID>11<Event>Destroyed \n# <Agent>T0-8<UID>8<Event>Destroyed \n# <Agent>T0-7<UID>7<Event>Destroyed \n# <Agent>T1-3<UID>13<Event>Destroyed\n# <Agent>T0-9<UID>9<Event>Destroyed\n# <Agent>T0-6<UID>6<Event>Destroyed\n# <Agent>T0-0<UID>0<Event>Destroyed\n# <Agent>T1-2<UID>12<Event>Destroyed\n# <Agent>T0-1<UID>1<Event>Destroyed\n# <Agent>T0-3<UID>3<Event>Destroyed\n# <Event>EndEpisode<EndReason><AllRLAgentDown>Lose<WinTeam>1\n# '''\n\n\n\n\n\n################## ########################## ########################\n################## ########################## ########################\n################## ########################## ########################\n################## single digit encode, not used ########################\n# h_map_center = (-7290.0, 6010.0)\n# h_grid_size = 400\n# v_ground = 340\n# v_grid_size = 1000\n\n# x_arr = np.array([h_map_center[0]+v_grid_size*i for i in range(-20, 20)])   # 0~39, 40, 1\n# y_arr = np.array([h_map_center[1]+v_grid_size*i for i in range(-20, 20)])   # 0~39, 40, 40\n# z_arr = np.array([v_ground+v_grid_size*i for i in range(4)])                # 0~3,  4,  1600\n# # offset                                                                    # 0~1,  2,  6400\n\n\n# # output $y \\in [1000, 12800]$\n# def _2digit(main_cmd, x, y, z):\n#     z_logit = np.argmin(np.abs(z - z_arr))\n#     x_logit = np.argmin(np.abs(x - x_arr))\n#     y_logit = np.argmin(np.abs(y - y_arr))\n#     if main_cmd=='SpecificMoving': cmd_logit = 0\n#     elif main_cmd=='PatrolMoving': cmd_logit = 1\n#     ls_mod = [1,40,1600,6400]\n#     offset = 1000\n#     x = np.array([x_logit, y_logit, z_logit, cmd_logit])\n#     print(x)\n#     y = np.dot(x, ls_mod)+offset\n#     return y\n\n# def _2coordinate(x):\n#     offset = 1000\n#     ls_mod = [1,40,1600,6400]\n#     x = x - offset\n#     res = []\n#     for mod in reversed(ls_mod):\n#         tmp = x // mod\n#         x = x - tmp*mod\n#         res.append(tmp)\n#     res = list(reversed(res))\n#     x_logit, y_logit, z_logit, cmd_logit = res\n#     if    cmd_logit == 0 : main_cmd ='SpecificMoving'\n#     elif  cmd_logit == 1 : main_cmd ='PatrolMoving'  \n#     x = x_arr[x_logit]\n#     y = y_arr[y_logit]\n#     z = z_arr[z_logit]\n#     print(main_cmd, x, y, z)\n#     return main_cmd, x, y, z\n################## ########################## ########################\n################## ########################## ########################\n################## ########################## ########################\n\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/agent.py",
    "content": "import numpy as np\nfrom .actset_lookup import agent_json2local_attrs\n\nclass Agent(object):\n    def __init__(self, team, team_id, uid) -> None:\n        self.team = team\n        self.team_id = team_id\n        self.uid = uid\n        self.attrs = agent_json2local_attrs\n        for attr_json, attr_agent in self.attrs: setattr(self, attr_agent, None)\n        self.pos3d = np.array([np.nan, np.nan, np.nan])\n        self.pos2d = np.array([np.nan, np.nan])\n    \n    def update_agent_attrs(self, dictionary):\n        if (not dictionary['agentAlive']):\n            self.alive = False\n        else:\n            assert dictionary['valid']\n            for attr_json, attr_agent in self.attrs: \n                setattr(self, attr_agent, dictionary[attr_json])\n            assert self.uid == self.uid_remote\n            self.pos3d = np.array(self.location)\n            self.pos2d = self.pos3d[:2]\n            self.vel3d = np.array(self.velocity)\n            self.vel2d = self.pos3d[:2]\n            self.scale3d = np.array(self.scale3)\n            self.scale = self.scale3[0]\n            self.yaw = self.rotation[0]\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/auto_download.py",
    "content": "import os, commentjson, shutil, subprocess, tqdm, shutil, distutils\nfrom onedrivedownloader import download\n\ntry: os.makedirs('./TEMP')\nexcept: pass\n\ndef download_from_shared_server(key = 'cat'):\n    # download uhmap file manifest | 下载manifest目录文件\n    print('download uhmap file manifest | 下载manifest目录文件')\n    manifest_url = \"https://ageasga-my.sharepoint.com/:u:/g/personal/fuqingxu_yiteam_tech/EVmCQMSUWV5MgREWaxiz_GoBalBRV3DWBU3ToSJ5OTQaLQ?e=I8yjl9\"\n\n    try:\n        file = download(manifest_url, filename=\"./TEMP/\", force_download=True)\n    except:\n        print('failed to connect to onedrive | 连接onedrive失败, 您可能需要翻墙才能下载资源')\n\n    with open(\"./TEMP/uhmap_manifest.jsonc\", \"r\") as f:\n        manifest = commentjson.load(f)\n    if key not in key: \n        print('The version you are looking for does not exists!')\n    uhmap_url = manifest[key]\n    print('download main files | 下载预定文件')\n    try:\n        file = download(uhmap_url, filename=\"./TEMP/DOWNLOAD\", unzip=True, unzip_path='./TEMP/UNZIP')\n    except:\n        print(f'download timeout | 下载失败, 您可能需要翻墙才能下载资源。另外如果您想手动下载的话: {uhmap_url}')\n    return file\n\ndef download_client_binary_on_platform(desired_path, desired_version, is_render_client, platform):\n    key = f\"Uhmap_{platform}_Build_Version{desired_version}\"\n    print('downloading', key)\n    download_from_shared_server(key = key)\n    print('download and extract complete, moving files')\n    from distutils import dir_util\n    target_dir = os.path.abspath(os.path.dirname(desired_path) + './..')\n    dir_util.copy_tree('./TEMP/UNZIP', target_dir)\n    assert os.path.exists(desired_path), \"unexpected path error! Are you using Linux style path on Windows?\"\n    return\n\n\ndef download_client_binary(desired_path, desired_version, is_render_client):\n    import platform\n    plat = \"Windows\"\n    if platform.system()==\"Linux\": plat = \"Linux\"\n    download_client_binary_on_platform(desired_path, desired_version, is_render_client, platform=plat)\n    return\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/struct.cpp",
    "content": "#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Containers/UnrealString.h\"\n#include \"XtensorAPIBPLibrary.h\"\n#include \"DataStruct.generated.h\"\n\nUSTRUCT(BlueprintType)\nstruct FAgentProperty\n{\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString ClassName = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool DebugAgent = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator InitRotator;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString WeaponType = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Color = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat DodgeProb = 0.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat ExplodeDmg = 20.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat FireRange = 1000.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat GuardRange = 1400.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat PerceptionRange = 1400.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD2 = \"\";\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FParsedDataInput\n{\n\t// please change lines in \n\t// bool AHMPLevelScriptActor::ParsedTcpInData()\n\t// together with this struct\n\n\tGENERATED_BODY()\n\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString DataCmd;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint NumAgents = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FAgentProperty> AgentSettingArray;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeStep = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeStepMax = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<float> Actions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> StringActions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FAgentDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool AgentAlive = true;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator AgentRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint PreviousAction;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> AvailActions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Reward;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> Interaction;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FKeyObjDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString ClassName;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Location;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator Rotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Scale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Velocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Hp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n};\n\n\n\n\n\n\nUSTRUCT(BlueprintType)\nstruct FGlobalDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat TeamReward = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool UseTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> Events;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> VisibleMatFlatten;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<float> DisMatFlatten;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxEpisodeStep = 999;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeCnt = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Time = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool EpisodeDone = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString EpisodeEndReason = \"unknown\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TeamWin = -1;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FKeyObjDataOutput> KeyObjArr;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString LevelName = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFXTensor DistanceMat;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n\n};\n\nUSTRUCT(BlueprintType)\nstruct FAgentDataOutputArr\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FAgentDataOutput> DataArr;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFGlobalDataOutput DataGlobal;\n};\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/uhmap.md",
    "content": "# Unreal HMAP (UHMAP) 混合多智能体平台-虚幻仿真模块\n\n## UHMAP 中对虚幻源代码的修改\n\n- (1) 将lz4的接口暴露在外，方便使用\n```\nF:\\UnrealSourceCode\\UnrealEngine-4.27.2-release\\Engine\\Source\\Runtime\\Core\\Public\\Compression\\lz4.h\n新增一行\n#define LZ4_DLL_EXPORT 1\n```\n\n- (2) 将AIPerception Sight的计算量拉高\n\n```\nF:\\UnrealSourceCode\\UnrealEngine-4.27.2-release\\Engine\\Source\\Runtime\\AIModule\\Private\\Perception\\AISense_Sight.cpp\n```\n修改参数，这两个参数增大，有助于尽早发现进入范围的智能体（源代码中为了运行效率牺牲了实时性，用运行时间和Trace数量加以约束）\n```\nstatic const int32 DefaultMaxTracesPerTick = 16;\nstatic const int32 DefaultMinQueriesPerTimeSliceCheck = 40;\n```\n\n## Switching MISSION to UHMAP in Json Config 切至虚幻仿真模块\nPlease use following template:\n\n请使用以下配置文件模板:\n```jsonc\n{\n    // config HMP core\n    \"config.py->GlobalConfig\": {\n        \"note\": \"uhmp-dev\",\n        \"env_name\": \"uhmap\", // ***\n        \"env_path\": \"MISSION.uhmap\", // ***\n        \"draw_mode\": \"Img\",\n        \"num_threads\": \"1\",\n        // \"heartbeat_on\": \"False\",\n        \"report_reward_interval\": \"1\",\n        \"test_interval\": \"128\",\n        \"test_epoch\": \"4\",\n        \"device\": \"cuda\",\n        \"max_n_episode\": 500000,\n        \"fold\": \"1\",\n        \"backup_files\": [\n        ]\n    },\n\n    // config MISSION\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {  // ***\n        \"N_AGENT_EACH_TEAM\": [3, 2], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 30,\n        \"n_actions\": 10, \n        \"StateProvided\": false,\n        \"render\": false,\n        \"SubTaskSelection\": \"UhmapBreakingBad\",\n        \"UhmapPort\": 21051,\n        // \"UhmapServerExe\": \"\",\n        \"UhmapRenderExe\": \"./../../WindowsNoEditor/UHMP.exe\",\n        \"UhmapServerExe\": \"./../../WindowsServer/UHMPServer.exe\",\n        \"TimeDilation\": 1.25, // 时间膨胀系数\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.script_ai.dummy_uhmap->DummyAlgorithmT1\",  // *** select ALGORITHMs\n            \"ALGORITHM.script_ai.dummy_uhmap->DummyAlgorithmT2\"  // *** select ALGORITHMs\n        ]\n    },\n\n    // config ALGORITHMs\n    \"ALGORITHM.script_ai.dummy_uhmap.py->DummyAlgConfig\": { \n        \"reserve\": \"\"\n    }\n}\n```\n\n## Configurations 重要配置参数\npath:json配置文件\n\n|  Field   | Value  | Explaination  | zh Explaination  |\n|  ----    | ----   | ----     |  ----  |\n|  device     | ```str```   | select gpu     |  选择GPU或CPU  |\n|  N_AGENT_EACH_TEAM     | ```list of int```   | Agent Num in Each Team     |  各队智能体数量  |\n|  MaxEpisodeStep    | ```int```   |   Time Step Limit   |  对战时间步数限制  |\n|  n_actions    | ```int```   | ----     |  强化学习预留  |\n|  render    | ```bool```   | use render server     |  是否使用渲染  |\n|  UhmapPort    | ```int```   |     |  临时，端口选择，后期将改为自动  |\n|  UhmapPort    | ```int```   |     |  临时，端口选择，后期将改为自动  |\n|  TimeDilation    | ```float```   |     |  时间膨胀，减小实现以实现慢动作，增大可以让CPU燃烧  |\n|  TEAM_NAMES    | ```str```   |     |  分别指定一队、二队策略  |\n\n\n## Unreal Agent Initializing Options 智能体初始化参数\npath:```MISSION\\uhmap\\SubTasks\\UhmapBreakingBad.py```\nfunction:```reset```\n\n|  Field   | Value  | Explaination  | zh Explaination  |\n|  ----    | ----   | ----     |  ----  |\n|  ClassName     | ```str```   | Select Agent Class in unreal engine side     |    |\n|  AgentTeam     | ```int```   | team belonging of an agent      | 智能体的队伍归属   |\n|  IndexInTeam     | ```int```   | team index of an agent      | 智能体在队伍中的编号   |\n|  UID     | ```int```   | index of an agent in the environment     | 智能体在虚幻仿真中的唯一编号   |\n|  MaxMoveSpeed     | ```float```   |      | 暂未接入，无效   |\n|  AgentHp     | ```int```   |      | 初始生命值   |\n|  WeaponCD     | ```float```   |      | 武器cooldown时间，单位秒   |\n|  RSVD1     | ```str```   |      | 智能体展现颜色   |\n|  InitLocation     | ```dict```   |      | 智能体初始位置   |\n\n## Unit conversion 单位转换\n\nLength unit in the system is 1mm,\ne.g. 800 = 800mm = 0.8m\n\n## Algorithm For demonstration\npath:```ALGORITHM\\script_ai\\dummy_uhmap.py```\nfunction:```interact_with_env(override)```\n\n### Argument:\n|  Field   | Value  | Explaination  | zh Explaination  |\n|  ----    | ----   | ----     |  ----  |\n|  ```State_Recall['Latest-Obs']```     |    |  observation array for reinforcement learning     |    |\n|  ```State_Recall['ENV-PAUSE']```     |    |  show which thread is paused (refer to [TimeLine](./../../VISUALIZE/md_imgs/timeline.jpg))     |    |\n|  ```State_Recall['Current-Obs-Step']```     |    |  show time step index in an episode     |    |\n|  ```State_Recall['Latest-Team-Info']```     |    |  interfacing with script-based AIs, including structed agent location, uid, et.al.     |    |\n|  ```State_Recall['Test-Flag']'```     |    |  show whether HMP central has recommanded to do a test run for RL     |    |\n|  ```'State_Recall['Env-Suffered-Reset']''```     |    |  show whether a thread has be reset and start a new episode     |    |\n\n### Convert Command Format:\n\n#### attack a agent with UID\n```python\nencode_action_as_digits(\"SpecificAttacking\", \"N/A\", x=None, y=None, z=None, UID=4, T=None, T_index=None)\n```\n\n#### PatrolMoving with coordinate\n```python\nencode_action_as_digits(\"PatrolMoving\", \"N/A\", x=444*5, y=444*5, z=379, UID=None, T=None, T_index=None)\n```\n\n#### PatrolMoving with direction\n```python\nencode_action_as_digits(\"PatrolMoving\", \"Dir+X+Y\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"PatrolMoving\", \"Dir+X-Y\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"PatrolMoving\", \"Dir+X\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \n```\n\n#### SpecificMoving with coordinate\n```python\nencode_action_as_digits(\"SpecificMoving\", \"N/A\", x=444*5, y=444*5, z=379, UID=None, T=None, T_index=None)\n```\n\n#### SpecificMoving with direction\n```python\nencode_action_as_digits(\"SpecificMoving\", \"Dir+X+Y\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"SpecificMoving\", \"Dir+X-Y\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"SpecificMoving\", \"Dir+X\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \n```\n\n#### Idle and change guard state\n```python\nencode_action_as_digits(\"Idle\", \"DynamicGuard\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"Idle\", \"StaticAlert\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \nencode_action_as_digits(\"Idle\", \"AggressivePersue\", x=None, y=None, z=None, UID=None, T=None, T_index=None) \n```\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/MISSION/uhmap/uhmap_env_wrapper.py",
    "content": "import json, os, subprocess, time, stat, platform, importlib\nimport numpy as np\nfrom UTIL.colorful import print蓝, print靛, print亮红\nfrom UTIL.network import TcpClientP2PWithCompress, find_free_port_no_repeat, get_host_ip\nfrom UTIL.config_args import ChainVar\nfrom config import GlobalConfig\nfrom ..common.base_env import BaseEnv\nfrom .actset_lookup import binary_friendly, dictionary_n_actions\nfrom .agent import Agent\n\n# please register this into MISSION/env_router.py\ndef make_uhmap_env(env_id, rank):\n    if ScenarioConfig.SubTaskSelection == 'UhmapEnv':\n        return UhmapEnv(rank)\n    else:\n        ST = ScenarioConfig.SubTaskSelection\n        assert os.path.exists(f'./MISSION/uhmap/SubTasks/{ST}.py'), \"Unknown subtask!\"\n        ST_CLASS = getattr(importlib.import_module(f'.SubTasks.{ST}', package='MISSION.uhmap'), ST)\n        return ST_CLASS(rank)\n\ndef get_subtask_conf(subtask):\n    ST = subtask\n    assert os.path.exists(f'./MISSION/uhmap/SubTasks/{ST}Conf.py'), \"Configuration not found!\"\n    ST_CONF_CLASS = getattr(importlib.import_module(f'.SubTasks.{ST}Conf', package='MISSION.uhmap'), 'SubTaskConfig')\n    return ST_CONF_CLASS\n\n\n\n\ndef usual_id_arrangment(N_AGENT_EACH_TEAM):\n    \"\"\"\n        e.g., \n        input [5, 3]\n        output [range(0,5), range(5,8)]\n    \"\"\"\n    AGENT_ID_EACH_TEAM = []\n    p = 0\n    for team_agent_num in N_AGENT_EACH_TEAM:\n        AGENT_ID_EACH_TEAM.append(range(p, p + team_agent_num))\n        p += team_agent_num\n    return AGENT_ID_EACH_TEAM\n\n# please register this ScenarioConfig into MISSION/env_router.py\nclass ScenarioConfig(object):  \n    '''\n        ScenarioConfig: This config class will be 'injected' with new settings from JSONC.\n        (E.g., override configs with ```python main.py --cfg example.jsonc```)\n        (As the name indicated, ChainVars will change WITH vars it 'chained_with' during config injection)\n        (please see UTIL.config_args to find out how this advanced trick works out.)\n    '''\n    # <Part 1> Needed by the hmp core #\n    N_AGENT_EACH_TEAM = [10, ]\n    AGENT_ID_EACH_TEAM = usual_id_arrangment(N_AGENT_EACH_TEAM)\n    N_TEAM = len(N_AGENT_EACH_TEAM)\n\n    # chained parameters, will change along with 'N_AGENT_EACH_TEAM'\n    AGENT_ID_EACH_TEAM_cv = ChainVar(lambda N_AGENT_EACH_TEAM: usual_id_arrangment(N_AGENT_EACH_TEAM), chained_with=['N_AGENT_EACH_TEAM'])\n    N_TEAM_cv = ChainVar(lambda N_AGENT_EACH_TEAM: len(N_AGENT_EACH_TEAM), chained_with=['N_AGENT_EACH_TEAM'])\n    \n    # algorithm selection\n    TEAM_NAMES = ['ALGORITHM.None->None',]\n\n    '''\n        ## If the length of action array == the number of teams, set ActAsUnity to True\n        ## If the length of action array == the number of agents, set ActAsUnity to False\n    '''\n    ActAsUnity = False\n\n    '''\n        ## If the length of reward array == the number of agents, set RewardAsUnity to False\n        ## If the length of reward array == 1, set RewardAsUnity to True\n    '''\n    RewardAsUnity = True\n\n    '''\n        ## If the length of obs array == the number of agents, set ObsAsUnity to False\n        ## If the length of obs array == the number of teams, set ObsAsUnity to True\n    '''\n    ObsAsUnity = False\n\n    # <Part 2> Needed by env itself #\n    MaxEpisodeStep = 100\n    render = False\n    TcpAddr = '127.0.0.1'\n    UhmapPort = 21051\n\n    UnrealLevel = 'UhmapBreakingBad'\n    SubTaskSelection = 'UhmapBreakingBad'\n    SubTaskConfig = get_subtask_conf(UnrealLevel)\n    SubTaskConfig_cv = ChainVar(lambda UnrealLevel:get_subtask_conf(UnrealLevel), chained_with=['SubTaskSelection'])\n\n    UElink2editor = False\n    AutoPortOverride = True\n    # AutoPortOverride is usually the reverse of UElink2editor\n    AutoPortOverride_cv = ChainVar(lambda UElink2editor:(not UElink2editor), chained_with=['UElink2editor'])\n\n    # this is not going to be precise,\n    # the precise step time will be floor(StepGameTime/TimeDilation*FrameRate)*TimeDilation/FrameRate\n    StepGameTime = 0.5  \n\n    UhmapServerExe = 'F:/UHMP/Build/WindowsServer/UHMPServer.exe'\n    UhmapRenderExe = ''\n    TimeDilation = 1.0    # engine calcualtion speed control\n    FrameRate = 25.6 # must satisfy: (TimeDilation=1*n, FrameRate=25.6*n)\n    FrameRate_cv = ChainVar(lambda TimeDilation: (TimeDilation/1 * 25.6), chained_with=['TimeDilation'])\n    UhmapStartCmd = []\n    # <Part 3> Needed by some ALGORITHM #\n    StateProvided = False\n    AvailActProvided = False\n    EntityOriented = True\n\n    ActionFormat = 'ASCII' # 'ASCII'/'Multi-Digit'/'Single-Digit'\n\n    n_actions = dictionary_n_actions\n\n    obs_vec_length = get_subtask_conf(UnrealLevel).obs_vec_length\n    obs_vec_length_cv = ChainVar(lambda UnrealLevel:get_subtask_conf(UnrealLevel).obs_vec_length, chained_with=['SubTaskSelection'])\n\n    obs_n_entity = get_subtask_conf(UnrealLevel).obs_n_entity\n    obs_n_entity_cv = ChainVar(lambda UnrealLevel:get_subtask_conf(UnrealLevel).obs_n_entity, chained_with=['SubTaskSelection'])\n\n    # # ObsBreakBase = 1e4\n\n    UhmapVersion = '2.3'\n\n    CanTurnOff = False\n\n    # Hete agents\n    HeteAgents = False\n\n    # 演示demo类别\n    DemoType = \"Default\" \n\n\n\nclass UhmapEnvParseHelper:\n    def parse_response_ob_info(self, response):\n        raise NotImplementedError\n\n    def make_obs(self):\n        raise NotImplementedError\n\n\nclass UhmapEnv(BaseEnv, UhmapEnvParseHelper):\n    def __init__(self, rank) -> None:\n        super().__init__(rank)\n        self.id = rank\n        self.render = ScenarioConfig.render and (self.id==0)\n        self.n_agents = sum(ScenarioConfig.N_AGENT_EACH_TEAM)\n        assert self.n_agents == len(ScenarioConfig.SubTaskConfig.agent_list), 'agent number defination error'\n        self.n_teams = ScenarioConfig.N_TEAM\n        self.sim_thread = None\n        self.client = None\n        # self.observation_space = ?\n        # self.action_space = ?\n        if ScenarioConfig.StateProvided:\n            # self.observation_space['state_shape'] = ?\n            pass\n\n        # Restart env, this is very fast, can be a failsafe if there is memory leaking away on UE side\n        self.max_simulation_life = 2048\n        \n        self.simulation_life = self.max_simulation_life\n        # with a lock, we can initialize UE side one by one (not necessary though)\n\n        # wait until thread 0 finish its initialization (to avoid a traffic jam in server memory)\n        traffic_light = './TEMP/uhmap_thread_0_init_ok_%s'%GlobalConfig.machine_info['ExpUUID'][:8]\n        if rank != 0:\n            while not os.path.exists(traffic_light): time.sleep(1)\n\n        self.activate_simulation(self.id, find_port=True)\n\n        # thread 0 finish its initialization, \n        if rank == 0:\n            with open(traffic_light, mode='w+') as f: f.write(traffic_light)\n\n\n\n    def __del__(self):\n        self.terminate_simulation()\n\n    def activate_simulation(self, rank, find_port=True):\n        print('thread %d initializing'%rank)\n        self.sim_thread = 'activiting'\n        \n        if find_port:\n            self.render = ScenarioConfig.render #  and (rank==0)\n            self.hmp_ue_port = ScenarioConfig.UhmapPort\n            if ScenarioConfig.AutoPortOverride:\n                self.hmp_ue_port, release_port_fn = find_free_port_no_repeat()   # port for hmp data exchanging\n            if not ScenarioConfig.UElink2editor:\n                self.ue_vis_port, release_port_fn = find_free_port_no_repeat()    # port for remote visualizing\n                # self.ue_vis_port = 32222\n                print蓝('Port %d will be used by hmp, port %d will be used by UE internally'%(self.hmp_ue_port, self.ue_vis_port))\n            if (not self.render) and (not ScenarioConfig.UElink2editor):\n                print蓝('To visualize on Windows, run \"./UHMP.exe -OpenLevel=%s:%d -WINDOWED -TimeDilation=%.8f -FrameRate=%.8f -IOInterval=%.8f -DebugMod=False -LockGameDuringCom=True\"'%(\n                    get_host_ip(), self.ue_vis_port, ScenarioConfig.TimeDilation, ScenarioConfig.FrameRate, ScenarioConfig.StepGameTime))\n            self.ip_port = (ScenarioConfig.TcpAddr, self.hmp_ue_port)\n        \n        \n        # os.system()\n        if not ScenarioConfig.UElink2editor:\n            assert ScenarioConfig.AutoPortOverride\n            # * A Butterfly Effect problem *:\n            # UE4 use float (instead of double) for time delta calculation,\n            # causing some error calcualtion dt = 1/FrameRate\n            # which will be enlarged due to Butterfly Effect\n            # therefore we have to make sure that FrameRate = 16,32,64,...\n            print('checking ScenarioConfig args problems ...') \n            assert ScenarioConfig.TimeDilation <= 128, \"* TimeDilation <= 128 *\"\n            assert binary_friendly(1/ScenarioConfig.FrameRate), \"* A Butterfly Effect problem *\"\n            assert binary_friendly(ScenarioConfig.TimeDilation/256), \"* A Butterfly Effect problem *\"\n            # real_step_time = \n            #   np.floor(ScenarioConfig.StepGameTime/ScenarioConfig.TimeDilation*ScenarioConfig.FrameRate) \n            #   * ScenarioConfig.TimeDilation / ScenarioConfig.FrameRate\n            if not self.render:\n                simulation_exe = ScenarioConfig.UhmapServerExe\n                assert 'Server' in simulation_exe\n            else: \n                simulation_exe = ScenarioConfig.UhmapRenderExe\n                assert 'NoEditor' in simulation_exe\n\n            if platform.system()==\"Linux\":\n                if self.render: assert False, \"You really want to render on Linux? If so, remove this line.\"\n                if simulation_exe.endswith('.exe'): \n                    simulation_exe = simulation_exe.replace('/Windows', '/Linux')\n                    simulation_exe = simulation_exe.replace('.exe','.sh')\n                # expand '~' path\n                simulation_exe = os.path.expanduser(simulation_exe)\n            else:   # Windows\n                if simulation_exe.endswith('.sh'): \n                    simulation_exe = simulation_exe.replace('/Linux', '/Windows')\n                    simulation_exe = simulation_exe.replace('.sh', '.exe')\n                if simulation_exe.startswith('/home'): \n                    simulation_exe = './TEMP' + simulation_exe\n\n            if not os.path.exists(simulation_exe):\n                if self.rank == 0:\n                    from .auto_download import download_client_binary\n                    download_client_binary(desired_path=simulation_exe, desired_version=ScenarioConfig.UhmapVersion, is_render_client=self.render)\n                else:\n                    while True:\n                        time.sleep(60)\n                        if os.path.exists(simulation_exe): break\n\n            # give execution permission\n            if platform.system()==\"Linux\":\n                st = os.stat(simulation_exe)\n                os.chmod(simulation_exe, st.st_mode | stat.S_IEXEC)\n\n            if (not self.render) and simulation_exe != '':\n                # start child process\n                self.sim_thread = subprocess.Popen([\n                    simulation_exe,\n                    # '-log', \n                    '-TcpPort=%d'%self.hmp_ue_port,   # port for hmp data exchanging\n                    '-Port=%d'%self.ue_vis_port,   # port for remote visualizing\n                    '-OpenLevel=%s'%ScenarioConfig.UnrealLevel, \n                    '-TimeDilation=%.8f'%ScenarioConfig.TimeDilation, \n                    '-FrameRate=%.8f'%ScenarioConfig.FrameRate,\n                    '-IOInterval=%.8f'%ScenarioConfig.StepGameTime,\n                    '-Seed=%d'%int(np.random.rand()*1e5), # 如果已经设定了主线程随机数种子，这里随机出来的数字则是确定的\n                    '-DebugMod=False',\n                    # '-LLMCSV',\n                    '-ABSLOG=%s'%os.path.abspath('./TEMP/uhmap/%s/%d.log'%(GlobalConfig.machine_info['ExpUUID'][:8], rank)),\n                    '-Version=%s'%ScenarioConfig.UhmapVersion,\n                    '-LockGameDuringCom=True',\n                ], stdout=subprocess.DEVNULL)\n                print('UHMAP (Headless) started ...')\n            elif self.render and simulation_exe != '':\n                self.sim_thread = subprocess.Popen([\n                    simulation_exe,\n                    # '-log', \n                    '-TcpPort=%d'%self.hmp_ue_port,   # port for hmp data exchanging\n                    '-Port=%d'%self.ue_vis_port,   # port for remote visualizing\n                    '-OpenLevel=%s'%ScenarioConfig.UnrealLevel, \n                    '-TimeDilation=%.8f'%ScenarioConfig.TimeDilation, \n                    '-FrameRate=%.8f'%ScenarioConfig.FrameRate,\n                    '-IOInterval=%.8f'%ScenarioConfig.StepGameTime,\n                    '-Seed=%d'%int(np.random.rand()*1e5), # 如果已经设定了主线程随机数种子，这里随机出来的数字则是确定的\n                    '-DebugMod=False',\n                    # '-LLMCSV',\n                    '-ABSLOG=%s'%os.path.abspath('./TEMP/uhmap/%s/%d.log'%(GlobalConfig.machine_info['ExpUUID'][:8], rank)),\n                    '-Version=%s'%ScenarioConfig.UhmapVersion,\n                    '-LockGameDuringCom=True',\n                    \"-ResX=1280\",\n                    \"-ResY=720\",\n                    \"-WINDOWED\"\n                ], stdout=subprocess.DEVNULL)\n                print('UHMAP (Render) started ...')\n            else:\n                print('Cannot start Headless Server Or GUI Server!')\n                assert False, 'Cannot start Headless Server Or GUI Server!'\n        else:\n            print('Trying to link to unreal editor ...')\n            assert not ScenarioConfig.AutoPortOverride\n\n        time.sleep(1+np.abs(self.id)/100)\n        self.client = TcpClientP2PWithCompress(self.ip_port)\n        MAX_RETRY = 150\n        for i in range(MAX_RETRY):\n            try: \n                self.client.manual_connect()\n                print('handshake complete %d'%rank)\n                break\n            except: \n                if i>25:\n                    print('Thread %d: Trying to connect to unreal engine. Related library not in memory, going to take some minutes. Retry %d ...'%(rank, i))\n                elif i>75:\n                    print('Thread %d: Waiting too long, please reduce parallel threads (num_threads), Retry %d ... | 请减小num_threads运行一次, 让动态库载入内存, 然后恢复num_threads即可'%(rank, i))\n                elif i >= MAX_RETRY-1:\n                    assert False, ('uhmap connection timeout, please reduce parallel threads (num_threads) !')\n                time.sleep(1)\n        # now that port is bind, no need to hold them anymore\n        if find_port:\n            if ScenarioConfig.AutoPortOverride: \n                release_port_fn(self.hmp_ue_port)\n            if not ScenarioConfig.UElink2editor:\n                release_port_fn(self.ue_vis_port)\n        self.t = 0\n        print('thread %d initialize complete'%rank)\n\n\n    def terminate_simulation(self):\n        if hasattr(self,'sim_thread') and (self.sim_thread is not None) and (self.client is not None):\n            # self.sim_thread.terminate()\n            # send terminate command to unreal side\n            self.client.send_dgram_to_target(json.dumps({\n                'valid': True,\n                'DataCmd': 'end_unreal_engine',\n                'TimeStepMax': ScenarioConfig.MaxEpisodeStep,\n                'TimeStep' : 0,\n                'Actions': None,\n            }))\n            self.client.close()\n            self.sim_thread = None\n            self.client = None\n\n\n    # override reset function\n    def reset(self):\n        self.simulation_life -= 1\n        if self.simulation_life < 0:\n            print('restarting simutation')\n            self.terminate_simulation()\n            self.simulation_life = self.max_simulation_life\n            self.activate_simulation(self.id, find_port=False)\n\n    def sleep(self):\n        self.simulation_life = -1\n        self.terminate_simulation()\n\n    # override step function\n    def step(self, act):\n        raise NotImplementedError\n        # return (ob, RewardForAllTeams,  done, info)  # choose this if RewardAsUnity\n        \n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/README.md",
    "content": "# HMP：Hybrid Multi-agent Playground\n\nSee https://github.com/binary-husky/hmp2g\n\n\n\n# Run demo in Editor mode\n```\n(Open map UhmapLargeScale in Unreal Editor)\n\ncd PythonExample/hmp_minimal_modules\npython main.py -c ZHECKPOINT/uhmap_hete10vs10/render_result_editor.jsonc\n```\n\n# Run tutorial of designing custom actions in Editor mode\n```\n(Open map UhmapWaterdrop or UhmapLargeScale in Unreal Editor)\n\ncd PythonExample/hmp_minimal_modules\npython main.py -c ZDOCS/examples/uhmap/random_waterdrop.jsonc\n```\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/__init__.py",
    "content": ""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/auto_gpu.py",
    "content": "\"\"\"\nCreated on Tue Aug 22 19:41:55 2017\n@author: Quantum Liu\n\"\"\"\n'''\nExample:\ngm=GPUManager()\nwith torch.cuda.device(gm.auto_choice()):\n    blabla\nOr:\ngm=GPUManager()\ntorch.cuda.set_device(gm.auto_choice())\n'''\n\nimport os, time\nfrom UTIL.colorful import print黄\n\nclass sel_gpu():\n    '''\n    qargs:\n        query arguments\n    A manager which can list all available GPU devices\n    and sort them and choice the most free one.Unspecified \n    ones pref.\n    GPU设备管理器, 考虑列举出所有可用GPU设备, 并加以排序, 自动选出\n    最空闲的设备。在一个GPUManager对象内会记录每个GPU是否已被指定,\n    优先选择未指定的GPU。\n    '''\n    def __init__(self,qargs=[]):\n        '''\n        '''\n        \n        self.qargs=qargs\n\n\n    def _sort_by_memory(self,gpus,by_size=False):\n        for gpu in gpus:    \n            # 优先使用A100显卡\n            if 'A100' in gpu['gpu_name']:\n                gpu['memory.free'] *= 1.25\n                gpu['memory.total'] *= 1.25\n\n        if by_size:\n            print黄('Sorted by free memory size')\n            res = sorted(gpus,key=lambda d:d['memory.free'],reverse=True)\n\n            return res\n        else:\n            print黄('Sorted by free memory rate')\n            return sorted(gpus,key=lambda d:float(d['memory.free'])/ d['memory.total'],reverse=True)\n\n\n    def _sort_by_power(self,gpus):\n        return sorted(gpus,key='by_power')\n    \n    def _sort_by_custom(self,gpus,key,reverse=False,qargs=[]):\n        if isinstance(key,str) and (key in qargs):\n            return sorted(gpus,key=lambda d:d[key],reverse=reverse)\n        if isinstance(key,type(lambda a:a)):\n            return sorted(gpus,key=key,reverse=reverse)\n        raise ValueError(\"The argument 'key' must be a function or a key in query args,please read the documention of nvidia-smi\")\n\n    def auto_choice(self,mode=0):\n        '''\n        mode:\n            0:(default)sorted by free memory size\n        return:\n            a TF device object\n        Auto choice the freest GPU device,not specified\n        ones \n        自动选择最空闲GPU,返回索引\n        '''\n        from UTIL.colorful import print黄\n        self.gpus=self.query_gpu(self.qargs)\n        for gpu in self.gpus:\n            gpu['specified']=False\n        self.gpu_num=len(self.gpus)\n        # if not self.check_gpus():\n        #     raise ImportError('GPU available check failed')\n\n        for old_infos,new_infos in zip(self.gpus,self.query_gpu(self.qargs)):\n            old_infos.update(new_infos)\n        unspecified_gpus=[gpu for gpu in self.gpus if not gpu['specified']] or self.gpus\n        \n        if mode==0:\n            chosen_gpu=self._sort_by_memory(unspecified_gpus,True)[0]\n            print黄('Choosing the GPU device has largest free memory...\\n')\n        elif mode==1:\n            chosen_gpu=self._sort_by_power(unspecified_gpus)[0]\n            print黄('Choosing the GPU device has highest free memory rate...\\n')\n        elif mode==2:\n            chosen_gpu=self._sort_by_power(unspecified_gpus)[0]\n            print黄('Choosing the GPU device by power...\\n')\n        else:\n            chosen_gpu=self._sort_by_memory(unspecified_gpus)[0]\n            print黄('Given an unaviliable mode,will be chosen by memory\\n')\n        chosen_gpu['specified']=True\n        index=chosen_gpu['index']\n        print黄('Using GPU {i}:\\n{info}'.format(i=index,info='\\n'.join([str(k)+':'+str(v) for k,v in chosen_gpu.items()])))\n        return int(index)\n\n\n    @staticmethod\n    def check_gpus():\n        '''\n        GPU available check\n        http://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-cuda/\n        '''\n        import torch\n        if not torch.cuda.is_available():\n            print黄('This script could only be used to manage NVIDIA GPUs,but no GPU found in your device')\n            return False\n\n\n        with os.popen('nvidia-smi -h') as f:\n            if not 'NVIDIA System Management' in f.read():\n                print黄(\"'nvidia-smi' tool not found.\")\n                f.close()\n                return False\n            f.close()\n        return True\n\n    @staticmethod\n    def parse(line,qargs):\n        '''\n        line:\n            a line of text\n        qargs:\n            query arguments\n        return:\n            a dict of gpu infos\n        Pasing a line of csv format text returned by nvidia-smi\n        解析一行nvidia-smi返回的csv格式文本\n        '''\n        numberic_args = ['memory.free', 'memory.total', 'power.draw', 'power.limit']    #可计数的参数\n        power_manage_enable=lambda v:(('Not Support' not in v) and ('[N/A]' not in v))  #lambda表达式，显卡是否支持power management（笔记本可能不支持）\n        to_numberic=lambda v:float(v.upper().strip().replace('MIB','').replace('W','')) #带单位字符串去掉单位\n        process = lambda k,v:((int(to_numberic(v)) if power_manage_enable(v) else 1) if k in numberic_args else v.strip())\n        return {k:process(k,v) for k,v in zip(qargs,line.strip().split(','))}\n\n    def query_gpu(self, qargs=[]):\n        '''\n        qargs:\n            query arguments\n        return:\n            a list of dict\n        Querying GPUs infos\n        查询GPU信息\n        '''\n        qargs =['index','gpu_name', 'memory.free', 'memory.total', 'power.draw', 'power.limit']+ qargs\n        cmd = 'nvidia-smi --query-gpu={} --format=csv,noheader'.format(','.join(qargs))\n        results = os.popen(cmd).readlines()\n        return [self.parse(line,qargs) for line in results]\n\n\n    @staticmethod\n    def by_power(d):\n        '''\n        helper function fo sorting gpus by power\n        '''\n        power_infos=(d['power.draw'],d['power.limit'])\n        if any(v==1 for v in power_infos):\n            print黄('Power management unable for GPU {}'.format(d['index']))\n            return 1\n        return float(d['power.draw'])/d['power.limit']\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/batch_exp.py",
    "content": "import subprocess\nimport threading\nimport copy, os\nimport time\nimport json\nfrom UTIL.network import get_host_ip\nfrom UTIL.colorful import *\ndef get_info(script_path):\n    info = {\n        'HostIP': get_host_ip(),\n        'RunPath': os.getcwd(),\n        'ScriptPath': os.path.abspath(script_path),\n        'StartDateTime': time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\n    }\n    try:\n        info['DockerContainerHash'] = subprocess.getoutput(r'cat /proc/self/cgroup | grep -o -e \"docker/.*\"| head -n 1 |sed \"s/docker\\\\/\\\\(.*\\\\)/\\\\1/\" |cut -c1-12')\n    except: \n        info['DockerContainerHash'] = 'None'\n    return info\n\ndef run_batch_exp(sum_note, n_run, n_run_mode, base_conf, conf_override, script_path):\n    arg_base = ['python', 'main.py']\n    time_mark_only = time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.localtime())\n    time_mark = time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.localtime()) + '-' + sum_note\n    log_dir = '%s/'%time_mark\n    exp_log_dir = log_dir+'exp_log'\n    if not os.path.exists('PROFILE/%s'%exp_log_dir):\n        os.makedirs('PROFILE/%s'%exp_log_dir)\n    exp_json_dir = log_dir+'exp_json'\n    if not os.path.exists('PROFILE/%s'%exp_json_dir):\n        os.makedirs('PROFILE/%s'%exp_json_dir)\n\n    conf_list = []\n    new_json_paths = []\n    for i in range(n_run):\n        conf = copy.deepcopy(base_conf)\n        new_json_path = 'PROFILE/%s/run-%d.json'%(exp_json_dir, i+1)\n        for key in conf_override:\n            assert n_run == len(conf_override[key]), ('检查！n_run是否对应')\n            tree_path, item = key.split('-->')\n            conf[tree_path][item] = conf_override[key][i]\n        with open(new_json_path,'w') as f:\n            json.dump(conf, f, indent=4)\n        # print(conf)\n        conf_list.append(conf)\n        new_json_paths.append(new_json_path)\n\n    print红('\\n')\n    print红('\\n')\n    print红('\\n')\n\n    printX = [\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n    ]\n    \n    conf_base_ = conf_list[0]\n    for k_ in conf_base_:\n        conf_base = conf_base_[k_]\n        for key in conf_base:\n            different = False\n            for i in range(len(conf_list)):\n                if conf_base[key]!=conf_list[i][k_][key]:\n                    different = True\n                    break\n            # \n            if different:\n                for i in range(len(conf_list)):\n                    printX[i](key, conf_list[i][k_][key])\n            else:\n                print(key, conf_base[key])\n\n\n\n    final_arg_list = []\n\n    for ith_run in range(n_run):\n        final_arg = copy.deepcopy(arg_base)\n        final_arg.append('--cfg')\n        final_arg.append(new_json_paths[ith_run])\n        final_arg_list.append(final_arg)\n        print('')\n\n\n    def local_worker(ith_run):\n        log_path = open('PROFILE/%s/run-%d.log'%(exp_log_dir, ith_run+1), 'w+')\n        printX[ith_run%len(printX)](final_arg_list[ith_run])\n        subprocess.run(final_arg_list[ith_run], stdout=log_path, stderr=log_path)\n\n    def remote_worker(ith_run):\n        # step 1: transfer all files\n        from UTIL.exp_helper import get_ssh_sftp\n        \n        addr = n_run_mode[ith_run]['addr']\n        if 'exe_here' in addr: \n            _, addr = addr.split('=>')\n            usr = n_run_mode[ith_run]['usr']\n            pwd = n_run_mode[ith_run]['pwd']\n            ssh, sftp = get_ssh_sftp(addr, usr, pwd)\n            src_path = os.getcwd()\n        else:\n            # assert False\n            usr = n_run_mode[ith_run]['usr']\n            pwd = n_run_mode[ith_run]['pwd']\n            ssh, sftp = get_ssh_sftp(addr, usr, pwd)\n            sftp.mkdir('/home/%s/MultiServerMission'%(usr), ignore_existing=True)\n            sftp.mkdir('/home/%s/MultiServerMission/%s'%(usr, time_mark), ignore_existing=True)\n            src_path = '/home/%s/MultiServerMission/%s/src'%(usr, time_mark)\n            try:\n                sftp.mkdir(src_path, ignore_existing=False)\n                sftp.put_dir('./', src_path, ignore_list=['__pycache__','TEMP','ZHECKPOINT'])\n                sftp.close()\n                print紫('upload complete')\n            except:\n                sftp.close()\n                print紫('do not need upload')\n\n        print('byobu attach -t %s'%time_mark_only)\n        addr_ip, addr_port = addr.split(':')\n        print亮蓝(\"Attach cmd: ssh %s@%s -p %s -t \\\"byobu attach -t %s\\\"\"%(usr, addr_ip, addr_port, time_mark_only))\n        \n        stdin, stdout, stderr = ssh.exec_command(command='byobu new-session -d -s %s'%time_mark_only, timeout=1)\n        print亮紫('byobu new-session -d -s %s'%time_mark_only)\n        time.sleep(1)\n\n        byobu_win_name = '%s--run-%d'%(time_mark_only, ith_run)\n        byobu_win_name = byobu_win_name\n        stdin, stdout, stderr = ssh.exec_command(command='byobu new-window -t %s'%time_mark_only, timeout=1)\n        print亮紫('byobu new-window -t %s'%time_mark_only)\n        time.sleep(1)\n\n        cmd = 'cd  ' + src_path\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n        \n        cmd = ' '.join(['echo',  str(get_info(script_path)) ,'>>', './private_remote_execution.log'])\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n\n        cmd = ' '.join(final_arg_list[ith_run])\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n\n\n\n        print亮蓝(\"command send is done!\")\n        time.sleep(2)\n\n        # 杀死\n        # stdin, stdout, stderr = ssh.exec_command(command='byobu kill-session -t %s'%byobu_win_name, timeout=1)\n        pass\n\n    def worker(ith_run):\n        if n_run_mode[ith_run] is None: \n            local_worker(ith_run)\n        else:\n            remote_worker(ith_run)\n\n\n\n    def clean_process(pid):\n        import psutil\n        parent = psutil.Process(pid)\n        for child in parent.children(recursive=True):\n            try:\n                print亮红('sending Terminate signal to', child)\n                child.terminate()\n                time.sleep(5)\n                print亮红('sending Kill signal to', child)\n                child.kill()\n            except: pass\n        parent.kill()\n\n    def clean_up():\n        print亮红('clean up!')\n        parent_pid = os.getpid()   # my example\n        clean_process(parent_pid)\n\n    \n            \n    input('Confirm execution? 确认执行?')\n    input('Confirm execution! 确认执行!')\n\n    t = 0\n    while (t >= 0):\n        print('Counting down ', t)\n        time.sleep(1)\n        t -= 1\n\n    DELAY = 60\n    for ith_run in range(n_run):\n        worker(ith_run)\n        for i in range(DELAY):\n            time.sleep(1)\n\n    print('all submitted')"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/colorful.py",
    "content": "import platform\nfrom sys import stdout\n\nif platform.system()==\"Linux\":\n    pass\nelse: \n    from colorama import init\n    init()\n\n# Do you like the elegance of Chinese characters?\ndef print红(*kw,**kargs):\n    print(\"\\033[0;31m\",*kw,\"\\033[0m\",**kargs)\ndef print绿(*kw,**kargs):\n    print(\"\\033[0;32m\",*kw,\"\\033[0m\",**kargs)\ndef print黄(*kw,**kargs):\n    print(\"\\033[0;33m\",*kw,\"\\033[0m\",**kargs)\ndef print蓝(*kw,**kargs):\n    print(\"\\033[0;34m\",*kw,\"\\033[0m\",**kargs)\ndef print紫(*kw,**kargs):\n    print(\"\\033[0;35m\",*kw,\"\\033[0m\",**kargs)\ndef print靛(*kw,**kargs):\n    print(\"\\033[0;36m\",*kw,\"\\033[0m\",**kargs)\n\ndef print亮红(*kw,**kargs):\n    print(\"\\033[1;31m\",*kw,\"\\033[0m\",**kargs)\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\ndef print亮黄(*kw,**kargs):\n    print(\"\\033[1;33m\",*kw,\"\\033[0m\",**kargs)\ndef print亮蓝(*kw,**kargs):\n    print(\"\\033[1;34m\",*kw,\"\\033[0m\",**kargs)\ndef print亮紫(*kw,**kargs):\n    print(\"\\033[1;35m\",*kw,\"\\033[0m\",**kargs)\ndef print亮靛(*kw,**kargs):\n    print(\"\\033[1;36m\",*kw,\"\\033[0m\",**kargs)\n\n\n\ndef print亮红(*kw,**kargs):\n    print(\"\\033[1;31m\",*kw,\"\\033[0m\",**kargs)\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\ndef print亮黄(*kw,**kargs):\n    print(\"\\033[1;33m\",*kw,\"\\033[0m\",**kargs)\ndef print亮蓝(*kw,**kargs):\n    print(\"\\033[1;34m\",*kw,\"\\033[0m\",**kargs)\ndef print亮紫(*kw,**kargs):\n    print(\"\\033[1;35m\",*kw,\"\\033[0m\",**kargs)\ndef print亮靛(*kw,**kargs):\n    print(\"\\033[1;36m\",*kw,\"\\033[0m\",**kargs)\n    \nprint_red = print红\nprint_green = print绿\nprint_yellow = print黄\nprint_blue = print蓝\nprint_purple = print紫\nprint_indigo = print靛\n\nprint_bold_red = print亮红\nprint_bold_green = print亮绿\nprint_bold_yellow = print亮黄\nprint_bold_blue = print亮蓝\nprint_bold_purple = print亮紫\nprint_bold_indigo = print亮靛\n    \nif not stdout.isatty():\n    # redirection, avoid a fucked up log file\n    print红 = print\n    print绿 = print\n    print黄 = print\n    print蓝 = print\n    print紫 = print\n    print靛 = print\n    print亮红 = print\n    print亮绿 = print\n    print亮黄 = print\n    print亮蓝 = print\n    print亮紫 = print\n    print亮靛 = print\n    print_red = print\n    print_green = print\n    print_yellow = print\n    print_blue = print\n    print_purple = print\n    print_indigo = print\n    print_bold_red = print\n    print_bold_green = print\n    print_bold_yellow = print\n    print_bold_blue = print\n    print_bold_purple = print\n    print_bold_indigo = print"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/config_args.py",
    "content": "import argparse, os, time, func_timeout\nfrom ast import Global\nfrom shutil import copyfile, copytree, ignore_patterns, rmtree\nfrom .colorful import *\nfrom .data_struct import remove_prefix, remove_suffix\n\n'''\n    This a chained var class, it deal with hyper-parameters that are bound together, \n    e.g. number of threads and test episode interval.\n    ChainVars are handled in utils.config_args.py\n'''\nclass ChainVar(object):\n    def __init__(self, chain_func, chained_with):\n        self.chain_func = chain_func\n        self.chained_with = chained_with\n\n# ChainVar relationship must end with '_cv' or '_CV'\ndef is_chained_key(key):\n    if key.endswith('_cv'):\n        return True, remove_suffix(key, '_cv')\n    elif key.endswith('_CV'):\n        return True, remove_suffix(key, '_CV')\n    else:\n        return False, key\n\n\n\n'''\n    Load all parameters in place\n'''\ndef prepare_args(vb=True):\n    if vb: prepare_tmp_folder()\n    parser = argparse.ArgumentParser(description='HMP')\n    parser.add_argument('-c', '--cfg', help='Path of the configuration file')\n    parser.add_argument('-s', '--skip', action='store_true', help='skip logdir check')\n    args, unknown = parser.parse_known_args()\n    load_via_json = (hasattr(args, 'cfg') and args.cfg is not None)\n    assert load_via_json\n    skip_logdir_check = (hasattr(args, 'skip') and (args.skip is not None) and args.skip) or (not vb)\n\n    if len(unknown) > 0 and vb: print亮红('Warning! In json setting mode, %s is ignored'%str(unknown))\n    # load configuration from file\n    import commentjson as json\n    if vb: print亮绿('reading configuration at', args.cfg)\n    # inject configuration into place\n    with open(args.cfg, encoding='utf8') as f: json_data = json.load(f)\n    # check and process tmp alg folder\n    if vb: prepare_alg_tmp_folder(json_data)\n    # inject configuration into place\n    load_config_via_json(json_data, vb)\n    # read the new global configuration\n    from config import GlobalConfig as cfg\n    # check log path conflict, change note name if required\n    note_name_overide = None\n    if not skip_logdir_check: \n        note_name_overide = check_experiment_log_path(cfg.logdir)\n        if note_name_overide is not None: \n            override_config_file('config.py->GlobalConfig', {'note':note_name_overide}, vb)\n    # create log path\n    if not os.path.exists(cfg.logdir): os.makedirs(cfg.logdir)\n    # back up essiential files\n    if (not cfg.recall_previous_session) and vb: \n        copyfile(args.cfg, '%s/experiment.jsonc'%cfg.logdir)\n        if not os.path.exists('%s/raw_exp.jsonc'%cfg.logdir):\n            copyfile(args.cfg, '%s/raw_exp.jsonc'%cfg.logdir)\n        backup_files(cfg.backup_files, cfg.logdir, args.cfg)\n        cfg.machine_info = register_machine_info(cfg.logdir)\n    # light up the ready flag\n    cfg.cfg_ready = True\n    # finish\n    return cfg\n\ndef load_config_via_json(json_data, vb):\n    for cfg_group in json_data:\n        if cfg_group == 'config.py->GlobalConfig': random_seed_warning(json_data[cfg_group])\n        dependency = override_config_file(cfg_group, json_data[cfg_group], vb)\n        if dependency is not None:\n            for dep in dependency:\n                assert any([dep in k for k in json_data.keys()]), 'Arg check failure, There is something missing!'\n    check_config_relevence(json_data)\n    return None\n\n\ndef override_config_file(cfg_group, new_cfg, vb):\n    import importlib\n    assert '->' in cfg_group\n    str_pro = '------------- %s -------------'%cfg_group\n    if vb:  print绿(str_pro)\n    file_, class_ = cfg_group.split('->')\n    if '.py' in file_: \n        # replace it with removesuffix('.py') if you have python>=3.9\n        if file_.endswith('.py'): file_ = file_[:-3]    \n    default_configs = getattr(importlib.import_module(file_), class_)\n    for key in new_cfg:\n        if new_cfg[key] is None: continue\n        my_setattr(conf_class=default_configs, key=key, new_value=new_cfg[key], vb=vb)\n    altered_cv = secure_chained_vars(default_configs, new_cfg, vb)\n    if vb:\n        print绿(''.join(['-']*len(str_pro)),)\n        arg_summary(default_configs, new_cfg, altered_cv)\n        print绿(''.join(['-']*len(str_pro)),'\\n\\n\\n')\n    if 'TEAM_NAMES' in new_cfg:\n        return [item.split('->')[0] for item in new_cfg['TEAM_NAMES'] if not item.startswith('TEMP')]\n    return None\n\ndef secure_chained_vars(default_cfg, new_cfg, vb):\n    default_cfg_dict = default_cfg.__dict__\n    altered_cv = []\n    for key in default_cfg_dict:\n        is_chain, o_key = is_chained_key(key)\n        if not is_chain: continue\n        if o_key in new_cfg: continue\n        assert hasattr(default_cfg, o_key), ('twin var does not have original')\n        # get twin\n        chain_var = getattr(default_cfg, key)   \n        need_reflesh = False\n        for chain_by_var in chain_var.chained_with:\n            if chain_by_var in new_cfg: need_reflesh = True\n        if not need_reflesh: continue\n        replace_item = chain_var.chain_func(*[getattr(default_cfg, v) for v in chain_var.chained_with])\n        original_item = getattr(default_cfg, o_key)\n        if vb: print靛('[config] warning, %s is chained by %s, automatic modifying:'%(o_key,\n                            str(chain_var.chained_with)), original_item, '-->', replace_item)\n        setattr(default_cfg, o_key, replace_item)\n        altered_cv.append(o_key)\n    return altered_cv\n\n\"\"\"\n    make sure that env selection Matches env configuration\n\"\"\"\ndef check_config_relevence(json_data):\n    env_name = json_data['config.py->GlobalConfig']['env_name']\n    env_path = json_data['config.py->GlobalConfig']['env_path']\n    for key in json_data.keys():\n        if 'MISSION' in key: assert env_path in key, ('configering wrong env!')\n\n\"\"\"\n    Warn user if the random seed is not given\n\"\"\"\ndef random_seed_warning(json_data):\n    if 'seed' not in json_data:\n        from config import GlobalConfig as cfg\n        print亮红('Random seed not given, using %d'%cfg.seed)\n        time.sleep(5)\n\ndef prepare_tmp_folder():\n    def init_dir(dir):\n        if not os.path.exists(dir):  os.makedirs(dir)\n    local_temp_folder = './TEMP'\n    global_temp_folder = os.path.expanduser('~/HmapTemp')\n    init_dir(local_temp_folder)\n    init_dir(global_temp_folder+'/GpuLock')\n    init_dir(global_temp_folder+'/PortFinder')\n\ndef prepare_alg_tmp_folder(json_data):\n    try:\n        # scan mission conf\n        mission_key = [k for k in json_data.keys() if k.startswith('MISSION')][0]\n        # obtain algorithm assignment\n        TEAM_NAMES = json_data[mission_key]['TEAM_NAMES']\n        for tname in TEAM_NAMES:\n            if not tname.startswith('TEMP'): continue\n            # obtain the path of algorithm to be mirrored\n            path = tname.split('->')[0].replace('.','/')\n            # trace path parent to algorithm folder.\n            trace_success = False\n            max_depth = 5\n            for _ in range(max_depth):\n                parent = os.path.relpath(path+'/..')\n                if os.path.basename(parent) == 'ALGORITHM': \n                    src_path = os.path.relpath(path, start=os.path.relpath(parent+'/..'))\n                    trace_success = True\n                    break\n                path = parent\n            # transmit temp algorithm\n            if trace_success:\n                import glob\n                from stat import S_IREAD, S_IRGRP, S_IROTH, S_IWRITE\n                def readonly_handler(func, path, execinfo):\n                    try:\n                        os.chmod(path, S_IWRITE)\n                        func(path)\n                    except:\n                        pass\n                    return\n                rmtree(path, onerror=readonly_handler)\n                # src_path = remove_prefix(path, 'TEMP/')\n                print亮绿(f'[config] Copying mirror algorithm from {src_path} to {path}')\n                copytree(src_path, path)\n                # make these temp files read only\n                for f in glob.glob(path+'/**/*.py', recursive=True): os.chmod(f, S_IREAD|S_IRGRP|S_IROTH)\n    except:\n        print亮红('[config] Errors occurs when executing prepare_alg_tmp_folder')\n        time.sleep(5)\n        return\n\ndef register_machine_info(logdir):\n    import socket, json, subprocess, uuid\n    from .network import get_host_ip\n    info = {\n        'HostIP': get_host_ip(),\n        'ExpUUID': uuid.uuid1().hex,\n        'RunPath': os.getcwd(),\n        'StartDateTime': time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\n    }\n    try:\n        info['DockerContainerHash'] = subprocess.getoutput(r'cat /proc/self/cgroup | grep -o -e \"docker/.*\"| head -n 1 |sed \"s/docker\\\\/\\\\(.*\\\\)/\\\\1/\" |cut -c1-12')\n    except: \n        info['DockerContainerHash'] = 'None'\n    with open('%s/info.json'%logdir, 'w+') as f:\n        json.dump(info, f, indent=4)\n    return info\n\ndef backup_files(files, logdir, jsonfile):\n    from config import GlobalConfig as cfg\n    if cfg.remote_server_ops != \"\":\n        remote_server_ops = cfg.remote_server_ops.replace(\"LOCALFILE\", jsonfile).replace(\n            \"REMOTEFILE\", \n            time.strftime(\"%Y_%m_%d_%H_%M_%S__\", time.localtime())+ cfg.note  + '__' + os.path.basename(jsonfile))\n        os.popen(remote_server_ops)\n    \n    for file in files:\n        if os.path.isfile(file):\n            print绿('[config] Backup File:',file)\n            bkdir = '%s/backup_files/'%logdir\n            if not os.path.exists(bkdir): os.makedirs(bkdir)\n            copyfile(file, '%s/%s'%(bkdir, os.path.basename(file)))\n        else:\n            print亮绿('[config] Backup Folder:',file)\n            assert os.path.isdir(file), ('cannot find', file)\n            copytree(file, '%s/backup_files/%s'%(logdir, os.path.basename(file)), \n                dirs_exist_ok=True, ignore=ignore_patterns(\"__pycache__\"))\n    return \n\ndef check_experiment_log_path(logdir):\n    res = None\n    if os.path.exists(logdir):\n        if os.path.exists(logdir+'test_stage'): return None\n        print亮红('Current log path:', logdir)\n        print亮红('Warning! you will overwrite old logs if continue!')\n        print亮红(\"Pause for 60 seconds before continue (or press Enter to confirm!)\")\n        try:\n            res = askChoice()\n            if res == '': res = None\n        except func_timeout.exceptions.FunctionTimedOut as e:\n            res = None\n    return res\n\n@func_timeout.func_set_timeout(60)\ndef askChoice():\n    return input('>>')\n\ndef arg_summary(config_class, modify_dict = {}, altered_cv = []):\n    for key in config_class.__dict__: \n        if '__' in key: continue\n        is_chain, _ = is_chained_key(key)\n        if is_chain: continue\n        if (not key in modify_dict) or (modify_dict[key] is None): \n            if key not in altered_cv: \n                print绿(key.center(25), '-->', str(getattr(config_class,key)))\n            else: \n                print靛(key.center(25), '-->', str(getattr(config_class,key)))\n        else: \n            print红(key.center(25), '-->', str(getattr(config_class,key)))\n\ndef my_setattr(conf_class, key, new_value, vb):\n    assert hasattr(conf_class, key), (conf_class, 'has no such config item: **%s**'%key)\n    setting_name = key\n    replace_item = new_value\n    original_item = getattr(conf_class, setting_name)\n    if vb: print绿('[config] override %s:'%setting_name, original_item, '-->', replace_item)\n    if isinstance(original_item, float):\n        replace_item = float(replace_item)\n    elif isinstance(original_item, bool):\n        if replace_item == 'True':\n            replace_item = True\n        elif replace_item == 'False':\n            replace_item = False\n        elif isinstance(replace_item, bool):\n            replace_item = replace_item\n        else:\n            assert False, ('enter True or False, but have:', replace_item)\n    elif isinstance(original_item, int):\n        assert int(replace_item) == float(replace_item), (\"warning, this var **%s** has an int default, but given a float override!\"%key)\n        replace_item = int(replace_item)\n    elif isinstance(original_item, str):\n        replace_item = replace_item\n    elif isinstance(original_item, list):\n        assert isinstance(replace_item, list)\n    elif isinstance(original_item, dict):\n        assert isinstance(replace_item, dict)\n    else:\n        assert False, ('not support this type')\n    setattr(conf_class, setting_name, replace_item)\n    return\n\ndef find_all_conf():\n    import glob\n    py_script_list = glob.glob('./**/*.py', recursive=True)\n    conf_class_gather = []\n    for python_file in py_script_list:\n        with open(python_file,encoding='UTF-8') as f:\n            lines = f.readlines()\n        for line in lines:\n            if 'ADD_TO_CONF_SYSTEM' not in line: continue\n            if 'class ' not in line: continue\n            conf_class_gather.append({'line':line, 'file':python_file})\n    def getBetween(str, str1, str2):\n        strOutput = str[str.find(str1)+len(str1):str.find(str2)]\n        return strOutput\n    for target in conf_class_gather:\n        class_name = getBetween(target['line'], 'class ', '(')\n        target['class_name'] = class_name\n        target['file'] = target['file'].replace('/', '.').replace('..', '')\n        import importlib\n        target['class'] = getattr(importlib.import_module(target['file'].replace('.py', '')), class_name)\n    return conf_class_gather\n\ndef make_json(conf_list):\n    import json\n    out = {}\n    for conf in conf_list:\n        local_conf = {}\n        config_class = conf['class']\n        for key in config_class.__dict__: \n            if '__' in key: continue\n            is_chain, _ = is_chained_key(key)\n            if is_chain: continue\n            item_to_be_serialize = getattr(config_class, key)\n            try:\n                json.dumps(item_to_be_serialize)\n            except:\n                item_to_be_serialize = '[cannot be json]' + str(item_to_be_serialize)\n            local_conf[key] = item_to_be_serialize\n        out[conf['file']] = local_conf\n    # json_str = json.dumps(out)\n    with open('all_conf.json', 'w') as f:\n        json.dump(out, f, indent=4)\n        print亮紫('the conf summary is successfully saved to all_conf.json')\n\nif __name__ == '__main__':\n    conf_list = find_all_conf()\n    res_json = make_json(conf_list)"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/data_struct.py",
    "content": "\n\nclass UniqueList():\n    def __init__(self, list_input=None):\n        self._list = []\n        if list_input is not None:\n            self.extend_unique(list_input)\n    \n    def append_unique(self, item):\n        if item in self._list:\n            return False\n        else:\n            self._list.append(item)\n    \n    def extend_unique(self, list_input):\n        for item in list_input:\n            self.append_unique(item)\n    \n    def has(self, item):\n        return (item in self._list)\n    \n    def len(self):\n        return len(self._list)\n    \n    def __len__(self):\n        return len(self._list)\n\n    def get(self):\n        return self._list\n\n    def __iter__(self):\n        return self._list.__iter__()\n\n# # https://stackoverflow.com/questions/16891340/remove-a-prefix-from-a-string\n# def remove_prefix(text, prefix):\n#     return text[text.startswith(prefix) and len(prefix):]\n\n# https://stackoverflow.com/questions/3663450/remove-substring-only-at-the-end-of-string\ndef remove_suffix(s, sub):\n    return s[:-len(sub)] if s.endswith(sub) else s\n\ndef remove_prefix(s, sub):\n    return s[len(sub):] if s.startswith(sub) else s"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/exp_helper.py",
    "content": "import paramiko, os, time\nfrom UTIL.colorful import print亮紫, print亮靛\nfrom UTIL.tensor_ops import __hash__\n\ndef singleton(cls):\n    _instance = {}\n\n    def inner(*args, **kwargs):\n        if cls not in _instance:\n            _instance[cls] = cls(*args, **kwargs)\n        return _instance[cls]\n    \n    return inner\n\nclass ChainVar(object):\n    def __init__(self, chain_func, chained_with):\n        self.chain_func = chain_func\n        self.chained_with = chained_with\n\nclass DataCentralServer(object): # ADD_TO_CONF_SYSTEM //DO NOT remove this comment//\n    addr = 'None'\n    usr = 'None'\n    pwd = 'None'\n\n\n@singleton\nclass changed():\n    def __init__(self):\n        self._storage = {}\n    \n    def check(self, value, key):\n        if key in self._storage:\n            new_hash = __hash__(value)\n            if self._storage[key] == new_hash:\n                return False\n            else:\n                self._storage[key] = new_hash\n                return True\n        else:\n            self._storage[key] = __hash__(value)\n            return True\n\n\nfrom stat import S_ISDIR\n\n# great thank to skoll for sharing this at stackoverflow:\n# https://stackoverflow.com/questions/4409502/directory-transfers-with-paramiko\nclass MySFTPClient(paramiko.SFTPClient):\n    def put_dir(self, source, target, ignore_list=[]):\n        ''' Uploads the contents of the source directory to the target path. The\n            target directory needs to exists. All subdirectories in source are \n            created under target.\n        '''\n        for item in os.listdir(source):\n            if item in ignore_list: continue\n            if os.path.isfile(os.path.join(source, item)):\n                # print亮靛('uploading: %s --> %s'%(os.path.join(source, item),'%s/%s' % (target, item)))\n                self.put(os.path.join(source, item), '%s/%s' % (target, item))\n            else:\n                self.mkdir('%s/%s' % (target, item), ignore_existing=True)\n                self.put_dir(os.path.join(source, item), '%s/%s' % (target, item), ignore_list)\n    \n    def isfile(self, path):\n        try:\n            return not S_ISDIR(self.stat(path).st_mode)\n        except IOError:\n            #Path does not exist, so by definition not a directory\n            return True\n\n    def get_dir(self, source, target, ignore_list=[]):\n        ''' Download the contents of the source directory to the target path. The\n            target directory needs to exists. All subdirectories in source are \n            created under target.\n        '''\n        for item in self.listdir(source):\n            if item in ignore_list: continue\n            if self.isfile(os.path.join(source, item).replace('\\\\','/')):\n                # print亮靛('uploading: %s --> %s'%(os.path.join(source, item),'%s/%s' % (target, item)))\n                self.get(os.path.join(source, item).replace('\\\\','/'), '%s/%s' % (target, item))\n            else:\n                if os.path.exists('%s/%s' % (target, item)):\n                    print('local dir already exists:', '%s/%s' % (target, item))\n                    continue\n                os.mkdir('%s/%s' % (target, item))\n                self.get_dir(os.path.join(source, item).replace('\\\\','/'), '%s/%s' % (target, item), ignore_list)\n\n    def mkdir(self, path, mode=511, ignore_existing=False):\n        ''' Augments mkdir by adding an option to not fail if the folder exists  '''\n        try:\n            super(MySFTPClient, self).mkdir(path, mode)\n        except IOError as e:\n            if e.__class__ == FileNotFoundError:\n                raise\n\n            if ignore_existing:\n                pass\n            else:\n                raise\n\n\ndef get_ssh_sftp(addr, usr, pwd):\n    ssh = paramiko.SSHClient() \n    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())\n    ssh.load_host_keys(os.path.expanduser(os.path.join(\"~\", \".ssh\", \"known_hosts\")))\n    port = 22\n    if ':' in addr: addr, port = addr.split(':')\n    ssh.connect(addr, username=usr, password=pwd, port=port)\n    sftp = MySFTPClient.from_transport(ssh.get_transport())\n    return ssh, sftp\n\n\ndef upload_exp(cfg): # shell it to catch error\n    try: upload_exp_(cfg)\n    except: pass\n\ndef upload_exp_(cfg):\n    path = cfg.logdir\n    name = cfg.note\n    try:\n        addr = DataCentralServer.addr     # ssh ubuntu address\n        usr = DataCentralServer.usr      # ubuntu user\n        pwd = DataCentralServer.pwd      # ubuntu password\n        assert addr != 'None' and (addr is not None)\n        assert usr != 'None' and (usr is not None)\n        assert pwd != 'None' and (pwd is not None)\n    except:\n        print('No experiment data central server is configured, 没有配置中央日志服务器')\n        return\n    remote_path = '/home/%s/CenterHmp/'%usr\n    ssh = paramiko.SSHClient() \n    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())\n    ssh.load_host_keys(os.path.expanduser(os.path.join(\"~\", \".ssh\", \"known_hosts\")))\n    ssh.connect(addr, username=usr, password=pwd)\n    put_str = '[%s] [%s] %s'%(cfg.note, time.strftime(\"%Y-%m-%d-%H:%M:%S\", time.localtime()), str(cfg.machine_info).replace('\\'',''))\n    ssh.exec_command(command='echo -e \"%s\" >> %s/active.log'%(put_str, remote_path), timeout=1)\n    sftp = MySFTPClient.from_transport(ssh.get_transport())\n    print亮紫('uploading results: %s --> %s'%(path, '%s/%s'%(remote_path, name)))\n    sftp.mkdir(remote_path, ignore_existing=True)\n    sftp.mkdir('%s/%s'%(remote_path, name), ignore_existing=True)\n    sftp.put_dir(path, '%s/%s'%(remote_path, name))\n    sftp.close()\n    print亮紫('upload complete')\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/fetch_multiserver.py",
    "content": "from UTIL.exp_helper import get_ssh_sftp\nfrom UTIL.colorful import *\nimport time,os\n\n'''\nFetch experiment results from worker servers\n'''\n\nn_run_mode = [\n    # {   # @1\n    #     \"addr\": \"172.18.116.149:2233\",\n    #     \"usr\": \"hmp\",\n    #     \"pwd\": \"hmp\"\n    # },\n    # {   # @2\n    #     \"addr\": \"172.18.116.150:2233\",\n    #     \"usr\": \"fuqingxu\",\n    #     \"pwd\": \"clara\"\n    # },\n    {   # @3\n        \"addr\": \"172.18.116.149:2233\",\n        \"usr\": \"fuqingxu\",\n        \"pwd\": \"clara\"\n    }\n\n]\ndownload_dir = './fetch/'\nafter_date = '2022-03-22-17-22-00'\nconsider_days = None\n\ninfo_list = {}\nto_download = {}\nfor ith_run in range(len(n_run_mode)):\n    addr = n_run_mode[ith_run]['addr']\n    usr = n_run_mode[ith_run]['usr']\n    pwd = n_run_mode[ith_run]['pwd']\n    ssh, sftp = get_ssh_sftp(addr, usr, pwd)\n    experiments_path = sftp.listdir(path='./MultiServerMission/')\n    # 将顺序改为从最早到最晚\n    experiments_path = reversed(sorted(experiments_path))\n    for index, exp_time in enumerate(experiments_path):\n        time_then = time.mktime(time.strptime(exp_time,\"%Y-%m-%d-%H:%M:%S\"))\n        time_now = time.mktime(time.localtime())\n        diff_time_days = (time_now - time_then)/3600/24\n        if consider_days is None:\n            consider_days = (time_now - time.mktime(time.strptime(after_date,\"%Y-%m-%d-%H-%M-%S\")))/3600/24\n        if diff_time_days > consider_days: continue\n        path_ckpt = './MultiServerMission/%s/src/ZHECKPOINT/'%exp_time\n        try:\n            list_of_sub_exp = sftp.listdir(path=path_ckpt)\n        except:\n            print('No ZHECKPOINT directory found!')\n            continue\n        key = str(ith_run)+'-'+str(index)\n        print亮绿(key,':',exp_time)\n        for sep in list_of_sub_exp:\n            print亮紫('\\t- ',sep)\n        info_list[key] = {'ith_run':ith_run, 'index':index, 'path':path_ckpt}\n        target_path = (download_dir+'/%s/'%exp_time.replace(':','-'))\n        try:\n            os.mkdir(target_path)\n            sftp.get_dir(source=path_ckpt,target=target_path)   # 下载！\n        except BaseException as e:\n            print('This directory already exists, skipping:', target_path)\n\nprint('download complete')\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/file_lock.py",
    "content": "# pip install filelock\nfrom filelock import FileLock as FileLockBase\n\nclass FileLock(FileLockBase):\n    def __init__(self, lock_file, timeout: float = -1) -> None:\n        assert lock_file.endswith('.lock')\n        super().__init__(lock_file, timeout)\n\n\ndef is_file_empty(file_path):\n    with open(file_path, 'r') as f: \n        file_content = f.read()\n    if file_content == '' or file_content == '\\n': \n        return True\n    else:\n        return False\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/gpu_eater.py",
    "content": "def validate_path():\n    import os, sys\n    dir_name = os.path.dirname(__file__)\n    root_dir_assume = os.path.abspath(os.path.dirname(__file__) +  '/..')\n    os.chdir(root_dir_assume)\n    sys.path.append(root_dir_assume)\n\nif __name__ == '__main__':\n    validate_path()\n    \nfrom multiprocessing import Process\nfrom UTIL.network import UnixTcpServerMultiClient\nimport os, time, re, torch\nimport threading\n\ndef check_devices_mem():\n    devices_info = os.popen(\n        '\"/usr/bin/nvidia-smi\"' + \n        ' --query-gpu=memory.total,memory.used' +\n        ' --format=csv,nounits,noheader'\n    ).read().strip().split(\"\\n\")\n    divices_mem_info = [x.split(',') for x in devices_info]\n    divices = os.environ.get(\"CUDA_VISIBLE_DEVICES\")\n    if divices is None:\n        return divices_mem_info\n    else:\n        device_list = []\n        for i in [int(x) for x in divices.split(',')]:\n            device_list.append(divices_mem_info[i])\n        return device_list\n\n\ndef occupy_device_mem(cuda_device, mem_info, free=1024):\n    total, used = int(mem_info[0]), int(mem_info[1])\n    block_mem = total - used - free\n    if block_mem > 0:\n        print('Occupy device_{}\\'s mem ...'.format(cuda_device))\n        x = torch.zeros(\n            (256, 1024, block_mem),\n            dtype=torch.float32,\n            device='cuda:{}'.format(cuda_device)\n        )\n        del x\n        print('Occupy device_{}\\'s mem finished'.format(cuda_device))\n    else:\n        print('Device_{}\\'s out of memory'.format(cuda_device))\n\n\ndef occupy_gpus_mem(free=4096):\n    for i, mem_info in enumerate(check_devices_mem()):\n        occupy_device_mem(i, mem_info, free)\n    print('Occupy all device\\'s mem finished')\n\n\nclass GPU_Eater(Process):\n    def __init__(self, unix_path, party):\n        super(GPU_Eater, self).__init__()\n        self.unix_path = unix_path\n        self.server = None\n        self.party = party\n        match_res = re.match(pattern=r'cuda(.)_party(.)', string=party)\n        cudax, self.party_index = match_res[1], match_res[2]\n        assert self.party_index == '0'\n        self.device = f'cuda:{cudax}'\n        cudax_int = int(cudax)\n        os.environ[\"CUDA_DEVICE_ORDER\"]=\"PCI_BUS_ID\"\n        os.environ[\"CUDA_VISIBLE_DEVICES\"] = cudax\n        self.previous_req = time.time()\n\n    def __del__(self):\n        if self.server is not None:\n            self.server.close()\n        self.terminate()\n        \n    def run_timer(self):\n        while True:\n            time.sleep(60)\n            delta_time = time.time() - self.previous_req\n            print(f'inactive for {delta_time} seconds')\n            if delta_time > 3600:\n                self.__del__()\n                break\n\n    def release_gpu(self):\n        torch.cuda.empty_cache()\n        pass\n\n    def hold_gpu(self):\n        occupy_gpus_mem(free=2048)\n        pass\n\n    def on_receive_data(self, data):\n        print('data incoming')\n        if data == 'link':\n            self.previous_req = time.time()\n            reply = 'success'\n        elif data == 'need_gpu':\n            self.release_gpu()\n            self.previous_req = time.time()\n            reply = 'ok'\n        elif data == 'giveup_gpu':\n            self.hold_gpu()\n            self.previous_req = time.time()\n            reply = 'ok'\n        elif data == 'offline':\n            self.previous_req = time.time()\n            reply = 'ok'\n        else:\n            assert False\n        print(data)\n        return reply\n\n    def run(self):\n        print('started')\n        try: os.unlink(self.unix_path)\n        except: pass\n        t = threading.Thread(target=self.run_timer)\n        t.daemon = True\n        t.start()\n        self.server = UnixTcpServerMultiClient(self.unix_path, obj='str')\n        self.server.on_receive_data = lambda data: self.on_receive_data(data)\n        self.server.be_online()\n\n\nif __name__ == '__main__':\n    import argparse\n    parser = argparse.ArgumentParser(description='gpu_party')\n    parser.add_argument('--party', type=str)\n    args = parser.parse_args()\n    party = args.party\n    unix_path = os.path.expanduser(f'~/HmapTemp/GpuLock/GpuEater_{party}')\n    o = GPU_Eater(unix_path, party)\n    o.run()"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/gpu_share.py",
    "content": "import platform, os, torch, uuid, time, psutil, json, random\nfrom UTIL.network import UnixTcpClientP2P, UnixTcpServerP2P\nfrom atexit import register\nfrom .file_lock import FileLock\ndef pid_exist(pid_str):\n    pid = int(pid_str)\n    return psutil.pid_exists(pid)\n\ndef read_json(fp):\n    # create if not exist\n    if not os.path.exists(fp):\n        with open(fp, \"w\") as f:\n            pass\n    # try to read, otherwise reset\n    try:\n        with open(fp, \"r+\") as f: \n            json_data = json.load(f)\n    except:\n        json_data = {}\n    return json_data\n\ndef write_json(fp, buf):\n    with open(fp, \"w\") as f:\n        json.dump(buf, fp=f)\n    return\n\ndef create_eater(unix_path):\n    from .gpu_eater import GPU_Eater\n    proc = GPU_Eater(unix_path)\n    proc.daemon = True\n    proc.start()\n\n\n\nclass GpuHolder():\n    def __init__(self, device) -> None:\n        # try to communicate with gpu holder\n        unix_path = os.path.expanduser(f'~/HmapTemp/GpuLock/GpuEater_{device}')\n        try:\n            self.client = UnixTcpClientP2P(unix_path, obj='str')\n            success = self.client.send_and_wait_reply('link')\n            print('already have a GpuHolder online')\n        except:\n            assert False\n            print('creating GpuHolder')\n            create_eater(unix_path)\n            time.sleep(3)\n            print('creating Finished')\n            self.client = UnixTcpClientP2P(unix_path, obj='str')\n            success = self.client.send_and_wait_reply('link')\n        assert success == 'success'\n\n    def __del__(self):\n        if self.client is not None:\n            self.client.send_and_wait_reply('offline')\n            self.client.__del__()\n\n    def need_gpu(self):\n        ok = self.client.send_and_wait_reply('need_gpu')\n        assert ok == 'ok'\n    \n    def giveup_gpu(self):\n        ok = self.client.send_and_wait_reply('giveup_gpu')\n        assert ok == 'ok'\n\n\nclass GpuShareUnit():\n    flesh = True\n    def __init__(self, which_gpu, lock_path=None, manual_gpu_ctl=True, gpu_party='', gpu_ensure_safe=False):\n        self.device = which_gpu\n        self.manual_gpu_ctl = True\n        self.lock_path=lock_path\n        self.gpu_party = gpu_party\n        self.gpu_lock = None\n        self.ensure_gpu_safe = gpu_ensure_safe\n        self.pid_str = str(os.getpid())\n        self.n_gpu_process_online = 1\n        if self.ensure_gpu_safe:\n            assert 'party0' in self.gpu_party; assert 'cuda' in self.gpu_party\n            self.gpu_eater = GpuHolder(device=self.gpu_party)\n        if gpu_party == 'off':\n            self.manual_gpu_ctl = False\n        # the default file lock path\n        if self.lock_path is None: \n            self.lock_path = os.path.expanduser('~/HmapTemp/GpuLock')\n        # create a folder if the path is invalid\n        if not os.path.exists(self.lock_path): \n            os.makedirs(self.lock_path)\n        # gpu party register file\n        self.register_file = self.lock_path+'/lock_gpu_%s_%s.json'%(self.device, self.gpu_party)\n        register(self.__del__)\n        \n    def __del__(self):\n        if hasattr(self,'_deleted_'): \n            # avoid exit twice\n            return\n        else: \n            self._deleted_ = True     # avoid exit twice\n\n        try:\n            with FileLock(self.register_file+'.lock'):\n                self.unregister_pid()\n        except:\n            pass\n\n        try: self.gpu_lock.__exit__(None,None,None)\n        except:pass\n\n    def __enter__(self):\n        self.get_gpu_lock()\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.release_gpu_lock()\n\n    def get_gpu_lock(self):\n        if self.manual_gpu_ctl:\n            print('Waiting for GPU %s %s...'%(self.device, self.gpu_party), end='', flush=True)\n            with FileLock(self.register_file+'.lock'):\n                self.n_gpu_process_online = self.register_pid()\n            fp = self.lock_path+'/gpu_lock_%s_%s'%(self.device, self.gpu_party)\n            self.gpu_lock = FileLock(fp+'.lock')\n            self.gpu_lock.__enter__()\n            if self.ensure_gpu_safe: self.gpu_eater.need_gpu()\n            print('Get GPU, currently shared with %d process!'%self.n_gpu_process_online)\n        return\n\n    def release_gpu_lock(self):\n        if self.manual_gpu_ctl:\n            # if self.n_gpu_process_online > 1: \n            torch.cuda.empty_cache()\n            if self.ensure_gpu_safe: self.gpu_eater.giveup_gpu()\n            self.gpu_lock.__exit__(None,None,None)\n            # else:\n                # print('GPU not shared')\n        return\n    \n    def register_pid(self):\n        all_pids = read_json(self.register_file)\n        need_write = False\n\n        # check all pid alive occasionally\n        if GpuShareUnit.flesh or random.random() < 0.05:\n            for pid in list(all_pids.keys()):\n                if not pid_exist(pid):\n                    all_pids.pop(pid); print('removing dead item', pid)\n                    need_write = True\n            GpuShareUnit.flesh = False\n\n        # add entry if not exist\n        if self.pid_str not in all_pids:\n            all_pids[self.pid_str] = {}\n            need_write = True\n\n        # write back if needed\n        if need_write: write_json(self.register_file, all_pids)\n\n        return len(all_pids)\n\n    def unregister_pid(self):\n        all_pids = read_json(self.register_file)\n\n        # check all pid alive\n        for pid in list(all_pids.keys()):\n            if not pid_exist(pid):\n                all_pids.pop(pid); print('removing dead item', pid)\n\n        try:\n            all_pids.pop(self.pid_str)\n        except:\n            pass\n\n        # write back if needed\n        write_json(self.register_file, all_pids)\n\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/hidden_print.py",
    "content": "import sys, os\nclass HiddenPrints:\n    def __enter__(self):\n        self._original_stdout = sys.stdout\n        sys.stdout = open(os.devnull, 'w')\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        sys.stdout.close()\n        sys.stdout = self._original_stdout"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/hmp_daemon.py",
    "content": "import time, requests, threading, os, atexit, psutil\nfrom UTIL.colorful import *\n\ndef kill_process(p):\n    try:\n        # print('正在发送terminate命令到进程:', os.getpid(), '-->', p.pid)\n        p.terminate()\n        _, alive = psutil.wait_procs([p,], timeout=0.01)    # 先等 10ms\n        if len(alive):\n            _, alive = psutil.wait_procs(alive, timeout=0.10)  # 再等 100ms\n            if len(alive):\n                # print('\\t (R1) 很遗憾, 进程不服从terminate信号, 正在发送kill-9命令到进程:', os.getpid(), '-->', p.pid)\n                for p in alive: p.kill()\n            else:\n                # print('\\t (R2) 进程成功结束')\n                pass\n        else:\n            # print('\\t (R2) 进程成功结束')\n            pass\n    except Exception as e:\n        print(e) \n\ndef kill_process_and_its_children(p):\n    p = psutil.Process(p.pid)   # p might be Python's process, convert to psutil's process\n    if len(p.children())>0:\n        # print('有子进程')\n        for child in p.children():\n            if hasattr(child,'children') and len(child.children())>0:\n                kill_process_and_its_children(child)\n            else:\n                kill_process(child)\n    else:\n        pass\n        # print('无子进程')\n    kill_process(p)\n\n\ndef kill_process_children(p):\n    p = psutil.Process(p.pid)   # p might be Python's process, convert to psutil's process\n    if len(p.children())>0:\n        # print('有子进程')\n        for child in p.children():\n            if hasattr(child,'children') and len(child.children())>0:\n                kill_process_and_its_children(child)\n            else:\n                kill_process(child)\n    else:\n        pass\n        # print('无子进程')\n\ndef clean_child_process(pid):\n    parent = psutil.Process(pid)\n    kill_process_children(parent)\n\n\ndef hmp_clean_up():\n    from UTIL.exp_helper import upload_exp\n    from config import GlobalConfig as cfg\n    print亮黄('[main.py] upload results to storage server via SSH')\n    if cfg.allow_res_upload: upload_exp(cfg)\n    print亮黄('[main.py] kill all children process, then self-terminate.')\n\n    clean_child_process(os.getpid())\n\ndef start_periodic_daemon(cfg):\n    print('[hmp_daemon.py] Disable periodic daemon to debug.')\n    return \n    periodic_thread = threading.Thread(target=periodic_daemon,args=(cfg,))\n\n    periodic_thread.setDaemon(True)\n\n    periodic_thread.start()\n    for i in range(100):\n        time.sleep(1)\n        print(i)\n    atexit.register(hmp_clean_up)\n\ndef periodic_daemon(cfg):\n    while True:\n        try: \n            print('start periodic_daemon_(cfg)')\n            periodic_daemon_(cfg)\n            print('end periodic_daemon_(cfg)')\n        except AssertionError: \n            hmp_clean_up()\n        except BaseException: \n            print('hmp server failed')\n            break\n        time.sleep(15*60)\n\ndef periodic_daemon_(cfg):\n    report = {\n        'type': 'hmp-client',\n        'note': cfg.note,\n        'time': time.strftime(\"%Y-%m-%d-%H:%M:%S\", time.localtime()),\n        'client_status': 'Running',\n        'StartingTime': cfg.machine_info['StartDateTime'],\n        'HostIP': cfg.machine_info['HostIP'],\n        'ExpUUID': cfg.machine_info['ExpUUID'],\n        'RunPath':cfg.machine_info['RunPath'],\n        'DockerContainerHash':cfg.machine_info['DockerContainerHash']\n    }\n    res = requests.post('http://linux.ipv4.fuqingxu.top:11511/',data = report)\n\n    if res.text=='Stop_Now': \n        report['client_status'] = 'Terminate'\n        requests.post('http://linux.ipv4.fuqingxu.top:11511/',data = report)\n        raise AssertionError('HMP-Center Has Given Terminate Signal!')"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/legacy/gpu_share_unfin.py",
    "content": "import flock, os, torch, uuid, time, glob\nfrom atexit import register\n\nclass GpuShareUnit():\n    def __init__(self, which_gpu, lock_path=None, manual_gpu_ctl=True, gpu_party=''):\n        self.device = which_gpu\n        self.manual_gpu_ctl = True\n        self.lock_path=lock_path\n        self.gpu_party = gpu_party\n        self.experiment_uuid = uuid.uuid1().hex + '\\n'\n        self.n_gpu_process_online = 1\n        self.flag_req_all_party = False\n        self.parties_req = None #   如果单个party的显存不够用，那么需要同时排队多个party，获取内存\n        if gpu_party == 'off' or gpu_party == 'OFF' or gpu_party<0:\n            self.manual_gpu_ctl = False\n        if self.lock_path is None: \n            self.lock_path = os.path.expanduser('~/GpuLock')\n        if not os.path.exists(self.lock_path): os.makedirs(self.lock_path)\n        register(self.unregister_uuids_)\n        \n\n    def __exit__(self, exc_type, exc_value, traceback):\n        if not self.flag_req_all_party:\n            self.release_gpu_lock()\n\n    def __enter__(self):\n        self._get_gpu_locks()\n        return self\n\n    def __del__(self):\n        self.unregister_uuids_()\n\n    def _get_gpu_locks(self):\n        if not self.flag_req_all_party:\n            self.parties_req = None\n            self.__get_gpu_lock(self.device, self.gpu_party)\n        else:\n            self.parties_req = self.__find_all_active_party(self.device)\n            if not (self.gpu_party in self.parties_req): self.parties_req.append(self.gpu_party)\n            for each_party in self.parties_req: self.__get_gpu_lock(self.device, each_party)\n\n    def __find_all_active_party(self, device):\n        list_of_active_parties = []\n        for indx in range(64):\n            res = self.___get_party_n_share(device, gpu_party=str(indx))\n            if res is None:\n                break\n            if res == 0:\n                break\n            if res >0:\n                list_of_active_parties.append(str(indx))\n        return list_of_active_parties\n\n\n    def __get_gpu_lock(self, device, gpu_party):\n        if self.manual_gpu_ctl:\n            print('Waiting for GPU %s %s...'%(device, gpu_party), end='', flush=True)\n            gpu_lock, gpu_lock_file = (None, None)\n            self.n_gpu_process_online = self.register_uuid_(device, gpu_party)\n            self.gpu_lock_file = open(self.lock_path+'/lock_gpu_%s_%s.glock'%(device, gpu_party), 'w+')\n            self.gpu_lock = flock.Flock(self.gpu_lock_file, flock.LOCK_EX)\n            self.gpu_lock.__enter__()\n            print('Get GPU, currently shared with %d process!'%self.n_gpu_process_online)\n        return\n\n    def release_gpu_lock(self):\n        self.flag_req_all_party = False\n        if self.manual_gpu_ctl:\n            if self.n_gpu_process_online >1: \n                torch.cuda.empty_cache()\n                self.gpu_lock.__exit__(None,None,None)\n                self.gpu_lock_file.close()\n            else:\n                print('不共享GPU')\n        return\n    \n    def ___get_party_n_share(self, device, gpu_party):\n        try:\n            flag = 'r'\n            with open(self.lock_path+'/lock_gpu_%s_%s.register'%(device, gpu_party), mode=flag) as gpu_register_file:\n                _lock = flock.Flock(gpu_register_file, flock.LOCK_EX); _lock.__enter__()\n                lines = gpu_register_file.readlines()\n                _lock.__exit__(None,None,None)\n                return len(lines)\n        except: \n            return None\n\n    def register_uuid_(self, device, gpu_party):\n        try:\n            flag = 'w+' if not os.path.exists(self.lock_path+'/lock_gpu_%s_%s.register'%(device, gpu_party)) else 'r+'\n            with open(self.lock_path+'/lock_gpu_%s_%s.register'%(device, gpu_party), mode=flag) as gpu_register_file:\n                _lock = flock.Flock(gpu_register_file, flock.LOCK_EX); _lock.__enter__()\n                lines = gpu_register_file.readlines()\n                if not any([line==self.experiment_uuid for line in lines]):\n                    lines.append(self.experiment_uuid)\n                    gpu_register_file.seek(0); gpu_register_file.truncate(0)\n                    gpu_register_file.writelines(lines)\n                    gpu_register_file.flush()\n                _lock.__exit__(None,None,None)\n                return len(lines)\n        except:\n            print('GPU 队列异常!')\n            return 999\n\n    def unregister_uuids_(self, device, gpu_party):\n        for __\n            self.unregister_uuid_(device, gpu_party)\n        try: self.gpu_lock.__exit__(None,None,None)\n        except:pass\n        try: self.gpu_lock_file.close()\n        except:pass\n\n\n    def unregister_uuid__(self, device, gpu_party):\n        flag = 'w+' if not os.path.exists(self.lock_path+'/lock_gpu_%s_%s.register'%(device, gpu_party)) else 'r+'\n        with open(self.lock_path+'/lock_gpu_%s_%s.register'%(device, gpu_party), mode=flag) as gpu_register_file:\n            _lock = flock.Flock(gpu_register_file, flock.LOCK_EX); _lock.__enter__()\n            lines = gpu_register_file.readlines()\n            gpu_register_file.seek(0); gpu_register_file.truncate(0)\n            gpu_register_file.writelines([line for line in lines if line!=self.experiment_uuid])\n            gpu_register_file.flush()\n            _lock.__exit__(None,None,None)\n            print('unregister')\n\n    def req_all_party(self):\n        self.flag_req_all_party = True"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/mem_watcher_ue.py",
    "content": "def validate_path():\n    import os, sys\n    # '/home/hmp/xx/hmp2g-heterogeneous-phase2/UTIL'\n    dir_name = os.path.dirname(__file__)\n    # '/home/hmp/xx/hmp2g-heterogeneous-phase2'\n    root_dir_assume = os.path.abspath(os.path.dirname(__file__) +  '/..')\n    # change working dir\n    os.chdir(root_dir_assume)\n    # import root\n    sys.path.append(root_dir_assume)\n    \nvalidate_path()\nimport time, requests, threading, os, atexit, psutil\nfrom UTIL.colorful import *\n\ndef thread_dfs(p, depth=0, info=None):\n    try:\n        if isinstance(p, int):\n            p = psutil.Process(p)\n        elif isinstance(p, psutil.Process):\n            pass\n        else:\n            p = psutil.Process(p.pid)\n        \n        pp = p\n        print_info(depth, pp, info)\n        if len(p.children())>0:\n            # print('有子进程')\n            for child in p.children():\n                if hasattr(child,'children') and len(child.children())>0:\n                    thread_dfs(child, depth = depth+1, info=info)\n                else:\n                    pp = child\n                    print_info(depth+1, pp, info)\n        else:\n            pass\n    except:\n        return\n\ndef print_info(depth, pp, info=None):\n    pid = pp.pid\n    name = pp.name()\n    name_trim = 'HmapShmPoolWorker'  if name.startswith('HmapShmPoolWorker') else name\n    mem = (psutil.Process(pid).memory_info().rss / 1024 / 1024 / 1024)\n    info['tot_mem'] += mem\n    info['tot_procs'] += 1\n    if name_trim not in info:\n        info[name_trim] = {\n            'mem':0,\n            'procs':0,\n        }\n    info[name_trim]['mem'] += mem\n    info[name_trim]['procs'] += 1\n\n\ndef find_procs_by_name(name):\n    \"Return a list of processes matching 'name'.\"\n    ls = []\n    for p in psutil.process_iter([\"name\", \"exe\", \"cmdline\"]):\n        if name == p.info['name'] or \\\n                p.info['exe'] and os.path.basename(p.info['exe']) == name or \\\n                p.info['cmdline'] and p.info['cmdline'][0] == name:\n            ls.append(p)\n    return ls[0]\n\nif __name__ ==  \"__main__\":\n    from VISUALIZE.mcom import mcom\n    mcv = mcom( \n            path='TEMP',\n            digit=-1, \n            rapid_flush=True, draw_mode='Img'\n    )\n\n\ndef main(root_name = 'UE4Editor.exe'):\n    pid = find_procs_by_name(root_name)\n    mem = (pid.memory_info().rss / 1024 / 1024 / 1024)\n    mcv.rec(mem, 'mem')\n    mcv.rec_show()\n\nif __name__ ==  \"__main__\":\n    while True:\n        main()\n        time.sleep(10) # 十分钟一次\n        # time.sleep(300) # 十分钟一次"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/memleak_finder.py",
    "content": "from pympler import tracker\ntr = tracker.SummaryTracker()\n\ndef memdb_print_diff():\n    tr.print_diff()\n\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/mprofile.py",
    "content": "import subprocess\nimport threading\nimport copy, os\nimport time\nimport json\nfrom UTIL.colorful import *\n# test sync to github\n# ubuntu command to kill process: kill -9 $(ps -ef | grep fuqingxu |grep python | grep -v grep | awk '{print $ 2}')\n\narg_base = ['python', 'main.py']\nlog_dir = '%s/'%time.strftime(\"%Y-%m-%d-%H:%M:%S\", time.localtime())\nrun_group = \"bench\"\n# base_conf = 'train.json'\n\nn_run = 4\n\nn_run_mode = ['local', 'remote']\n\nconf_override = {\n    \"config.py->GlobalConfig-->note\":       \n                [\n                    \"train_origin_T(5itf) t5\",\n                    \"train_origin_T(5itf) t6\",\n                    \"train_origin_T(5itf) t7\",\n                    \"train_origin_T(5itf) t8\",\n                ],\n\n    \"MISSION.collective_assult.collective_assult_parallel_run.py->ScenarioConfig-->random_jam_prob\":       \n                [\n                    0.05,\n                    0.05,\n                    0.05,\n                    0.05,\n                ],\n\n    \"config.py->GlobalConfig-->seed\":       \n                [\n                    22222221,\n                    22222222,\n                    22222223,\n                    22222224,\n                ],\n    \"config.py->GlobalConfig-->device\":       \n                [\n                    \"cuda:0\",\n                    \"cuda:1\",\n                    \"cuda:2\",\n                    \"cuda:3\", \n\n                ],\n    \"config.py->GlobalConfig-->gpu_party\":       \n                [\n                    \"off\",\n                    \"off\",\n                    \"off\",\n                    \"off\",\n                ],\n\n}\n\n\n\nbase_conf = {\n    \"config.py->GlobalConfig\": {\n        # please checkout config.py for information\n        \"note\": \"example experiment\",                   # in case you forget the purpose of this trainning session, write a note\n        \"env_name\": \"collective_assult\",                # which environment, see ./MISSION/env_router.py\n        \"env_path\": \"MISSION.collective_assult\",       # path of environment\n        \"draw_mode\": \"Img\",                             # activate data plotting (Tensorboard is not used because I do not like it)\n        \"num_threads\": \"64\",                            # run N parallel envs, a 'env' is refered to as a 'thread'\n        \"report_reward_interval\": \"64\",                 # reporting interval\n        \"test_interval\": \"2048\",                        # test every $test_interval episode\n        \"fold\": \"1\",                                    # this 'folding' is designed for IPC efficiency, you can thank python GIL for such a strange design... \n        \"seed\": 22222222,                                   # seed controls pytorch and numpy\n        \"backup_files\": [                               # backup files, pack them up\n            \"example.jsonc\",\n            \"ALGORITHM/conc\",\n            \"MISSION/collective_assult/envs/collective_assult_env.py\"\n        ],\n        \"device\": \"cuda:0\",                             # choose from 'cpu' (no GPU), 'cuda' (auto select GPU), 'cuda:3' (manual select GPU) \n        # GPU memory is precious! assign multiple training process to a 'party', then they will share GPU memory \n        \"gpu_party\": \"Cuda0-Party0\",                     # default is 'off', \n        \"upload_after_test\": True\n    },\n\n    \"UTIL.exp_upload.py->DataCentralServer\": {\n        \"addr\": \"172.18.112.16\", \n        \"usr\": \"fuqingxu\", \n        \"pwd\": \"clara\"\n    },\n\n    \"MISSION.collective_assult.collective_assult_parallel_run.py->ScenarioConfig\": {\n        # please checkout ./MISSION/collective_assult/collective_assult_parallel_run.py for information\n        \"size\": \"5\",\n        \"random_jam_prob\": 0.05,\n        \"introduce_terrain\": \"True\",\n        \"terrain_parameters\": [\n            0.05,\n            0.2\n        ],\n        \"num_steps\": \"180\",\n        \"render\": \"False\",\n        \"render_with_unity\": \"False\",\n        \"MCOM_DEBUG\": \"False\",\n        \"render_ip_with_unity\": \"cn-cd-dx-1.natfrp.cloud:55861\",\n        \"half_death_reward\": \"True\",\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.conc.foundation->ReinforceAlgorithmFoundation\"\n        ]\n    },\n    \"ALGORITHM.conc.foundation.py->AlgorithmConfig\": {\n        \"n_focus_on\": 2,\n        \"actor_attn_mod\": \"False\",\n        \"extral_train_loop\": \"False\",\n        \"lr\": 0.0005,\n        \"ppo_epoch\": 24,\n        \"train_traj_needed\": \"64\",\n        \"load_checkpoint\": False\n    }\n}\n\n\nassert '_' not in run_group, ('下划线的显示效果不好')\nexp_log_dir = log_dir+'exp_log'\nif not os.path.exists('PROFILE/%s'%exp_log_dir):\n    os.makedirs('PROFILE/%s'%exp_log_dir)\nexp_json_dir = log_dir+'exp_json'\nif not os.path.exists('PROFILE/%s'%exp_json_dir):\n    os.makedirs('PROFILE/%s'%exp_json_dir)\n\n\nnew_json_paths = []\nfor i in range(n_run):\n    conf = copy.deepcopy(base_conf)\n    new_json_path = 'PROFILE/%s/run-%d.json'%(exp_json_dir, i+1)\n    for key in conf_override:\n        assert n_run == len(conf_override[key]), ('检查！n_run是否对应')\n        tree_path, item = key.split('-->')\n        conf[tree_path][item] = conf_override[key][i]\n    with open(new_json_path,'w') as f:\n        json.dump(conf, f, indent=4)\n    print(conf)\n    new_json_paths.append(new_json_path)\n\n\n\n\n\n\n\n\n\n\n\nfinal_arg_list = []\nprintX = [print红,print绿,print黄,print蓝,print紫,print靛,print亮红,print亮绿,print亮黄,print亮蓝,print亮紫,print亮靛]\n\nfor ith_run in range(n_run):\n    final_arg = copy.deepcopy(arg_base)\n    final_arg.append('--cfg')\n    final_arg.append(new_json_paths[ith_run])\n    final_arg_list.append(final_arg)\n    print('')\n\ndef worker(ith_run):\n    log_path = open('PROFILE/%s/run-%d.log'%(exp_log_dir, ith_run+1), 'w+')\n    printX[ith_run%len(printX)](final_arg_list[ith_run])\n    res = subprocess.run(final_arg_list[ith_run], stdout=log_path, stderr=log_path)\n    print('worker end')\n\ndef clean_process(pid):\n    import psutil\n    parent = psutil.Process(pid)\n    for child in parent.children(recursive=True):\n        try:\n            print亮红('sending Terminate signal to', child)\n            child.terminate()\n            time.sleep(5)\n            print亮红('sending Kill signal to', child)\n            child.kill()\n        except: pass\n    parent.kill()\n\ndef clean_up():\n    print亮红('clean up!')\n    parent_pid = os.getpid()   # my example\n    clean_process(parent_pid)\n\n\nif __name__ == '__main__':\n        \n    input('确认执行？')\n    input('确认执行！')\n\n    t = 0\n    while (t >= 0):\n        print('运行倒计时：', t)\n        time.sleep(1)\n        t -= 1\n\n    threads = [ threading.Thread( target=worker,args=(ith_run,) ) for ith_run in range(n_run) ]\n    for thread in threads:\n        thread.setDaemon(True)\n        thread.start()\n        print('错峰执行，启动', thread)\n        DELAY = 3\n        for i in range(DELAY):\n            print('\\r 错峰执行，启动倒计时%d     '%(DELAY-i), end='', flush=True)\n            time.sleep(1)\n\n    from atexit import register\n    register(clean_up)\n    while True:\n        is_alive = [thread.is_alive() for thread in threads]\n        if any(is_alive):\n            time_now = time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime()) \n            print(time_now, 'I am still running!', is_alive)\n            print靛('current scipt:%s, current log:%s'%(os.path.abspath(__file__), 'PROFILE/%s/run-%d.log'%(exp_log_dir, ith_run+1)))\n            time.sleep(60)\n        else:\n            break\n    print('[profile] All task done!')\n    for thread in threads:\n        thread.join()\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/mserver_launcher.sh",
    "content": "byobu new-session -d -s $USER"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/network.py",
    "content": "import socket, threading, pickle, uuid, os, atexit, time, json, psutil\nfrom UTIL.file_lock import FileLock\nport_finder = os.path.expanduser('~/HmapTemp') + '/PortFinder/find_free_port_no_repeat.json'\n\ndef check_pid(pid):        \n    return psutil.pid_exists(pid)\n    #     return True\n    # \"\"\" Check For the existence of a unix pid. \"\"\"\n    # try:\n    #     os.kill(pid, 0)\n    # except OSError:\n    #     return False\n    # else:\n    #     return True\n    \ndef find_free_port():\n    from contextlib import closing\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n        s.bind(('', 0))\n        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n        return s.getsockname()[1]\n\n\ndef find_free_port_no_repeat():\n    fp = port_finder\n    def read():\n        if not os.path.exists(fp):\n            with open(fp, \"w\") as f: pass\n\n        try:\n            with open(fp, \"r+\") as f: ports_to_be_taken = json.load(f)\n        except:\n            ports_to_be_taken = {}\n        return ports_to_be_taken\n    \n    def write(ports_to_be_taken):\n        # clean outdated\n        for port in list(ports_to_be_taken.keys()):\n            if not check_pid(ports_to_be_taken[port]['pid']):\n                ports_to_be_taken.pop(port)\n                print('removing dead item', port)\n\n        with open(fp, \"w\") as f:\n            json.dump(ports_to_be_taken, fp=f)\n\n\n    with FileLock(fp+'.lock'):\n\n        ports_to_be_taken = read()\n        \n        while True:\n            new_port = find_free_port()\n            if str(new_port) not in ports_to_be_taken:\n                break\n            else:\n                print('port taken, change another')\n        print('find port:', new_port)\n\n        ports_to_be_taken[str(new_port)] = {\n            'time': time.time(),\n            'pid': os.getpid(),\n        } \n        write(ports_to_be_taken)\n\n    def release_fn(port):\n        with FileLock(fp+'.lock'):\n            ports_to_be_taken = read()\n            if str(port) in ports_to_be_taken: \n                ports_to_be_taken.pop(str(port))\n            else:\n                pass\n            write(ports_to_be_taken)\n        return release_fn\n    \n    import atexit\n    atexit.register(release_fn, port=new_port)\n\n    return new_port, release_fn\n\n\ndef get_host_ip():\n    ip = None\n    try:\n        s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)\n        s.connect(('8.8.8.8',80))  # if fail here, please connect Internet to get IP?\n        ip=s.getsockname()[0]\n    finally:\n        s.close()\n    return ip\n\n\nBUFSIZE = 10485760\n# ip_port = ('127.0.0.1', 9999)\nDEBUG_NETWORK = False\nclass UdpServer:\n    def __init__(self, ip_port, obj='bytes') -> None:\n        self.ip_port = ip_port\n        self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n        self.server.bind(self.ip_port)\n        self.most_recent_client = None\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        return\n\n    def wait_next_dgram(self):\n        data, self.most_recent_client = self.server.recvfrom(BUFSIZE)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('recv from :', self.most_recent_client, ' data :', data)\n        return data\n\n    def reply_last_client(self, data):\n        assert self.most_recent_client is not None\n        if DEBUG_NETWORK: print('reply_last_client :', self.most_recent_client, ' data :', data)\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.server.sendto(data, self.most_recent_client)\n        return\n\n    def __del__(self):\n        self.server.close()\n        return\n\nclass UdpTargetedClient:\n    def __init__(self, target_ip_port, obj='bytes') -> None:\n        self.target_ip_port = target_ip_port\n        self.client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        return\n\n    def send_dgram_to_target(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.client.sendto(data, self.target_ip_port)\n        if DEBUG_NETWORK: print('send_targeted_dgram :', self.target_ip_port, ' data :', data)\n        return\n\n    def send_and_wait_reply(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.client.sendto(data, self.target_ip_port)\n        data, _ = self.client.recvfrom(BUFSIZE)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('get_reply :', self.target_ip_port, ' data :', data)\n        return data\n\n\n# ///////   test ipv4 udp\n# import numpy as np\n\n# server = UdpServer(ip_port, obj='pickle')\n# client = UdpTargetedClient(ip_port, obj='pickle')\n\n# def server_fn():\n#     data = server.wait_next_dgram()\n#     server.reply_last_client(np.array([4,5,6]))\n\n# def client_fn():\n#     rep = client.send_and_wait_reply(np.array([1,2,3]))\n\n\n# thread_hi = threading.Thread(target=server_fn)\n# thread_hello = threading.Thread(target=client_fn)\n# # 启动线程\n# thread_hi.start()\n# thread_hello.start()\n\nclass UnixUdpServer:\n    def __init__(self, unix_path, obj='bytes') -> None:\n        try: os.makedirs(os.path.dirname(unix_path))\n        except: pass\n        self.unix_path = unix_path\n        self.server = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)\n        self.server.bind(self.unix_path)\n        self.most_recent_client = None\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        return\n\n    def wait_next_dgram(self):\n        data, self.most_recent_client = self.server.recvfrom(BUFSIZE)\n        if DEBUG_NETWORK: print('self.most_recent_client',self.most_recent_client)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('recv from :', self.most_recent_client, ' data :', data)\n        return data\n\n    def reply_last_client(self, data):\n        assert self.most_recent_client is not None\n        if DEBUG_NETWORK: print('reply_last_client :', self.most_recent_client, ' data :', data)\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.server.sendto(data, self.most_recent_client)\n        return\n\n    def __del__(self):\n        self.server.close()\n        os.unlink(self.unix_path)\n        return\n\nclass UnixUdpTargetedClient:\n    def __init__(self, target_unix_path, self_unix_path=None, obj='bytes') -> None:\n        self.target_unix_path = target_unix_path\n        if self_unix_path is not None:\n            self.self_unix_path = self_unix_path  \n        else:\n            self.self_unix_path = target_unix_path+'_client_'+uuid.uuid1().hex[:5]\n        self.client = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)\n        self.client.bind(self.self_unix_path)\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        return\n\n    def send_dgram_to_target(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.client.sendto(data, self.target_unix_path)\n        if DEBUG_NETWORK: print('send_targeted_dgram :', self.target_unix_path, ' data :', data)\n        return\n\n    def send_and_wait_reply(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.client.sendto(data, self.target_unix_path)\n        data, _ = self.client.recvfrom(BUFSIZE)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('get_reply :', self.target_unix_path, ' data :', data)\n        return data\n    \n    def __del__(self):\n        self.client.close()\n        os.unlink(self.self_unix_path)\n        return\n\n\n# ///////   test unix udp\n# remote_uuid = uuid.uuid1().hex   # use uuid to identify threads\n\n# unix_path = 'TEMP/Sockets/unix/%s'%remote_uuid\n# server = UnixUdpServer(unix_path, obj='pickle')\n# client = UnixUdpTargetedClient(unix_path, obj='pickle')\n\n# def server_fn():\n#     data = server.wait_next_dgram()\n#     server.reply_last_client(np.array([4,5,6]))\n\n# def client_fn():\n#     rep = client.send_and_wait_reply(np.array([1,2,3]))\n\n\n# thread_hi = threading.Thread(target=server_fn)\n# thread_hello = threading.Thread(target=client_fn)\n# # 启动线程\n# thread_hi.start()\n# thread_hello.start()\n\n\n\nclass StreamingPackageSep:\n    def __init__(self):\n        self.buff = [b'']\n        self.myEOF = b'\\xaa\\x55\\xaaHMP\\xaa\\x55'    # those bytes follow 010101 or 101010 pattern\n        # self.myEOF = b'#A5@5A#'    # the EOF string for frame seperation\n\n    def lower_send(self, data, connection):\n        if DEBUG_NETWORK: assert self.myEOF not in data, 'This is (almost) not possible!'\n        data = data + self.myEOF\n        if DEBUG_NETWORK: print('data length:', len(data))\n        connection.send(data)\n\n    def lowest_recv(self, connection):\n        while True:\n            recvData = connection.recv(BUFSIZE)\n            # ends_with_mark = recvData.endswith(self.myEOF)\n            split_res = recvData.split(self.myEOF)\n            assert len(split_res) != 0\n            if len(split_res) == 1:\n                # 说明没有终止符，直接将结果贴到buf最后一项\n                self.buff[-1] = self.buff[-1] + split_res[0]\n                if self.myEOF in self.buff[-1]: self.handle_flag_breakdown()\n            else:\n                n_split = len(split_res)\n                for i, r in enumerate(split_res):\n                    self.buff[-1] = self.buff[-1] + r   # 追加buff\n                    if i == 0 and (self.myEOF in self.buff[-1]): \n                        # 第一次追加后，在修复的数据断面上发现了myEOF！\n                        self.handle_flag_breakdown()\n                    if i != n_split-1: \n                        # starts a new entry\n                        self.buff.append(b'')\n                    else:  \n                        # i == n_split-1, which is the last item\n                        if r == b'': continue\n            if len(self.buff)>=2:\n                # 数据成型，拿取成型的数据\n                buff_list = self.buff[:-1]  \n                self.buff = self.buff[-1:]\n                return buff_list\n\n    # Fox-Protocal\n    def lower_recv(self, connection, expect_single=True):\n        buff_list = self.lowest_recv(connection)\n        if expect_single:\n            assert len(buff_list) == 1, ('一次拿到了多帧数据, 但expect_single=True, 触发错误.', buff_list)\n            return buff_list[0], connection\n        else:\n            return buff_list, connection\n\n\n    def handle_flag_breakdown(self):\n        split_ = self.buff[-1].split(self.myEOF)\n        assert len(split_)==2\n        self.buff[-1] = split_[0]\n        # starts a new entry\n        self.buff.append(b'')\n        self.buff[-1] = split_[1]\n        return\n\n\n\n# send() is used for TCP SOCK_STREAM connected sockets, and sendto() is used for UDP SOCK_DGRAM unconnected datagram sockets\nclass UnixTcpServerP2P(StreamingPackageSep):\n    def __init__(self, unix_path, obj='bytes') -> None:\n        super().__init__()\n        try: os.makedirs(os.path.dirname(unix_path))\n        except: pass\n        self.unix_path = unix_path\n        self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n        self.server.bind(self.unix_path)\n        self.server.listen()\n        self.most_recent_client = None\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        atexit.register(self.__del__)\n\n    def accept_conn(self):\n        conn, _  = self.server.accept()\n        return conn\n\n    def wait_next_dgram(self):\n        if self.most_recent_client is None: self.most_recent_client, _ = self.server.accept()\n        data, self.most_recent_client = self.lower_recv(self.most_recent_client)\n        if DEBUG_NETWORK: print('self.most_recent_client',self.most_recent_client)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('recv from :', self.most_recent_client, ' data :', data)\n        return data\n\n    def reply_last_client(self, data):\n        assert self.most_recent_client is not None\n        if DEBUG_NETWORK: print('reply_last_client :', self.most_recent_client, ' data :', data)\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.lower_send(data, self.most_recent_client)\n        return\n\n    def __del__(self):\n        self.server.close()\n        try: os.unlink(self.unix_path)\n        except: pass\n        return\n\n\nclass UnixTcpServerMultiClient(StreamingPackageSep):\n    def __init__(self, unix_path, obj='bytes') -> None:\n        super().__init__()\n        try: os.makedirs(os.path.dirname(unix_path))\n        except: pass\n        self.unix_path = unix_path\n        self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n        self.server.bind(self.unix_path)\n        self.server.listen()\n        self.most_recent_client = None\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        self.on_receive_data = lambda data: data\n        atexit.register(self.__del__)\n\n    def serve_clients(self, most_recent_client):\n        while True:\n            data, most_recent_client = self.lower_recv(most_recent_client)\n            if self.convert_str: data = data.decode('utf8')\n            if self.use_pickle: data = pickle.loads(data)\n            reply = self.on_receive_data(data)\n            if self.use_pickle: reply = pickle.dumps(reply)\n            if self.convert_str: reply = bytes(reply, encoding='utf8')\n            self.lower_send(reply, most_recent_client)\n            if data == 'offline': break\n\n    def be_online(self):\n        while True:\n            most_recent_client, _ = self.server.accept()\n            t = threading.Thread(target=self.serve_clients, args=(most_recent_client, ))\n            t.daemon = True\n            t.start()\n\n    def __del__(self):\n        self.server.close()\n        try: os.unlink(self.unix_path)\n        except: pass\n        return\n\nclass UnixTcpClientP2P(StreamingPackageSep):\n    def __init__(self, target_unix_path, self_unix_path=None, obj='bytes') -> None:\n        super().__init__()\n        self.target_unix_path = target_unix_path\n        if self_unix_path is not None:\n            self.self_unix_path = self_unix_path  \n        else:\n            self.self_unix_path = target_unix_path+'_client_'+uuid.uuid1().hex[:5]\n        self.client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n        self.client.bind(self.self_unix_path)\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        self.connected = False\n        atexit.register(self.__del__)\n\n    def send_dgram_to_target(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_unix_path); self.connected = True\n        self.lower_send(data, self.client)\n        if DEBUG_NETWORK: print('send_targeted_dgram :', self.client, ' data :', data)\n        return\n\n    def send_and_wait_reply(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_unix_path); self.connected = True\n        self.lower_send(data, self.client)\n        data, _ = self.lower_recv(self.client)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('get_reply :', self.client, ' data :', data)\n        return data\n\n    def __del__(self):\n        self.client.close()\n        os.unlink(self.self_unix_path)\n        return\n\n'''\n\n    remote_uuid = uuid.uuid1().hex   # use uuid to identify threads\n\n    unix_path = 'TEMP/Sockets/unix/%s'%remote_uuid\n    server = UnixTcpServerP2P(unix_path, obj='pickle')\n    client = UnixTcpClientP2P(unix_path, obj='pickle')\n\n    def server_fn():\n        # data = server.wait_next_dgram()\n        # server.reply_last_client(np.array([4,5,6]))\n        while 1:\n            data = server.wait_next_dgram()\n            server.reply_last_client(data)\n\n    def client_fn():\n        # rep = client.send_and_wait_reply(np.array([1,2,3]))\n        while True:\n            buf = np.random.rand(100,1000)\n            rep = client.send_and_wait_reply(buf)\n            assert (buf==rep).all()\n            print('成功')\n\n\n    thread_hi = threading.Thread(target=server_fn)\n    thread_hello = threading.Thread(target=client_fn)\n    # 启动线程\n    thread_hi.start()\n    thread_hello.start()\n\n'''\n\n\n\n# send() is used for TCP SOCK_STREAM connected sockets, and sendto() is used for UDP SOCK_DGRAM unconnected datagram sockets\nclass TcpServerP2P(StreamingPackageSep):\n    def __init__(self, ip_port, obj='bytes') -> None:\n        super().__init__()\n        self.ip_port = ip_port\n        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.server.bind(self.ip_port)\n        self.server.listen()\n        self.most_recent_client = None\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        atexit.register(self.__del__)\n\n    def accept_conn(self):\n        conn, _  = self.server.accept()\n        return conn\n\n    def manual_wait_connection(self):\n        if self.most_recent_client is None: \n            self.most_recent_client, _ = self.server.accept()\n        return\n\n    def wait_next_dgram(self):\n        if self.most_recent_client is None: self.most_recent_client, _ = self.server.accept()\n        data, self.most_recent_client = self.lower_recv(self.most_recent_client)\n        if DEBUG_NETWORK: print('self.most_recent_client',self.most_recent_client)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('recv from :', self.most_recent_client, ' data :', data)\n        return data\n\n    def wait_multi_dgrams(self):\n        if self.most_recent_client is None: self.most_recent_client, _ = self.server.accept()\n        data_list, self.most_recent_client = self.lower_recv(self.most_recent_client, expect_single=False)\n        if DEBUG_NETWORK: print('self.most_recent_client',self.most_recent_client)\n        if self.convert_str: data_list = [data.decode('utf8') for data in data_list]\n        if self.use_pickle: data_list = [pickle.loads(data) for data in data_list]\n        if DEBUG_NETWORK: print('recv from :', self.most_recent_client, ' data_list :', data_list)\n        return data_list\n\n    def reply_last_client(self, data):\n        assert self.most_recent_client is not None\n        if DEBUG_NETWORK: print('reply_last_client :', self.most_recent_client, ' data :', data)\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        self.lower_send(data, self.most_recent_client)\n        return\n\n    def __del__(self):\n        self.close()\n        return\n\n    def close(self):\n        self.server.close()\n\nclass TcpClientP2P(StreamingPackageSep):\n    def __init__(self, target_ip_port, self_ip_port=None, obj='bytes') -> None:\n        super().__init__()\n        self.target_ip_port = target_ip_port\n        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.use_pickle = (obj=='pickle')\n        self.convert_str = (obj=='str')\n        self.connected = False\n        atexit.register(self.__del__)\n\n    def send_dgram_to_target(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n        self.lower_send(data, self.client)\n        if DEBUG_NETWORK: print('send_targeted_dgram :', self.client, ' data :', data)\n        return\n\n    def manual_connect(self):\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n\n    def send_and_wait_reply(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        if self.convert_str: data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n        self.lower_send(data, self.client)\n        data, _ = self.lower_recv(self.client)\n        if self.convert_str: data = data.decode('utf8')\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('get_reply :', self.client, ' data :', data)\n        return data\n\n    def __del__(self):\n        self.close()\n        return\n\n    def close(self):\n        self.client.close()\n'''\n\n    ipport = ('127.0.0.1', 25453)\n    server = TcpServerP2P(ipport, obj='pickle')\n    client = TcpClientP2P(ipport, obj='pickle')\n    def server_fn():\n        data = server.wait_next_dgram()\n        server.reply_last_client(np.array([4,5,6]))\n    def client_fn():\n        rep = client.send_and_wait_reply(np.array([1,2,3]))\n    thread_hi = threading.Thread(target=server_fn)\n    thread_hello = threading.Thread(target=client_fn)\n    # 启动线程\n    thread_hi.start()\n    thread_hello.start()\n\n'''\n\nclass TcpClientP2PWithCompress(StreamingPackageSep):\n    def __init__(self, target_ip_port, self_ip_port=None, obj='bytes') -> None:\n        import lz4.block as lz4block\n        self.lz4block = lz4block\n        self.try_decom_usize = 255\n        super().__init__()\n        self.target_ip_port = target_ip_port\n        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        self.use_pickle = (obj=='pickle')\n        assert not (obj=='str')\n        self.connected = False\n        atexit.register(self.__del__)\n\n    def decompress(self, data):\n        while True:\n            try:\n                decompressed = self.lz4block.decompress(data, uncompressed_size=self.try_decom_usize)\n                return decompressed\n            except:\n                self.try_decom_usize *= 2\n                if self.try_decom_usize > 10485760: # 10 MB\n                    assert False, \"compression failure\"\n        return None\n\n    def compress(self, data):\n        compressed = self.lz4block.compress(data, store_size=False)\n        return compressed\n\n    def send_dgram_to_target(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n        data = self.compress(data)\n        self.lower_send(data, self.client)\n        if DEBUG_NETWORK: print('send_targeted_dgram :', self.client, ' data :', data)\n        return\n\n    def manual_connect(self):\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n\n    def send_and_wait_reply(self, data):\n        if self.use_pickle: data = pickle.dumps(data)\n        data = bytes(data, encoding='utf8')\n        if not self.connected: self.client.connect(self.target_ip_port); self.connected = True\n        data = self.compress(data)\n        self.lower_send(data, self.client)\n        data, _ = self.lower_recv(self.client)\n        data = self.decompress(data)\n        if self.use_pickle: data = pickle.loads(data)\n        if DEBUG_NETWORK: print('get_reply :', self.client, ' data :', data)\n        return data\n\n    def __del__(self):\n        self.close()\n        return\n\n    def close(self):\n        self.client.close()\n\nclass QueueOnTcpClient():\n    def __init__(self, ip):\n        TCP_IP, TCP_PORT = ip.split(':')\n        TCP_PORT = int(TCP_PORT)\n        ip_port = (TCP_IP, TCP_PORT)\n\n        self.tcpClientP2P = TcpClientP2P(ip_port, obj='str')\n        self.tcpClientP2P.manual_connect()\n\n    def send_str(self, b_msg):\n        self.tcpClientP2P.send_dgram_to_target(b_msg)\n\n    def close(self):\n        self.tcpClientP2P.close()\n\n    def __del__(self):\n        self.close()\n\nclass QueueOnTcpServer():\n    def __init__(self, ip_port):\n        from UTIL.network import TcpServerP2P\n        self.tcpServerP2P = TcpServerP2P(ip_port, obj='str')\n        self.handler = None\n        self.queue = None\n        self.buff = ['']\n\n    def wait_connection(self):\n        self.tcpServerP2P.manual_wait_connection()\n        t = threading.Thread(target=self.listening_thread)\n        t.daemon = True\n        t.start()\n\n    def listening_thread(self):\n        while True:\n            buff_list = self.tcpServerP2P.wait_multi_dgrams()\n            if self.handler is not None: \n                self.handler(buff_list)\n            if self.queue is not None: \n                self.queue.put(buff_list)\n\n    def set_handler(self, handler):\n        self.handler = handler\n\n    def get_queue(self):\n        import queue\n        self.queue = queue.Queue()\n        return self.queue\n\n    def recv(self):\n        return\n\n    def close(self):\n        self.tcpServerP2P.close()\n\n    def __del__(self):\n        self.close()"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/pip_find_missing.py",
    "content": "#coding=utf-8\nimport glob,os,sys,re,subprocess,platform\n\n\ndef print红(*kw):\n    print(\"\\033[0;31m\",*kw,\"\\033[0m\")\ndef print绿(*kw):\n    print(\"\\033[0;32m\",*kw,\"\\033[0m\")\ndef print黄(*kw):\n    print(\"\\033[0;33m\",*kw,\"\\033[0m\")\ndef print蓝(*kw):\n    print(\"\\033[0;34m\",*kw,\"\\033[0m\")\ndef print紫(*kw):\n    print(\"\\033[0;35m\",*kw,\"\\033[0m\")\ndef print靛(*kw):\n    print(\"\\033[0;36m\",*kw,\"\\033[0m\")\ndef printX(*kw):\n    print(\"\\033[0;38m\",*kw,\"\\033[0m\")\n\n# 用pip执行安装指令\ndef install(package):\n    try:\n        subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\",\n        \"-i\",\"https://pypi.tuna.tsinghua.edu.cn/simple\",\n        \"--progress-bar\",\"emoji\",\n        \"--prefer-binary\", \n        package])\n    except:\n        print红(\"执行命令 \", \"pip\", \"install\",\"-i\",\"https://pypi.tuna.tsinghua.edu.cn/simple\", package, \"时，抛出错误\")\n        pass\n\nsys_name = platform.system()\nif sys_name == \"Windows\":\n    try:\n        from colorama import init,Fore,Back,Style\n        init(autoreset=False)\n        def print红(*kw):\n            print(Fore.RED,*kw)\n        def print绿(*kw):\n            print(Fore.GREEN,*kw)\n        def print黄(*kw):\n            print(Fore.YELLOW,*kw)\n        def print蓝(*kw):\n            print(Fore.BLUE,*kw)\n        def print紫(*kw):\n            print(Fore.MAGENTA,*kw)\n        def print靛(*kw):\n            print(Fore.CYAN,*kw)\n\n    except:\n        install('colorama')\n        print('颜色组件安装完成！现在请重新运行！')\n        sys_name.exit(0)\n\n\"\"\"\n# step 1, 查询所有子路径.py脚本文件，列表\n\"\"\"\npy_script_list = glob.glob('./**/*.py', recursive=True)\nrequired = []\nlocal_name_list = {\"None\":False}\n引发连锁错误的包_列表 = {\"None\":False}\n\n\n\"\"\"\n# step 2, 提取 import 以及 from *** import\n\"\"\"\ndef 是否为工程内的文件交叉调用(包,python_file):\n    包_org = 包\n    if '.' not in 包:\n        res = os.path.exists(\"./\"+包+\".py\")\n        if res:\n            return True,包_org\n        else:\n            return False, 包_org\n    if 包.startswith('.'):\n        包 = os.path.dirname(python_file).replace(\"/\", \".\").replace(\"..\", \".\") + 包\n        包_org = 包\n    包 = 包.replace(\".\", \"/\")\n    res = os.path.exists(\"./\"+包+\".py\")\n    if res:\n        tmp = 包_org.split(\".\")\n        if tmp[0]!='': local_name_list[tmp[0]] = True\n        return True, 包_org\n    else:\n        return False, 包_org\n\nfor python_file in py_script_list:\n    with open(python_file,encoding='UTF-8') as f:\n        lines = f.readlines()\n        for line in lines:\n            if \"import\" in line or \"from\" in line:\n                t = line.split()\n                # from 开头 或者 import开头\n                if t[0] == \"import\" or t[0] == \"from\":\n                    i = 1\n                    包 = \"\"\n                    for ti in t[1:]:\n                        if (ti!=\"import\") and (ti!=\"as\"):\n                            包 = 包 + ti\n                        else:\n                            break\n                    if \",\" in 包:\n                        包_l = 包.split(\",\")\n                    else:\n                        包_l = [包]\n                    for 包 in 包_l:\n                        包_debug = 包\n                        if 包_debug == '.':\n                            continue\n                        res,包 = 是否为工程内的文件交叉调用(包,python_file)\n                        if not res:\n                            required.append(包)\n                            \n\n\nrequired = set(required)\nrequired = sorted(required)\n\n\"\"\"\n# step 3, 尝试import，筛查缺失的包\n\"\"\"\nprint黄(\"**************************************************************\")\nprint黄(\"尝试import\")\n# 使用清华镜像\nneed_fix_cmd_orig = \"pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \"\nneed_fix_cmd      = \"pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \"\nneed_fix_list = []\nfailed_cmd = []\nchain_failed = []\nfor 包 in required:\n    cmd = \"import \"+包\n    try:\n        # 如果这里罕见地报错，\n        # 说明该文件有 import开头的、被“”“包裹的注释, \n        # 找到它，然后删除这个奇葩注释\n        exec(cmd)\n    except ImportError as error:\n        print红(\"error trying to do:\",cmd,error.msg)\n        error_str = error.msg.split('\\'')\n        package_import_error = (len(error_str) >= 2)\n        if not package_import_error:\n            continue\n        包_error = error_str[1]\n        if '.' in 包:\n            包_l = 包.split('.')\n            包_tmp = 包_l[0]\n        # 引发问题的不是这个包本身，而是这个包import其他包，但这个包内引包失败了\n        # 仅仅是一个连锁错误而已，无需处理\n        if 包_error != 包:\n            print红(\"发生连锁引包错误: \",error.msg)\n            chain_failed.append(error.msg)\n            cmd = cmd + \"\\t\\t此项仅仅由连锁引包错误导致: \" + error.msg\n            if 包_tmp not in 引发连锁错误的包_列表:\n                引发连锁错误的包_列表[包_tmp]=True\n        else:\n            # 非连锁，一定是真的缺\n            引发连锁错误的包_列表[包]=False\n        failed_cmd.append(cmd)\n        if '.' in 包:\n            包_l = 包.split('.')\n            包 = 包_l[0]\n        if len(包)>19: # some comment mixed in somehow\n            continue\n        need_fix_list.append(包)\n    except BaseException as error:\n        print红(error)\n    else:\n        print绿(\"this package is ok:\",cmd)\n\nneed_fix_list = set(need_fix_list)\nneed_fix_list = sorted(need_fix_list)\nif len(failed_cmd) > 0:\n    print红(\"以下的包import操作失败\")\n    for cmd in failed_cmd:\n        print红(cmd)\n\n\n\"\"\"\n# step 4, 处理缺失的包，并找到对应的pip安装指令\n\"\"\"\nterm_replace_dict = {\n    \"cv2\":\"opencv-python\",\n    \"torch\":\"torch\",\n    \"mpi\":\"mpi4py\",\n    \"MPI\":\"mpi4py\",\n    \"mujoco_py\":\"None\",    # pip cannot install this????\n    \"pybullet_envs\":\"None\",\n    \"stable_baselines3\":\"None\",\n    \"pyximport\":\"cython\",\n    \"PIL\":\"None\",\n    \"collective_assult\":\"None\",\n    \"gym_fortattack\":\"None\",\n    \"multiagent\":\"None\",\n    \"z_config\":\"None\",\n    \"gym_vecenv\":\"None\"\n}\n\nPIL\n\nfor inx, 包 in enumerate(need_fix_list):\n    if 包 in term_replace_dict:\n        包 = term_replace_dict[包]\n        need_fix_list[inx] = 包\n    if (包 in local_name_list) or (包 in 引发连锁错误的包_列表 and 引发连锁错误的包_列表[包]==True):\n        need_fix_list[inx] = \"None\"\n\nneed_fix_list = set(need_fix_list)\nneed_fix_list = sorted(need_fix_list)\nif len(need_fix_list) == 0:\n    print绿(\"所有依赖已就绪\")\n    exit(0)\n\n\"\"\"\n# step 5, 如果有requirement.txt，从中提取出有用的版本信息\n\"\"\"\nprint黄(\"**************************************************************\")\nprint蓝(\"requirement.txt中的相关信息\")\nexecute_fix = []\nif os.path.exists(\"./requirements.txt\"):\n    with open(\"./requirements.txt\",encoding='UTF-8') as f:\n        lines = f.readlines()\n        for line in lines:\n            if line.startswith(\"-\"):\n                print蓝(\"requirement.txt要求以下版本： -->   \"+\"pip install \"+line[:-1])\n                print蓝(\"首先git clone，然后找到setup.py的路径，然后执行 pip install --no-deps  -e .\")\n                continue\n            line_split = line.split(\"==\")\n            if (len(line_split)==2) and (line_split[0] in need_fix_list):\n                print蓝(\"requirement.txt要求以下版本： -->   \"+\"pip install \"+line[:-1])\n\n\n\n\"\"\"\n# step 6, 如果需要安装pytorch，gym等特殊包，对应给出安装建议\n\"\"\"\n\ndef config_anaconda():\n    with open(__file__,'r') as f:\n        conda_cmd = f.readlines()\n        condarc_lines = conda_cmd[-18:-2]\n    f = open('./.condarc','w+')\n    f.writelines(condarc_lines)\n    f.close()\n\n\nprint黄(\"**************************************************************\")\ntry:\n    conda_env_name = sys.executable.split('/')[-3]\nexcept:\n    conda_env_name = sys.executable.split('\\\\')[-2]\nfor 包 in need_fix_list:\n    if 包 == \"torch\":\n        print蓝(\"pytorch需要手动安装，pytorch 的安装方法（选择其一），然后重新运行该脚本:\")\n        print蓝(\"conda install -n %s  pytorch torchvision torchaudio cudatoolkit=10.2 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/\"%conda_env_name)\n        print蓝(\"conda install -n %s  pytorch torchvision torchaudio cudatoolkit=11.0 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/\"%conda_env_name)\n        print蓝(\"\")\n        \n        sys.exit(0)\n    # if 包 == \"tensorflow\":\n    #     print靛(\"Tensorflow需要手动安装，首先，更换conda源的指令\")\n    #     config_anaconda()\n    #     print靛(\"cp\",os.getcwd()+\"/.condarc\",\"~/.condarc\")\n    #     print靛(\"然后，安装TF一代的指令\")\n    #     print靛(\"conda install -n %s tensorflow-gpu=1.*\"%conda_env_name)\n    #     sys.exit(0)\n    if (包 is not \"None\"):\n        need_fix_cmd = need_fix_cmd + 包 + \"  \"\n        execute_fix.append(包)\nprint黄(\"**************************************************************\")\nprint绿(need_fix_cmd)\nprint黄(\"**************************************************************\")\n\n\n\n\n\"\"\"\n# step 7, 对于除了特殊包之外的其他软件包，调用pip直接安装\n\"\"\"\nprint绿(\"注意！当前的conda环境是：\",conda_env_name,\" 所有操作都将只在该conda环境内生效\")\ninput(\"执行自动安装？\")\nif input(\"确定执行自动安装？(y/n)\")=='y':\n    for 包 in execute_fix:\n        install(包)\n\n\n\"\"\"\n# step 8, 完成任务，取消以下代码的注释，测试pytorch是否工作\n\"\"\"\n        \n# import torch\n# flag = torch.cuda.is_available()\n# print(flag)\n\n# ngpu= 1\n# # Decide which device we want to run on\n# device = torch.device(\"cuda:0\" if (torch.cuda.is_available() and ngpu > 0) else \"cpu\")\n# print(device)\n# print(torch.cuda.get_device_name(0))\n# print(torch.rand(3,3).cuda()) \n\n'''\n不要修改或者删除以下内容！！有用！！\nchannels:\n  - defaults\nshow_channel_urls: true\nchannel_alias: https://mirrors.tuna.tsinghua.edu.cn/anaconda\ndefault_channels:\n  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main\n  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free\n  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r\n  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro\n  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2\ncustom_channels:\n  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/shm_env.py",
    "content": "import numpy as np\nimport time\nfrom MISSION.env_router import make_env_function\nfrom UTIL.colorful import print亮红\n\nN = lambda x: np.array(x)\n\n# Here use a pool of multiprocess workers to control a bundle of environment to sync step\n# SuperPool.add_target: in each process, initiate a class object named xxxx, \n#     example:\n#     self.SuperPool.add_target(name='env', lam=EnvWithRay, args_list=env_args_dict_list)\n# SuperPool.exec_target: in each process, make the object (id by name) to call its method\n#     example:\n#     self.SuperPool.exec_target(name='env', dowhat='step', args_list=actions)\n#     self.SuperPool.exec_target(name='env', dowhat='reset')\n\n\n# ! this class execute in child process\n# Ray is much slower compare to our shm/pipe solution,\n# we don't use it any more despite the class name\nclass EnvWithRay(object):\n    def __init__(self, env_args_dict):\n        env_name = env_args_dict['env_name']\n        proc_index = env_args_dict['proc_index']\n        env_init_fn = make_env_function(env_name=env_name, rank=proc_index)\n        self.env = env_init_fn()\n        # finally the env is initialized\n        self.observation_space = self.env.observation_space\n        self.action_space = self.env.action_space\n        self.echo = None\n\n    def __del__(self):\n        # print亮红('[shm_env.py] exec EnvWithRay exit')\n        if hasattr(self,'env'): \n            del self.env\n\n    def step(self, act):\n        if np.isnan(act).any(): \n            # env is paused, skip by returning previous obs\n            assert self.echo is not None\n            return self.echo\n        # ! step here\n        ob, reward, done, info = self.env.step(act)\n        if isinstance(ob, list): \n            print('warning, ob is list, which is low-efficient')\n            ob = np.array(ob, dtype=object)\n        \n        if np.any(done):\n            # if the environment is terminated, \n            # first, put terminal obs into 'info'\n            if info is None:\n                info = {'obs-echo':ob}\n            else:\n                assert isinstance(info, dict), ('oh? info is not dictionary? did not expect that...')\n                info.update({'obs-echo': ob.copy()})\n            # second, automatically reset env\n            ob = self.env.reset()\n            if isinstance(ob, tuple):\n                # some env like starcraft return (ob, info) tuple at reset\n                # have info, then update info\n                ob, info_reset = ob\n                info = self.dict_update(info, info_reset)\n                \n        # preserve an echo here, \n        # will be use to handle unexpected env pause\n        self.echo = [ob, reward, done, info]\n        # give everything back to main process\n        return (ob, reward, done, info)\n\n    def dict_update(self, info, info_reset):\n        for key in info_reset:\n            if key in info: info[key+'-echo'] = info.pop(key)\n        info.update(info_reset)\n        return info\n\n    def reset(self):\n        return self.env.reset()\n\n    def sleep(self):\n        return self.env.sleep()\n\n    def render(self):\n        return self.env.render()\n\n    def close(self):\n        return None\n\n    def get_act_space(self):\n        return self.action_space\n\n    def get_obs_space(self):\n        return self.observation_space\n\n    def get_act_space_str(self):\n        return str(self.action_space)\n\n    def get_obs_space_str(self):\n        return str(self.observation_space)\n\n\n# ! this class execute in main process\nclass SuperpoolEnv(object):\n    def __init__(self, process_pool, env_args_dict_list, spaces=None):\n        self.SuperPool = process_pool\n        self.num_envs = len(env_args_dict_list)\n        self.env_name_marker = env_args_dict_list[0][0]['marker']\n        self.env = 'env' + self.env_name_marker\n        self.SuperPool.add_target(name=self.env, lam=EnvWithRay, args_list=env_args_dict_list)\n        try:\n            self.observation_space = self.SuperPool.exec_target(name=self.env, dowhat='get_obs_space')[0]\n            self.action_space =      self.SuperPool.exec_target(name=self.env, dowhat='get_act_space')[0]\n        except:\n            print亮红('Gym Space is unable to transfer between processes, using string instead')\n            self.observation_space = self.SuperPool.exec_target(name=self.env, dowhat='get_obs_space_str')[0]\n            self.action_space =      self.SuperPool.exec_target(name=self.env, dowhat='get_act_space_str')[0]\n        # self.observation_space = self.SuperPool.exec_target(name=self.env, dowhat='get_obs_space_str')[0]\n        # self.action_space =      self.SuperPool.exec_target(name=self.env, dowhat='get_act_space_str')[0]\n        return\n\n    def get_space(self):\n        return {'obs_space': self.observation_space, 'act_space': self.action_space}\n\n    def step(self, actions):\n        # ENV_PAUSE = [np.isnan(thread_act).any() for thread_act in actions]\n        results = self.SuperPool.exec_target(name=self.env, dowhat='step', args_list=actions)\n        obs, rews, dones, infos = zip(*results)\n\n        # if any(ENV_PAUSE):\n        #     assert not all(ENV_PAUSE)\n        #     return self.stack(ENV_PAUSE, obs, rews, dones, infos)\n        # else:\n        try:\n            return np.stack(obs), np.stack(rews), np.stack(dones), np.stack(infos)\n        except:\n            assert False, ('unalign! ',obs, rews, dones)\n\n\n    def reset(self):\n        results = self.SuperPool.exec_target(name=self.env, dowhat='reset')\n        # [ env.reset.remote() for env in self.ray_env_vector])\n        if isinstance(results[0], tuple):\n            obs, infos = zip(*results)\n            return np.stack(obs), np.stack(infos)\n        else:\n            return np.stack(results)\n\n    def sleep(self):\n        self.SuperPool.exec_target(name=self.env, dowhat='sleep')\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/shm_pool.pyx",
    "content": "\"\"\"\n    Author: Fu Qingxu,CASIA\n    Description: Efficient parallel execting tool, \n        it resembles Ray but:\n            1.optimized for single machine using shared memory\n            2.optimized for numpy ndarray\n            3.use semaphore for IPC\n            4.faster!\n    Note: \n        SHARE_BUF_SIZE: shared memory size, 10MB per process\n\"\"\"\nimport time, pickle, platform, setproctitle, numpy, copy, traceback\nfrom multiprocessing import Process, RawValue, Semaphore\nfrom multiprocessing import shared_memory\nfrom .hmp_daemon import kill_process_and_its_children\nfrom ctypes import c_bool, c_uint32\nfrom sys import stdout\nSHARE_BUF_SIZE = 10485760 # 10 MB for parameter buffer\nREGULAR_BUF_SIZE = 500000 # The non-numpy content max buffer size\n\nTRAFFIC_LIGHT_ERROR = 2\nTRAFFIC_LIGHT_CHILD_BUSY = 1\nTRAFFIC_LIGHT_CHILD_FREE = 0\n\n# define Python user-defined exceptions\nclass ChildExitException(Exception):\n    pass\n\ndef print_red(*kw,**kargs):\n    print(\"\\033[1;31m\",*kw,\"\\033[0m\",**kargs)\n\ndef print_green(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\nif not stdout.isatty():\n    print_green = print_red = print\n    \n# optimize share mem IO for numpy ndarray\nclass ndarray_indicator():\n    def __init__(self, shape, dtype, shm_start, shm_end):\n        self.shape = shape\n        self.dtype = dtype\n        self.shm_start = shm_start\n        self.shm_end = shm_end\n        self.count = (self.shm_end-self.shm_start)//self.dtype.itemsize\n\n# optimize share mem IO for numpy ndarray\ndef convert_ndarray(numpy_ndarray, shm_pointer, shm):\n    nbyte = numpy_ndarray.nbytes\n    shape = numpy_ndarray.shape\n    dtype = numpy_ndarray.dtype\n    assert shm_pointer+nbyte < SHARE_BUF_SIZE, ('share memory overflow, need at least %d, yet only have %d'%(shm_pointer+nbyte, SHARE_BUF_SIZE))\n    shm_array_object = numpy.ndarray(shape, dtype=dtype, buffer=shm[shm_pointer:shm_pointer+nbyte])\n    shm_array_object[:] = numpy_ndarray[:]\n    NID = ndarray_indicator(shape, dtype, shm_pointer, shm_pointer+nbyte)\n    shm_pointer = shm_pointer+nbyte\n    return NID, shm_pointer\n\n# optimize share mem IO for numpy ndarray\ndef deepin(obj, shm, shm_pointer):\n    if isinstance(obj, list): iterator_ = enumerate(obj)\n    elif isinstance(obj, dict): iterator_ = obj.items()\n    elif isinstance(obj, numpy.ndarray) and obj.dtype=='object': iterator_ = enumerate(obj)\n    else: \n        assert not isinstance(obj, tuple)\n        return shm_pointer\n    for k, v in iterator_:\n        if isinstance(v, (list,dict)) and len(v)>0:\n            shm_pointer = deepin(v, shm, shm_pointer)\n        elif isinstance(v, tuple):\n            item2 = list(v)\n            shm_pointer = deepin(item2, shm, shm_pointer)\n            obj[k] = tuple(item2)\n        elif isinstance(v, numpy.ndarray) and len(v)>0:\n            if v.dtype == 'object':\n                shm_pointer = deepin(v, shm, shm_pointer)\n            elif v.nbytes < 64:\n                pass\n            else:\n                NID, shm_pointer = convert_ndarray(v, shm_pointer, shm)\n                obj[k] = NID\n        else:\n            continue\n    return shm_pointer\n\n# optimize share mem IO for numpy ndarray\ndef opti_numpy_object(obj, shm, shm_pointer=REGULAR_BUF_SIZE):\n    shm_pointer_terminal = deepin(obj, shm, shm_pointer)\n    return obj, shm_pointer_terminal\n\n# optimize share mem IO for numpy ndarray\ndef reverse_deepin(obj, shm):\n    if isinstance(obj, list): iterator_ = enumerate(obj)\n    elif isinstance(obj, dict): iterator_ = obj.items()\n    elif isinstance(obj, numpy.ndarray) and obj.dtype == 'object': iterator_ = enumerate(obj)\n    else: return\n    for k, v in iterator_:\n        if isinstance(v, (list,dict)) and len(v)>0:\n            reverse_deepin(v, shm)\n        if isinstance(v, numpy.ndarray) and v.dtype == 'object' and len(v)>0:\n            reverse_deepin(v, shm)\n        elif isinstance(v, tuple):\n            item2 = list(v)\n            reverse_deepin(item2, shm)\n            obj[k] = tuple(item2)\n        elif isinstance(v, ndarray_indicator):\n            obj[k] = numpy.frombuffer(shm, dtype=v.dtype, offset=v.shm_start, count=v.count).reshape(v.shape)\n    return\n\n# optimize share mem IO for numpy ndarray\ndef reverse_opti_numpy_object(obj, shm):\n    reverse_deepin(obj, shm)\n\n    return obj\n\n\n\n\n\n\n\nclass SuperProc(Process):\n    \"\"\"\n        Child process worker (efficient distributed worker)\n    \"\"\"\n    # initialize traffic IO\n    def __init__(self, index, smib, smiobli, smtl, buf_size_limit, base_seed, sem_push, sem_pull):\n        super(SuperProc, self).__init__()\n        self.shared_memory = smib\n        self.shared_memory_io_buffer = smib.buf\n        self.shared_memory_io_buffer_len_indicator = smiobli\n        self.shared_memory_traffic_light = smtl\n        self.buf_size_limit = buf_size_limit\n        self.local_seed = index + base_seed\n        self.index = index\n        self.sem_push = sem_push\n        self.sem_pull = sem_pull\n        self.target_tracker = []\n        \n    # on parent exit\n    def __del__(self):\n        if hasattr(self,'_deleted_'): return    # avoid exit twice\n        else: self._deleted_ = True     # avoid exit twice\n        self.shared_memory.close()\n        for target_name in self.target_tracker: \n            setattr(self, target_name, None)    # GC by clearing the pointer.\n        # force terminate all child process\n        try: kill_process_and_its_children(self)\n        except Exception as e: print_red('[shm_pool]: error occur when kill_process_and_its_children:\\n', e)\n\n    # add any class level objects\n    def automatic_generation(self, name, gen_fn, *arg):\n        setattr(self, name, gen_fn(*arg))\n\n    # add any class level objects\n    def add_targets(self, new_tarprepare_args):\n        for new_target_arg in new_tarprepare_args:\n            name, gen_fn, arg = new_target_arg\n            if name not in self.target_tracker: self.target_tracker.append(name)\n            if arg is None:\n                self.automatic_generation(name, gen_fn)\n            elif isinstance(arg, tuple): \n                self.automatic_generation(name, gen_fn, *arg)\n            else:                        \n                self.automatic_generation(name, gen_fn, arg)\n\n    # execute any class method, return the results\n    def execute_target(self, recv_args):\n        res_list = [None] * len(recv_args)\n        for i, recv_arg in enumerate(recv_args):\n            name, dowhat, arg = recv_arg\n            if dowhat == 'None':\n                continue\n            if arg is None:              \n                res = getattr(getattr(self, name), dowhat)()\n            elif isinstance(arg, tuple): \n                res = getattr(getattr(self, name), dowhat)(*arg)\n            else:                        \n                res = getattr(getattr(self, name), dowhat)(arg)\n            res_list[i] = res\n        return res_list\n\n    # inf loop, controlled / blocked by semaphore\n    def run(self):\n        # reset numpy seed\n        import numpy; numpy.random.seed(self.local_seed)\n        # set top process title\n        setproctitle.setproctitle('HmapShmPoolWorker_%d'%self.index)\n        try:\n            while True:\n                recv_args = self._recv_squence() # block and wait incoming req\n                if not isinstance(recv_args, list): # not list object, switch to helper channel\n                    if recv_args == 0: \n                        self._set_done()\n                        self.add_targets(self._recv_squence())\n                        self._set_done()\n                    elif recv_args == -1:\n                        self._set_done() # termination signal\n                        break\n                    else:\n                        assert False, \"unknown command\"\n                    continue\n                else:\n                    # if list, execute target\n                    result = self.execute_target(recv_args)\n                    # return the results (self._set_done() is called inside)\n                    self._send_squence(result)\n\n        except KeyboardInterrupt:\n            # 'child KeyboardInterrupt: close unlink'\n            self._demand_exit()\n            self.__del__()\n        except:\n            print_red(traceback.format_exc(), flush=True)\n            self._demand_exit()\n        self.__del__()\n\n    def _demand_exit(self):\n        self.shared_memory_traffic_light.value = TRAFFIC_LIGHT_ERROR  # CORE! the job is done, waiting for next one\n        self.sem_pull.release()\n\n    # block and wait incoming req \n    def _recv_squence(self): \n        self.sem_push.acquire()\n        assert self.shared_memory_traffic_light.value == TRAFFIC_LIGHT_CHILD_BUSY\n        bufLen = self.shared_memory_io_buffer_len_indicator.value\n        recv_args = pickle.loads(self.shared_memory_io_buffer[:bufLen])\n        recv_args = reverse_opti_numpy_object(recv_args, shm=self.shared_memory_io_buffer)\n        return recv_args\n\n    # return results\n    def _send_squence(self, send_obj):\n        assert self.shared_memory_traffic_light.value == TRAFFIC_LIGHT_CHILD_BUSY\n        # second prepare parameter\n        send_obj, _ = opti_numpy_object(send_obj, shm=self.shared_memory_io_buffer)\n        picked_obj = pickle.dumps(send_obj, protocol=pickle.HIGHEST_PROTOCOL)\n        lenOfObj = len(picked_obj)\n        assert lenOfObj <= REGULAR_BUF_SIZE, ('The non-numpy content size > 0.5MB, please check!', lenOfObj)\n        self.shared_memory_io_buffer_len_indicator.value = lenOfObj\n        self.shared_memory_io_buffer[:lenOfObj] = picked_obj\n        # then light up the work flag, turn off the processed flag\n        self.shared_memory_traffic_light.value = TRAFFIC_LIGHT_CHILD_FREE  # CORE! the job is done, waiting for next one\n        self.sem_pull.release()\n\n    # set traffic IO flag\n    def _set_done(self):\n        self.shared_memory_traffic_light.value = TRAFFIC_LIGHT_CHILD_FREE  # CORE! the job is done, waiting for next one\n        self.sem_pull.release()\n\n\n\n\n\nclass SmartPool(object):\n    \"\"\"\n        Main parallel runner / coodinator\n    \"\"\"\n    # setup and spawn workers\n    def __init__(self, proc_num, fold, base_seed=None):\n        self.proc_num = proc_num\n        self.task_fold = fold\n        self.base_seed = int(numpy.random.rand()*1e5) if base_seed is None else base_seed\n        self.buf_size_limit = SHARE_BUF_SIZE # 10 MB for parameter buffer\n        print_green('Linux multi-env using share memory')\n        setproctitle.setproctitle('HmapRootProcess')\n        self.shared_memory_io_buffer_handle = [shared_memory.SharedMemory(create=True, size=SHARE_BUF_SIZE) for _ in range(proc_num)]\n        self.shared_memory_io_buffer_len_indicator = [RawValue(c_uint32, 0) for _ in range(proc_num)]\n        self.shared_memory_traffic_light = [RawValue(c_uint32, False) for _ in range(proc_num)] # time to work flag\n        self.last_time_response_handled = [True for _ in range(proc_num)] # time to work flag\n        self.semaphore_push = [Semaphore(value=0) for _ in range(proc_num)] # time to work flag\n        self.semaphore_pull = Semaphore(value=0) # time to work flag\n        self.proc_pool = [SuperProc(cnt, smib, smiobli, smtl, SHARE_BUF_SIZE, self.base_seed, \n                                    sem_push, self.semaphore_pull)\n                            for cnt, smib, smiobli, smtl, sem_push in \n                            zip(range(proc_num),\n                                self.shared_memory_io_buffer_handle, self.shared_memory_io_buffer_len_indicator, \n                                self.shared_memory_traffic_light, self.semaphore_push\n                            )]\n        self.shared_memory_io_buffer = [shm.buf for shm in self.shared_memory_io_buffer_handle]\n        self.t_profile = 0\n        for proc in self.proc_pool:\n            # proc.daemon = True\n            proc.start()\n    \n    # add class level targets in each worker\n    def add_target(self, name, lam, args_list=None):\n        lam_list = None\n        if isinstance(lam, list): lam_list = lam\n        # send command for workers to wait appending new target\n        for j in range(self.proc_num):\n            self._send_squence(send_obj=0, target_proc=j)\n        self.notify_all_children()\n        for j in range(self.proc_num): self._wait_done(j)\n\n        for j in range(self.proc_num):\n            tuple_list_to_be_send = []\n            for i in range(self.task_fold):\n                name_fold = name + str(i)\n                args = None if args_list is None else args_list[i + j*self.task_fold]\n                if lam_list is not None: lam = lam_list[i + j*self.task_fold]\n                tuple_list_to_be_send.append((name_fold, lam, args))\n            self._send_squence(send_obj=tuple_list_to_be_send, target_proc=j)\n        self.notify_all_children()\n        for j in range(self.proc_num): self._wait_done(j)\n\n    # run class method in each worker\n    def exec_target(self, name, dowhat, args_list = None, index_list = None, ensure_safe = False):\n\n        if index_list is not None:\n            for j in range(self.proc_num):\n                tuple_list_to_be_send = []\n                for i in range(self.task_fold):\n                    n_thread = i + j*self.task_fold\n                    name_fold = name + str(i)\n                    if n_thread in index_list:\n                        args = None if args_list is None else args_list[index_list.index(n_thread)]\n                        tuple_list_to_be_send.append((name_fold, dowhat, args))\n                    else:\n                        tuple_list_to_be_send.append((name_fold, 'None', 'None'))\n                self._send_squence(send_obj=tuple_list_to_be_send, target_proc=j, ensure_safe=ensure_safe)\n                self.semaphore_push[j].release()\n\n        else: # if index_list is None:\n            for j in range(self.proc_num):\n                tuple_list_to_be_send = []\n                for i in range(self.task_fold):\n                    name_fold = name + str(i)\n                    args = None if args_list is None else args_list[i + j*self.task_fold]\n                    tuple_list_to_be_send.append((name_fold, dowhat, args))\n                self._send_squence(send_obj=tuple_list_to_be_send, target_proc=j, ensure_safe=ensure_safe)\n                self.semaphore_push[j].release()\n        res_sort = self._recv_squence_all()\n        return res_sort\n\n    # low-level send\n    def _send_squence(self, send_obj, target_proc, ensure_safe=False):\n        assert self.last_time_response_handled[target_proc] == True\n        send_obj, shm_pointer = opti_numpy_object(send_obj, shm=self.shared_memory_io_buffer[target_proc])\n        picked_obj = pickle.dumps(send_obj, protocol=pickle.HIGHEST_PROTOCOL)\n        lenOfObj = len(picked_obj)\n        assert lenOfObj <= REGULAR_BUF_SIZE, ('The non-numpy content size > 0.5MB, please check!', lenOfObj)\n        self.shared_memory_io_buffer_len_indicator[target_proc].value = lenOfObj\n        self.shared_memory_io_buffer[target_proc][:lenOfObj] = picked_obj\n        self.last_time_response_handled[target_proc] = False  # then light up the work flag, turn off the processed flag\n        if ensure_safe and shm_pointer != REGULAR_BUF_SIZE:\n            send_obj = reverse_opti_numpy_object(send_obj, shm=self.shared_memory_io_buffer[target_proc])\n        self.shared_memory_traffic_light[target_proc].value = TRAFFIC_LIGHT_CHILD_BUSY  \n\n    # low-level recv\n    def _recv_squence_all(self):\n        res_sort  = [None] * (self.proc_num*self.task_fold)\n        not_ready = [True] * self.proc_num\n        n_acq = 0\n        ready_n = 0\n        while True:\n            self.semaphore_pull.acquire()   # wait child process and OS coordination, it will take a moment\n            n_acq += 1\n            for target_proc, not_r in enumerate(not_ready):\n                if not not_r: continue  # finish already\n                if self.shared_memory_traffic_light[target_proc].value == TRAFFIC_LIGHT_CHILD_BUSY: continue  # not ready\n                if self.shared_memory_traffic_light[target_proc].value == TRAFFIC_LIGHT_ERROR: raise ChildExitException\n                bufLen = self.shared_memory_io_buffer_len_indicator[target_proc].value\n                recv_obj = pickle.loads(self.shared_memory_io_buffer[target_proc][:bufLen])\n                recv_obj = reverse_opti_numpy_object(recv_obj, shm=self.shared_memory_io_buffer[target_proc])\n\n                self.last_time_response_handled[target_proc] = True\n                res_sort[target_proc*self.task_fold: (target_proc+1)*self.task_fold] = recv_obj\n                not_ready[target_proc] = False\n                ready_n += 1\n\n            if ready_n == self.proc_num:\n                break\n\n        for _ in range(self.proc_num-n_acq):\n            self.semaphore_pull.acquire()  # clear semaphore_pull\n        return res_sort\n\n\n    # low-level wait\n    def _wait_done(self, target_proc):   # used only in add_target\n        self.semaphore_pull.acquire()\n        if self.shared_memory_traffic_light[target_proc].value == TRAFFIC_LIGHT_ERROR: raise ChildExitException\n        self.last_time_response_handled[target_proc] = True\n\n    # let all workers know about incomming req\n    def notify_all_children(self):\n        for j in range(self.proc_num): \n            self.semaphore_push[j].release()  # notify all child process\n\n    # exit and clean up carefully\n    def party_over(self):\n        self.__del__()\n\n    # exit and clean up carefully\n    def __del__(self):\n        if hasattr(self, 'terminated'): \n            return\n            \n        # traceback.print_exc()\n        print_green('[shm_pool]: executing superpool del')\n\n        try:\n            for i in range(self.proc_num): self._send_squence(send_obj=-1, target_proc=i)\n            self.notify_all_children()\n            # print('[shm_pool]: self.notify_all_children()')\n        except: pass\n\n        # print('[shm_pool]: shm.close(); shm.unlink()')\n        for shm in self.shared_memory_io_buffer_handle:\n            try: shm.close(); shm.unlink()\n            except: pass\n\n        N_SEC_WAIT = 2\n        for i in range(N_SEC_WAIT):\n            print_red('[shm_pool]: terminate in %d'%(N_SEC_WAIT-i));time.sleep(1)\n\n        # kill shm_pool's process tree\n        # print_red('[shm_pool]: kill_process_and_its_children(proc)')\n        for proc in self.proc_pool: \n            try: kill_process_and_its_children(proc)\n            except Exception as e: pass # print_red('[shm_pool]: error occur when kill_process_and_its_children:\\n', e)\n\n        print_green('[shm_pool]: __del__ finish')\n        self.terminated = True\n\n# To compat Windows, redirect to pipe solution\nif not platform.system()==\"Linux\":  \n    from UTIL.win_pool import SmartPool\n    "
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/sync_exp.py",
    "content": "import torch, time\nimport pickle, os\n\nfrom UTIL.colorful import print亮红\nfrom .tensor_ops import __hash__\nfrom UTIL.exp_helper import singleton\n\n@singleton\nclass SynWorker:\n\n    def __init__(self, mod) -> None:\n        self.sychronize_FILE_hashdict = 'TEMP/sychronize_hashdict'\n        self.sychronize_FILE_cnt = 'TEMP/sychronize_cnt'\n        self.mod = mod\n        self.sychronize_internal_hashdict = {}\n        self.sychronize_internal_cnt = {}\n        self.follow_cnt = {}\n        print亮红('warning, SynWorker init, mod is', mod)\n        time.sleep(5)\n        if mod == 'follow':\n            with open(self.sychronize_FILE_hashdict, 'rb') as f:\n                self.sychronize_internal_hashdict = pickle.load(f)\n            with open(self.sychronize_FILE_cnt, 'rb') as f:\n                self.sychronize_internal_cnt = pickle.load(f)\n        else:\n            try:\n                os.remove(self.sychronize_FILE_hashdict)\n                os.remove(self.sychronize_FILE_cnt)\n            except: pass\n\n    def dump_sychronize_data(self):\n        if self.mod == 'follow':\n            return\n        with open(self.sychronize_FILE_hashdict, 'wb+') as f:\n            pickle.dump(self.sychronize_internal_hashdict, f)\n        with open(self.sychronize_FILE_cnt, 'wb+') as f:\n            pickle.dump(self.sychronize_internal_cnt, f)\n\n    def sychronize_experiment(self, key, data, reset_when_close=False):\n        if self.mod == 'lead':\n            hash_code = __hash__(data)\n            if key not in self.sychronize_internal_hashdict:\n                self.sychronize_internal_cnt[key] = 0\n                self.sychronize_internal_hashdict[key] = [\n                    {\n                        'hash_code':hash_code,\n                        'data': data,\n                    }\n                    ,\n                ]\n            else:\n                self.sychronize_internal_hashdict[key].append({\n                        'hash_code':hash_code,\n                        'data': data,\n                })\n\n            self.sychronize_internal_cnt[key] += 1\n\n        if self.mod == 'follow':\n            hash_code = __hash__(data)\n            if key not in self.follow_cnt:\n                self.follow_cnt[key] = 0\n\n            if hash_code != self.sychronize_internal_hashdict[key][self.follow_cnt[key]]['hash_code']:\n                if not (torch.isclose(self.sychronize_internal_hashdict[key][self.follow_cnt[key]]['data'],data).all()) or (not isinstance(data, torch.Tensor)):\n                    print('%s: error expected hash: %s, get hash %s, data %s'%(key,\n                        self.sychronize_internal_hashdict[key][self.follow_cnt[key]]['hash_code'],\n                        hash_code,\n                        str(data)\n                    ))\n                else:\n                    print('%s: error expected hash, but very very close (<1e-5)'%key)\n                    if reset_when_close:\n                        return data\n            self.follow_cnt[key] += 1"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/tensor_ops.py",
    "content": "import copy, json\nimport numpy as np\nfrom functools import lru_cache\n\ntry:\n    import torch\n    import torch.nn.functional as F\nexcept:\n    print('warning, pytorch not installed!')\n    print('警告, 没有安装pytorch, 所有pytorch相关函数不可用!')\n    class torch():\n        Tensor = Exception\nfrom functools import wraps\n\n\nclass ConfigCache(object):\n    def __init__(self) -> None:\n        super().__init__()\n        self.init = False\n\n    def read_cfg(self):\n        from config import GlobalConfig\n        if GlobalConfig.cfg_ready:\n            self.device_ = GlobalConfig.device\n            self.use_float64_ = GlobalConfig.use_float64\n            self.init = True\n\n    @property\n    def device(self):\n        if not self.init: self.read_cfg()\n        assert self.init, ('cuda_cfg not ready!')\n        return self.device_\n\n    @property\n    def use_float64(self):\n        if not self.init: self.read_cfg()\n        assert self.init, ('cuda_cfg not ready!')\n        return self.use_float64_\n\ncuda_cfg = ConfigCache()\n\ndef pt_inf():\n    # if not cuda_cfg.init: cuda_cfg.read_cfg()\n    pt_dtype = torch.float64 if cuda_cfg.use_float64 else torch.float32\n    return torch.tensor(np.inf, dtype=pt_dtype, device=cuda_cfg.device)\n\ndef pt_nan():\n    # if not cuda_cfg.init: cuda_cfg.read_cfg()\n    pt_dtype = torch.float64 if cuda_cfg.use_float64 else torch.float32\n    return torch.tensor(np.nan, dtype=pt_dtype, device=cuda_cfg.device)\n\n\ndef vis_mat(mat):\n    mat = mat.astype(np.float)\n    mat = mat - mat.min()\n    mat = mat / mat.max()\n    import matplotlib.pyplot as plt\n    import matplotlib.image as mpimg\n\n    imgplot = plt.imshow(mat)\n    plt.xlabel(\"cols, 2rd dim\")\n    plt.ylabel(\"lines, 1st dim\")\n    plt.show()\n\n\n\"\"\"\n    improve torch.repeat / torch.expand function\n    eg.1    x.shape = (4, 5, 6, 7); insert_dim = -1; n_times=666\n            y = repeat_at(x, insert_dim, n_times)\n            y.shape = (4, 5, 6, 7, 666)\n\n    eg.2    x.shape = (4, 5, 6, 7); insert_dim = +1; n_times=666\n            y = repeat_at(x, insert_dim, n_times)\n            y.shape = (4, 666, 5, 6, 7)\n\"\"\"\ndef repeat_at(tensor, insert_dim, n_times, copy_mem=False):\n    if not isinstance(tensor, torch.Tensor):\n        return np_repeat_at(tensor, insert_dim, n_times)\n    tensor = tensor.unsqueeze(insert_dim)\n    shape = list(tensor.shape)\n    assert shape[insert_dim] == 1\n    shape[insert_dim] = n_times\n    if copy_mem: tensor.repeat(*shape)\n    return tensor.expand(*shape)\n\ndef np_repeat_at(array, insert_dim, n_times):\n    array = np.expand_dims(array, insert_dim)\n    return array.repeat(axis=insert_dim, repeats=n_times)\n\n\ndef copy_clone(x):\n    if x is None:\n        return None\n    return (\n        x.clone()\n        if hasattr(x, \"clone\")\n        else x.copy()\n        if hasattr(x, \"copy\")\n        else copy.deepcopy(x)\n    )\n\n\n\"\"\"\n    improve np.reshape and torch.view function\n    If a dim is assigned with 0, it will keep its original dimension\n    eg.1    x.shape = (4, 5, 6, 7); new_shape = [0, 0, -1]\n            y = my_view(x, new_shape)\n            y.shape = (4, 5, 6*7)\n\n    eg.2    x.shape = (4, 5, 6, 7); new_shape = [-1, 0, 0]\n            y = my_view(x, new_shape)\n            y.shape = (4*5, 6, 7)\n\n    eg.3    x.shape = (4, 5, 6); new_shape = [0, 0, -1, 3]\n            y = my_view(x, new_shape)\n            y.shape = [4, 5, 2, 3]\n\n    eg.4    x.shape = (3, 4, 5, 6); new_shape = [0, 2, -1, 0, 0]\n            y = my_view(x, new_shape)\n            y.shape = [3, 2, 2, 5, 6]\n\n    eg.5    x.shape = (32, 10, 24); new_shape = [32, 10, 24, 1]\n            y = my_view(x, new_shape)\n            y.shape = [32, 10, 24, 1]\n\n    Error eg.1\n            x.shape = (3, 4, 5, 6); new_shape = [0, 2, 0, -1, 0]\n            Error: 2(!=4) and -1 must stick together!\n            Fix 1: new_shape   = [0,  2,  2,  0,  0]\n            Fix 2: new_shape   = [0,  2, -1,  0,  0]\n            Fix 3: new_shape   = [0, -1,  2,  0,  0]\n            After Fix: y.shape = [3,  2,  2,  5,  6]\n            \n    Error eg.2\n            x.shape = (3, 4, 5, 6); new_shape = [12, 0, -1]\n            Error: 12(!=3) and -1 must stick together!\n            Fix 1: new_shape   = [12,  0, 0]\n            Fix 2: new_shape   = [12, -1, 6]\n            Fix 3: new_shape   = [12, -1, 0]\n            Fix 4: new_shape   = [-1,  0, 0]\n            After Fix: y.shape = [12,  5, 6]\n\"\"\"\ndef my_view(x, shape):\n    # fill both way until meet -1 \n    for i, dim in enumerate(shape):\n        if dim == 0: shape[i] = x.shape[i]\n        elif dim == -1: break\n        elif i >= len(x.shape): break  # prevent x.shape[i] out of range\n        elif dim != x.shape[i]: break\n    for i in range(len(shape)):\n        if i >= len(x.shape): break # prevent x.shape[ni] out of range\n        ni = -(i + 1)\n        dim = shape[ni]\n        if dim == 0: shape[ni] = x.shape[ni]\n        elif dim == -1: break\n\n    # print(shape)\n    if isinstance(x, np.ndarray):\n        return x.reshape(*shape)\n    return x.view(*shape)\n\n\ndef add_onehot_id_at_last_dim(x):\n    if isinstance(x, np.ndarray):\n        return np_add_onehot_id_at_last_dim(x)\n    _hot_dim = x.shape[-2]\n    _identity = torch.tile(torch.eye(_hot_dim, device=x.device), (*x.shape[:-2], 1, 1))\n    return torch.cat((x, _identity), -1)\n\ndef np_add_onehot_id_at_last_dim(x):\n    _hot_dim = x.shape[-2]\n    _identity = np.tile(np.eye(_hot_dim), (*x.shape[:-2], 1, 1))\n    return np.concatenate((x, _identity), -1)\n\n\n# x. shape = (..., core_dim)\n# agent_ids.shape = (..., null)\n# output. shape = (..., core_dim+fixlen)\ndef add_onehot_id_at_last_dim_fixlen(x, fixlen, agent_ids):\n    if agent_ids is None:\n        return add_onehot_id_at_last_dim(x)\n    # if isinstance(x, np.ndarray):\n        # return np_add_onehot_id_at_last_dim_fixlen(x, fixlen)\n    # manually control output vector length\n    # or\n    # adjust output vector length according to -2 dim\n    _identity = torch.eye(fixlen, device=x.device)[agent_ids]\n    return torch.cat((x, _identity), -1)\n\n# def np_add_onehot_id_at_last_dim_fixlen(x, fixlen, agent_ids):\n#     _identity = np.tile(np.eye(fixlen), (*x.shape[:-2], 1, 1))\n#     return np.concatenate((x, _identity[..., :x.shape[-2], :]), -1)\n\n\n\n\n\"\"\"\n    numpy corresponding to torch.nn.functional.one_hot\n    x is array, e.g. x = [4,2,3,1]\n    n is int, e.g. n=5\n    >> np_one_hot( np.array([4,2,3,1]), n=5)\n    np.array([\n        [0,0,0,0,1],\n        [0,0,1,0,0],\n        [0,0,0,1,0],\n        [0,1,0,0,0],\n    ])\n\n\"\"\"\ndef np_one_hot(x, n):\n    return np.eye(n)[x]\n\n\ndef add_obs_container_subject(container_emb, subject_emb, div):\n    # for subject, add one-hot embedding of its group\n    n_container = container_emb.shape[1]\n    subject_belonging_info = np_one_hot(div, n_container)\n    subject_out_emb = np.concatenate((subject_emb, subject_belonging_info), -1)\n    # for container, add add multi-hot embedding of its subjects\n    container_multihot = np.concatenate(\n        [np.expand_dims((div == nth_container).astype(np.long), 1) \n            for nth_container in range(n_container)],\n        1,\n    )\n    container_out_emb = np.concatenate((container_emb, container_multihot), -1)\n    return container_out_emb, subject_out_emb\n\n\ndef MayGoWrong(f):\n    @wraps(f)\n    def decorated(*args, **kwargs):\n        try:\n            return f(*args, **kwargs)\n        except:\n            print('going wrong!')\n            return f(*args, **kwargs)\n\n    return decorated\n\n\ndef dummy_decorator(f=None):\n    if callable(f):\n        @wraps(f)\n        def decorated(*args, **kwargs):\n            return f(*args, **kwargs)\n        return decorated\n    else:\n        def actual_decorator(func):\n            @wraps(func)\n            def wrapper(*args, **kwargs):\n                return func(*args, **kwargs)\n            return wrapper\n        return actual_decorator\n\n\"\"\"\n    Function decorate, \n    Turning numpy array to torch.Tensor, then put it on the right GPU / CPU\n\"\"\"\ndef Args2tensor(f):\n    # if not cuda_cfg.init: cuda_cfg.read_cfg()\n    def _2tensor(x):\n        if isinstance(x, torch.Tensor):\n            return x.to(cuda_cfg.device)\n        elif isinstance(x, np.ndarray):\n            if (not cuda_cfg.use_float64) and x.dtype == np.float64:\n                x = x.astype(np.float32)\n            if cuda_cfg.use_float64 and x.dtype == np.float32:\n                x = x.astype(np.float64)\n            return torch.from_numpy(x).to(cuda_cfg.device)\n        elif isinstance(x, dict):\n            y = {}\n            for key in x:\n                y[key] = _2tensor(x[key])\n            return y\n        else:\n            return x\n\n    @wraps(f)\n    def decorated(*args, **kwargs):\n        for key in kwargs:\n            kwargs[key] = _2tensor(kwargs[key])\n        return f(*(_2tensor(arg) for arg in args), **kwargs)\n\n    return decorated\n\n\n\n\ndef Return2numpy(f):\n\n    def _2cpu2numpy(x):\n        return (\n            None\n            if x is None\n            else x\n            if not isinstance(x, torch.Tensor)\n            else x.detach().cpu().numpy()\n            if x.requires_grad\n            else x.cpu().numpy()\n        )\n\n    @wraps(f)\n    def decorated(*args, **kwargs):\n        ret_tuple = f(*args, **kwargs)\n        if isinstance(ret_tuple, tuple):\n            return (_2cpu2numpy(ret) for ret in ret_tuple)\n        else:\n            return _2cpu2numpy(ret_tuple)\n\n    return decorated\n\n\n\"\"\"\n    Function decorate, \n    Turning numpy array to torch.Tensor, then put it on the right GPU / CPU,\n    When returning, convert all torch.Tensor to numpy array\n\"\"\"\ndef Args2tensor_Return2numpy(f):\n    def _2tensor(x):\n        if isinstance(x, torch.Tensor):\n            return x.to(cuda_cfg.device)\n        elif isinstance(x, np.ndarray) and x.dtype != 'object':\n            if (not cuda_cfg.use_float64) and x.dtype == np.float64:\n                x = x.astype(np.float32)\n            if cuda_cfg.use_float64 and x.dtype == np.float32:\n                x = x.astype(np.float64)\n            return torch.from_numpy(x).to(cuda_cfg.device)\n        elif isinstance(x, dict):\n            y = {}\n            for key in x:\n                y[key] = _2tensor(x[key])\n            return y\n        else:\n            return x\n\n    def _2cpu2numpy(x):\n        return (\n            None\n            if x is None\n            else x\n            if not isinstance(x, torch.Tensor)\n            else x.detach().cpu().numpy()\n            if x.requires_grad\n            else x.cpu().numpy()\n        )\n\n    @wraps(f)\n    def decorated(*args, **kwargs):\n        for key in kwargs:\n            kwargs[key] = _2tensor(kwargs[key])\n        ret_tuple = f(*(_2tensor(arg) for arg in args), **kwargs)\n        if not isinstance(ret_tuple, tuple):\n            return _2cpu2numpy(ret_tuple)\n        return (_2cpu2numpy(ret) for ret in ret_tuple)\n\n    return decorated\n\n\n\"\"\"\n    Turning torch.Tensor to numpy array, put it on CPU,\n\"\"\"\n\n\ndef _2cpu2numpy(x):\n    return (\n        None\n        if x is None\n        else x\n        if not isinstance(x, torch.Tensor)\n        else x.detach().cpu().numpy()\n        if x.requires_grad\n        else x.cpu().numpy()\n    )\n\n\n\"\"\"\n    Convert torch.Tensor to numpy array.\n    Turning numpy array to torch.Tensor, then put it on the right GPU / CPU.\n\"\"\"\n\n\ndef _2tensor(x):\n    # if not cuda_cfg.init: cuda_cfg.read_cfg()\n    if isinstance(x, torch.Tensor):\n        return x.to(cuda_cfg.device)\n    elif isinstance(x, np.ndarray):\n        if (not cuda_cfg.use_float64) and x.dtype == np.float64:\n            x = x.astype(np.float32)\n        if cuda_cfg.use_float64 and x.dtype == np.float32:\n            x = x.astype(np.float64)\n        return torch.from_numpy(x).to(cuda_cfg.device)\n    elif isinstance(x, dict):\n        y = {}\n        for key in x:\n            y[key] = _2tensor(x[key])\n        return y\n    elif isinstance(x, torch.nn.Module):\n        x.to(cuda_cfg.device)\n        return x\n    else:\n        return x\n\n\"\"\"\n    Stack an array whose elements with different len, pad empty place with with NaN\n\"\"\"\n\ndef pad_vec_array(arr_list, max_len):\n    # init to NaNs\n    res = np.zeros(shape=(len(arr_list), max_len), dtype=np.double) + np.nan\n    for i in range(len(arr_list)):\n        if arr_list[i] is None:\n            continue\n        res[i, : len(arr_list[i])] = arr_list[i]\n    return res\n\n\ndef one_hot_with_nan_np(tensr, num_classes):\n    tensr = tensr.copy()\n    tensr[np.isnan(tensr)] = num_classes\n    Res_1MoreCol = np_one_hot(tensr.astype(np.long), num_classes + 1)\n    return Res_1MoreCol[..., :-1]\n\n\ndef one_hot_with_nan(tensr, num_classes):\n    if isinstance(tensr, np.ndarray):\n        return one_hot_with_nan_np(tensr, num_classes)\n    tensr = tensr.clone()\n    tensr[torch.isnan(tensr)] = num_classes\n    Res_1MoreCol = F.one_hot(tensr.long(), num_classes + 1)\n    return Res_1MoreCol[..., :-1]\n\n\ndef scatter_with_nan(tensr, num_classes, out_type=\"binary\"):\n    res = one_hot_with_nan(tensr, num_classes)\n    res = res.sum(-2)\n    if out_type == \"bool\":\n        res = res != 0\n    return res\n\n\"\"\"\n    Not used anymore\n\"\"\"\ndef process_space(space):\n    # starcraft 环境无须特殊处理\n    if not (\"Box\" in space[\"obs_space\"] or \"Discrete\" in space[\"act_space\"]):\n        return space\n\n    # 其他环境需要进行格式转换\n    import re\n\n    obs_dim = int(\n        re.findall(\n            re.compile(r\"Box[(]-inf, inf, [(](.*?)[,)]\", re.S), space[\"obs_space\"]\n        )[0]\n    )\n    print(space[\"obs_space\"])\n    space_ = {}\n    space_[\"obs_space\"] = {}\n    space_[\"act_space\"] = {}\n    space_[\"obs_space\"][\"state_shape\"] = 8\n    space_[\"obs_space\"][\"obs_shape\"] = obs_dim\n    space_[\"act_space\"][\"n_actions\"] = 8\n    space_[\"obs_space\"] = str(space_[\"obs_space\"])\n    space_[\"act_space\"] = str(space_[\"act_space\"])\n    return space_\n\n\"\"\"\n    Not used anymore\n\"\"\"\nclass Policy_shift_observer(object):\n    def __init__(self, act_range, act_num):\n        self.act_range = act_range  # 15\n        self.act_num = act_num  # 3\n        self.act_cnt_array = np.zeros(shape=(act_num, act_range))\n        self.rate = None\n        self.rate_history = None\n\n    def new_sample(self, act):\n        act_rec = act.shape[0]\n        for act_index in range(self.act_num):\n            for act_nth in range(self.act_range):\n                self.act_cnt_array[act_index, act_nth] = torch.sum(\n                    (act[:, act_index] == act_nth).long()\n                )\n        self.rate = self.act_cnt_array / act_rec\n        if self.rate_history is None:\n            self.rate_history = self.rate\n        else:\n            self.rate_history = self.rate_history * 0.9 + self.rate * 0.1\n        print(\"rate\", self.rate)\n        # conclusion: the action distribution is not reinforced because the rewards signal is too weak.\n\n\n\"\"\"\n    Get the hash code string of an array, \n    compatable for numpy array and torch.tensor\n\"\"\"\ndef __hash__(x):\n    import hashlib\n    md5 = hashlib.md5()  # ignore\n    # if isinstance(x, str):\n    #     md5.update(x)\n    #     return md5.hexdigest()\n    if hasattr(x, \"cpu\"):\n        md5.update(x.detach().cpu().numpy().data.tobytes())\n        return md5.hexdigest()\n    elif hasattr(x, \"numpy\"):\n        md5.update(x.numpy().data.tobytes())\n        return md5.hexdigest()\n    elif hasattr(x, \"data\"):\n        md5.update(x.data.tobytes())\n        return md5.hexdigest()\n    else:\n        try:\n            md5.update(x.encode(\"utf-8\"))\n            return md5.hexdigest()\n        except:\n            return str(x)\n\n\ndef __hashm__(*args):\n    import hashlib\n\n    md5 = hashlib.md5()  # ignore\n    for arg in args:\n        x = arg\n        if hasattr(x, \"cpu\"):\n            md5.update(x.detach().cpu().numpy().data.tobytes())\n        elif hasattr(x, \"numpy\"):\n            md5.update(x.numpy().data.tobytes())\n        elif hasattr(x, \"data\"):\n            md5.update(x.data.tobytes())\n        else:\n            try:\n                md5.update(x.encode(\"utf-8\"))\n            except:\n                md5.update(str(x).encode(\"utf-8\"))\n    return md5.hexdigest()\n\n\n\"\"\"\n    Get the hash code string of the pytorch network parameters\n    eg. \n        __hashn__(mlp_module.parameters())\n\"\"\"\ndef __hashn__(generator):\n    import hashlib\n\n    md5 = hashlib.md5()  # ignore\n    for arg in generator:\n        x = arg.data\n        if hasattr(x, \"cpu\"):\n            md5.update(x.detach().cpu().numpy().data.tobytes())\n        elif hasattr(x, \"numpy\"):\n            md5.update(x.numpy().data.tobytes())\n        elif hasattr(x, \"data\"):\n            md5.update(x.data.tobytes())\n        else:\n            try:\n                md5.update(x.encode(\"utf-8\"))\n            except:\n                md5.update(str(x).encode(\"utf-8\"))\n    return md5.hexdigest()\n\n\n\"\"\"\n    numpy version of softmax\n\"\"\"\n\n\ndef np_softmax(x, axis=None):\n    # compute in log space for numerical stability\n    return np.exp(x - logsumexp(x, axis=axis, keepdims=True))\n\n\n\"\"\"\n    numpy version of logsumexp\n\"\"\"\n\n\ndef logsumexp(a, axis=None, keepdims=False, return_sign=False):\n    a_max = np.amax(a, axis=axis, keepdims=True)\n    if a_max.ndim > 0:\n        a_max[~np.isfinite(a_max)] = 0\n    elif not np.isfinite(a_max):\n        a_max = 0\n    tmp = np.exp(a - a_max)\n    # suppress warnings about log of zero\n    with np.errstate(divide=\"ignore\"):\n        s = np.sum(tmp, axis=axis, keepdims=keepdims)\n        if return_sign:\n            sgn = np.sign(s)\n            s *= sgn  # /= makes more sense but we need zero -> zero\n        out = np.log(s)\n    if not keepdims:\n        a_max = np.squeeze(a_max, axis=axis)\n    out += a_max\n    if return_sign:\n        return out, sgn\n    else:\n        return out\n\n\n\"\"\"\n函数说明：在有限的、不均衡的多标签数据集中，按照预设的比例，取出尽可能多的样本\n\"\"\"\n\n\ndef sample_balance(x, y, n_class, weight=None):\n    if weight is None:\n        weight = torch.ones(n_class, device=x.device)\n    else:\n        weight = torch.Tensor(weight).to(x.device)\n    n_instance = torch.zeros(n_class, device=x.device)\n    indices = [None] * n_class\n    for i in range(n_class):\n        indices[i] = torch.where(y == i)[0]\n        n_instance[i] = len(indices[i])\n    ratio = n_instance / weight\n    bottle_neck = torch.argmin(n_instance / weight)\n    r = ratio[bottle_neck]\n    n_sample = (r * weight).long()\n    # print(n_instance, n_sample)\n    new_indices = [indices[i][torch.randperm(n_sample[i])] for i in range(n_class)]\n    # print(new_indices)\n    new_indices_ = torch.cat(new_indices)\n    assert len(new_indices_) == sum(n_sample)\n    return x[new_indices_], y[new_indices_]\n\n\n\"\"\"\n    gather tensor with index, \n    regarding all right hand dimensions as dimensions need to be gathered\n    eg.1\n        src = torch.Tensor([[[ 0,  1,  2], [ 3,  4,  5]],\n                            [[ 6,  7,  8], [ 9, 10, 11]],\n                            [[12, 13, 14], [15, 16, 17]]])\n        index = torch.Tensor([[0], [1], [0]])\n        src.shape = (3, 2, 3)\n        src.shape = (3, 1)\n        >> res = gather_righthand(src,index)\n        res.shape = (3, 1, 3)\n        res= tensor([[[ 0.,  1.,  2.]],\n                     [[ 9., 10., 11.]],\n                     [[12., 13., 14.]]])\n    eg.2\n        src.shape   = (64, 16, 8, 88, 888)\n        index.shape = (64, 5)\n        >> res = gather_righthand(src,index)\n        res.shape   = (64, 5,  8, 88, 888)\n\n    eg.3\n        src.shape   = (64,  16,  88, 888)\n        index.shape = (64, 777)\n        >> res = gather_righthand(src,index)\n        res.shape   = (64, 777,  88, 888)\n\n\"\"\"\ndef gather_righthand(src, index, check=True):\n    if not isinstance(src, torch.Tensor):\n        return np_gather_righthand(src, index, check)\n    index = index.long()\n    i_dim = index.dim()\n    s_dim = src.dim()\n    t_dim = i_dim - 1\n    if check:\n        assert s_dim >= i_dim\n        assert index.max() <= src.shape[t_dim] - 1\n        if index.max() != src.shape[t_dim] - 1:\n            print(\n                \"[gather_righthand] warning, index max value does not match src target dim\"\n            )\n        assert (\n            src.shape[t_dim] != index.shape[t_dim]\n        ), \"Do you really want to select %d item out of %d?? If so, please set check=False.\" % (\n            index.shape[t_dim],\n            src.shape[t_dim],\n        )\n        for d in range(0, t_dim):\n            assert src.shape[d] == index.shape[d]\n    index_new_shape = list(src.shape)\n    index_new_shape[t_dim] = index.shape[t_dim]\n    for _ in range(i_dim, s_dim):\n        index = index.unsqueeze(-1)\n    index_expand = index.expand(index_new_shape)  # only this two line matters\n    return torch.gather(\n        src, dim=t_dim, index=index_expand\n    )  # only this two line matters\n\n\n\n\n\"\"\"\n    numpy version of 'gather_righthand'\n\"\"\"\ndef np_gather_righthand(src, index, check=True):\n    index = index.astype(np.long)\n    dim = lambda x: len(x.shape)\n    i_dim = dim(index)\n    s_dim = dim(src)\n    t_dim = i_dim - 1\n    if check:\n        assert s_dim >= i_dim\n        assert index.max() <= src.shape[t_dim] - 1, (\"\\tindex.max()=\", index.max(), \"\\tsrc.shape[t_dim]-1=\", src.shape[t_dim] - 1)\n        if index.max() != src.shape[t_dim] - 1:\n            print(\n                \"[gather_righthand] warning, index max value does not match src target dim\"\n            )\n        assert (\n            src.shape[t_dim] != index.shape[t_dim]\n        ), \"you really want to select %d item out of %d?\" % (\n            index.shape[t_dim],\n            src.shape[t_dim],\n        )\n        for d in range(0, t_dim):\n            assert src.shape[d] == index.shape[d]\n    tile_shape = np.array(src.shape)  # warning: careful when moving to pytorch\n    tile_shape[: (t_dim + 1)] = 1\n    for _ in range(i_dim, s_dim):\n        index = np.expand_dims(index, -1)\n    index_expand = np.tile(\n        index, tile_shape\n    )  # index.expand(index_new_shape)            # only this two line matters\n    return np.take_along_axis(arr=src, indices=index_expand, axis=t_dim)\n    # return torch.gather(src, dim=t_dim, index=index_expand) # only this two line matters\n\n\"\"\"\n    reverse operation of 'gather_righthand'\n\"\"\"\ndef scatter_righthand(scatter_into, src, index, check=True):\n    index = index.long()\n    i_dim = index.dim()\n    s_dim = src.dim()\n    t_dim = i_dim - 1\n    index_new_shape = list(src.shape)\n    index_new_shape[t_dim] = index.shape[t_dim]\n    for _ in range(i_dim, s_dim):\n        index = index.unsqueeze(-1)\n    index_expand = index.expand(index_new_shape)  # only this two line matters\n    return scatter_into.scatter(t_dim, index_expand, src)\n\n\n\"\"\"\n    calculate distance matrix between two position vector array A and B, support 3d and 2d\n    test >>\n    A = np.array([  [0,0],\n                    [1,1],])\n    B = np.array([  [0,-1],\n                    [1, 0],\n                    [0, 1],])\n    distance_mat_between(A, B) == [ [ 1 1 1  ], [sqrt(5), 1, 1 ]] => shape = (2,3)\n\"\"\"\ndef distance_mat_between(A, B):\n    n_subject_a = A.shape[-2]  # A (64, 3)\n    n_subject_b = B.shape[-2]  # B (28, 3)\n    A = np.repeat(np.expand_dims(A, -2), n_subject_b, axis=-2)  # =>(64, 28, 3)\n    B = np.repeat(np.expand_dims(B, -2), n_subject_a, axis=-2)  # =>(28, 64, 3)\n    B = np.swapaxes(B, -2, -3)  # =>(64, 28, 3)\n    dis = A - B  # =>(64, 100, 100, 2)\n    dis = np.linalg.norm(dis, axis=-1)\n    return dis\n\n\n\"\"\"\n    calculate distance matrix for a position vector array A, support 3d and 2d\n\"\"\"\ndef distance_matrix(A):\n    n_subject = A.shape[-2]  # is 2\n    A = np.repeat(np.expand_dims(A, -2), n_subject, axis=-2)  # =>(64, 100, 100, 2)\n    At = np.swapaxes(A, -2, -3)  # =>(64, 100, 100, 2)\n    dis = At - A  # =>(64, 100, 100, 2)\n    dis = np.linalg.norm(dis, axis=-1)\n    return dis\n\n\"\"\"\n    calculate delta matrix for a position vector array A\n\"\"\"\ndef delta_matrix(A):\n    n_subject = A.shape[-2]  # is 2\n    A = np.repeat(np.expand_dims(A, -2), n_subject, axis=-2)  # =>(64, 100, 100, 2)\n    At = np.swapaxes(A, -2, -3)  # =>(64, 100, 100, 2)\n    delta = At - A  # =>(64, 100, 100, 2)\n    return delta\n\ndef np_normalize_last_dim(mat):\n    return mat / np.expand_dims(np.linalg.norm(mat, axis=-1) + 1e-16, axis=-1)\n\ndef dir2rad_old(delta_pos):\n    result = np.empty(delta_pos.shape[:-1], dtype=complex)\n    result.real = delta_pos[..., 0]\n    result.imag = delta_pos[..., 1]\n    rad_angle = np.angle(result)\n    # assert (dir2rad_new(delta_pos)==rad_angle).all()\n    return rad_angle\n\n\"\"\"\n    arctan2, but support any batch\n\"\"\"\ndef dir2rad(delta_pos):\n    return np.arctan2(delta_pos[..., 1], delta_pos[..., 0])\n\n\ndef dir3d_rad(delta_pos):\n    assert delta_pos.shape[-1]==3\n    xy = delta_pos[..., :2]\n    r1 = dir2rad(xy)\n    xy_norm = np.linalg.norm(xy, axis=-1)\n    r2 = dir2rad(np.stack((xy_norm, delta_pos[..., 2]),-1))\n    return np.stack((r1,r2), axis=-1)\n    \n\ndef reg_deg(deg):\n    return (deg + 180) % 360 - 180\n\n# make angles comparable\ndef reg_deg_at(rad, ref):\n    return reg_deg(rad-ref) + ref\n\ndef reg_rad(rad):\n    # it's OK to show \"RuntimeWarning: invalid value encountered in remainder\"\n    return (rad + np.pi) % (2 * np.pi) - np.pi\n\n# make angles comparable\ndef reg_rad_at(rad, ref):\n    return reg_rad(rad-ref) + ref\n\n# the average of two angles (in rad)\ndef avg_rad(rad1, rad2):\n    return reg_rad_at(rad1, rad2)/2 + rad2/2\n\ndef zeros_like_except_dim(array, except_dim, n):\n    shape_ = list(array.shape)\n    shape_[except_dim] = n\n    return torch.zeros(size=shape_, device=array.device, dtype=array.dtype)\n\n\ndef pad_at_dim(array, dim, n):\n    extra_n = n-array.shape[dim]\n    padding = zeros_like_except_dim(array, except_dim=dim, n=extra_n)\n    return torch.cat((array, padding), axis=dim)\n\ndef stack_vec_with_padding(arr_list):\n    _len = [arr.len() for arr in arr_list]\n    max_len = max(_len)\n    n_subject = arr_list.len()\n    dtype = arr_list[0].dtype\n    arr_np = np.zeros(shape=(n_subject, max_len), dtype=dtype)\n    for i, arr in enumerate(arr_list):\n        arr_np[i,:_len[i]] = arr\n    return arr_np\n\n\ndef objdump(obj):\n    import pickle\n    with open('objdump.tmp', 'wb+') as f:\n        pickle.dump(obj, f)\n    return\n\ndef objload():\n    import pickle, os\n    if not os.path.exists('objdump.tmp'): \n        return\n    with open('objdump.tmp', 'rb') as f:\n        return pickle.load(f)\n\ndef stack_padding(l, padding=np.nan):\n    max_len = max([t.shape[0] for t in l])\n    shape_desired = (len(l), max_len, *(l[0].shape[1:]))\n    target = np.zeros(shape=shape_desired, dtype=float) + padding\n    for i in range(len(l)): target[i, :len(l[i])] = l[i]\n    return target\n\ndef n_item(tensor):\n    n = 1\n    for d in tensor.shape:\n        n = n*d\n    return n\n\n\n\n\ndef cat_last_dim(tensor, cat):\n    assert tensor.shape[-1] >= cat.shape[-1]\n    for i, s in enumerate(tensor.shape[:-1]):\n        if s!=cat.shape[i]:\n            cat = repeat_at(cat, i, s)\n    cat = tensor[..., :cat.shape[-1]] * 0 + cat\n    return torch.cat((tensor, cat), -1)\n\n\"\"\"\n    input: [25, 25]\n    output: [ range(0,25), range(25,50) ]\n\"\"\"\n# @lru_cache(10)\ndef arrange_id(N_AGENT_EACH_TEAM):\n    AGENT_ID_EACH_TEAM_cv = []\n    begin = 0\n    for _, n in enumerate(N_AGENT_EACH_TEAM):\n        b = begin\n        s = begin + n\n        AGENT_ID_EACH_TEAM_cv.append(range(b, s))\n        begin = s\n    return AGENT_ID_EACH_TEAM_cv\n\n\"\"\"\n    convert digit to binary\n    >> get_binary(3, 8)\n    np.array([  1,1,0,0,  0,0,0,0   ])\n\"\"\"\n@lru_cache(500)\ndef get_binary(n:int, n_bits:int, dtype=np.float32):\n    arr = np.zeros(n_bits, dtype=dtype)\n    pointer = 0\n    while True:\n        arr[pointer] = int(n%2==1)\n        n = n >> 1\n        pointer += 1\n        if n == 0: break\n    return arr\n\n\"\"\"\n    >> get_binary_n_rows( 3, 8)\n    array([[0., 0., 0., 0., 0., 0., 0., 0.],\n        [1., 0., 0., 0., 0., 0., 0., 0.],\n        [0., 1., 0., 0., 0., 0., 0., 0.]], dtype=float32)\n\"\"\"\n@lru_cache(10)\ndef get_binary_n_rows(n_row, n_bit=8, dtype=np.float32):\n    n_int = np.arange(n_row)\n    arr = np.zeros((n_row, n_bit), dtype=dtype)\n    for i in range(n_bit):\n        arr[:, i] = (n_int%2==1).astype(int)\n        n_int = n_int / 2\n        n_int = n_int.astype(np.int8)\n    return arr"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/tensor_ops_c.pyx",
    "content": "import numpy as np\ncimport numpy as np\ncimport cython\nfrom cython.parallel import prange\nfrom libc.math cimport cos, atan2, abs\n\nnp.import_array()\nctypedef np.float64_t DTYPE_F64_t\nctypedef np.float32_t DTYPE_t\nctypedef fused DTYPE_int64_t:\n    np.int64_t\n    np.int32_t  # to compat Windows\nctypedef np.uint8_t DTYPE_bool_t\n\nPI = np.pi\n@cython.boundscheck(False)\n@cython.wraparound(False)\n@cython.nonecheck(False)\ndef reg_rad_arr(DTYPE_F64_t[:] rad):\n    cdef Py_ssize_t dim = rad.shape[0]\n    cdef Py_ssize_t x, y\n    result = np.zeros(dim, dtype=np.double)\n    cdef DTYPE_F64_t[:] result_view = result\n    cdef DTYPE_F64_t PI = np.pi\n\n    for x in prange(dim, nogil=True):\n        result_view[x] = (rad[x] + PI) % (2*PI) - PI\n    return result\n\n\n# @cython.boundscheck(False)\n# @cython.wraparound(False)\n# @cython.nonecheck(False)\n# def roll_hisory( DTYPE_t[:,:,:,:] obs_feed_new, \n#                 DTYPE_t[:,:,:,:] prev_obs_feed, \n#                 DTYPE_bool_t[:,:,:] valid_mask, \n#                 DTYPE_int64_t[:,:] N_valid, \n#                 DTYPE_t[:,:,:,:] next_his_pool):\n#     cdef Py_ssize_t vmax = N_valid.shape[0]\n#     cdef Py_ssize_t wmax = N_valid.shape[1]\n#     cdef Py_ssize_t max_obs_entity = obs_feed_new.shape[2]\n#     cdef int n_v, th, a, t, k, pointer\n#     for th in prange(vmax, nogil=True):\n#         for a in range(wmax):\n#             pointer = 0\n#             for k in range(max_obs_entity):\n#                 if valid_mask[th,a,k]:\n#                     next_his_pool[th,  a,  pointer] = obs_feed_new[th,a,k]\n#                     pointer = pointer + 1\n#             n_v = N_valid[th,a]\n#             for k in range(n_v, max_obs_entity):\n#                 next_his_pool[th,a,k] = prev_obs_feed[th,a,k-n_v]\n#     return np.asarray(next_his_pool)\n\n\n#  https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html?highlight=wraparound#compiler-directives\n'''\n    binding (True): Python函数的内省, 查看函数内部的细节['__class__',  '__delatrr__', ...., 'co_code', 'co_filename', 'co_argcount', 'co_varnames',...]等等\n    boundscheck (True): 数组的边界检查\n    wraparound (True） ： 是否支持索引倒数，如a[-1]\n\n    initializedcheck (True / False): ?\n    nonecheck (False)\n    always_allow_keywords (True / False)\n    profile (False):   Write hooks for Python profilers into the compiled C code. Default is False.\n\n\n    infer_types (True / False): Infer types of untyped variables in function bodies. Default is None, indicating that only safe (semantically-unchanging) inferences are allowed. In particular, inferring integral types for variables used in arithmetic expressions is considered unsafe (due to possible overflow) and must be explicitly requested.\n\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/UTIL/win_pool.py",
    "content": "\"\"\"\n    Author: Fu Qingxu, CASIA\n    Description: Efficient parallel execting tool, \n    Less efficient than the shm_pool (Linux only), \n    but this one supports Windows as well as Linux.\n\"\"\"\nimport numpy as np\nimport time, psutil, platform, copy, multiprocessing\nfrom multiprocessing import Pipe\nfrom config import GlobalConfig\nfrom .hmp_daemon import kill_process_and_its_children\nfrom sys import stdout\n\ndef print_red(*kw,**kargs):\n    print(\"\\033[1;31m\",*kw,\"\\033[0m\",**kargs)\n\ndef print_green(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\nif not stdout.isatty():\n    print_green = print_red = print\n\ndef child_process_load_config(machine_info):\n    # This function is only needed in Windows:\n    # Load json config or cmdline config to child process, \n    from UTIL.config_args import prepare_args\n    prepare_args(vb=False)\n    # there is a 'machine_info' in GlobalConfig that must agree with main process\n    GlobalConfig.machine_info = machine_info\n    pass\n\nclass SuperProc(multiprocessing.Process):\n    def __init__(self, pipe, pipeHelp, index, base_seed, machine_info):\n        super(SuperProc, self).__init__()\n        self.p = pipe\n        self.pH = pipeHelp\n        self.local_seed = index + base_seed\n        self.index = index\n        self.machine_info = machine_info\n        \n\n    def automatic_generation(self, name, gen_fn, *arg):\n        setattr(self, name, gen_fn(*arg))\n\n    def automatic_execution(self, name, dowhat, *arg):\n        return getattr(getattr(self, name), dowhat)(*arg)\n\n    def add_targets(self, new_target_args):\n        for new_target_arg in new_target_args:\n            name, gen_fn, arg = new_target_arg\n            if arg is None:\n                self.automatic_generation(name, gen_fn)\n            elif isinstance(arg, tuple):\n                self.automatic_generation(name, gen_fn, *arg)\n            else:\n                self.automatic_generation(name, gen_fn, arg)\n\n    def execute_target(self, recv_args):\n        res_list = [None] * len(recv_args)\n        for i, recv_arg in enumerate(recv_args):\n            name, dowhat, arg = recv_arg\n            if arg is None:\n                res = self.automatic_execution(name, dowhat)\n            elif isinstance(arg, tuple):\n                res = self.automatic_execution(name, dowhat, *arg)\n            else:\n                res = self.automatic_execution(name, dowhat, arg)\n            res_list[i] = res\n        return res_list\n\n    def run(self):\n        import numpy\n        numpy.random.seed(self.local_seed)\n        # linux uses fork, but windows does not, reload config for windows\n        if not platform.system()==\"Linux\":  \n            child_process_load_config(self.machine_info)\n        print('[win_pool]: process worker %d started'%self.index)\n        try:\n            while True:\n                recv_args = self.p.recv()\n                if not isinstance(recv_args, list):  # not list object, switch to helper channel\n                    if recv_args == 0:\n                        self.add_targets(self.pH.recv())\n                    elif recv_args == -1:\n                        print('Parallel worker exit')\n                        break  # terminate\n                    else:\n                        assert False\n                    continue\n                result = self.execute_target(recv_args)\n                self.p.send(result)\n        except KeyboardInterrupt:\n            self.__del__()\n        self.__del__()\n\n    def __del__(self):\n        self.p.close()\n        self.pH.close()\n        kill_process_and_its_children(psutil.Process())\n\n\nclass SmartPool(object):\n    def __init__(self, proc_num, fold, base_seed=None):\n\n\n        self.proc_num = proc_num\n        self.task_fold = fold\n        self.thisSide, self.thatSide = zip(*[Pipe() for _ in range(proc_num)])\n        self.thisSideHelp, self.thatSideHelp = zip(*[Pipe() for _ in range(proc_num)])\n        self.base_seed = int(np.random.rand()*1e5) if base_seed is None else base_seed\n        print('[win_pool]: SmartPool base rand seed', self.base_seed)\n        self.proc_pool = [SuperProc(pipe=p, pipeHelp=pH, index=cnt, base_seed=self.base_seed, machine_info=GlobalConfig.machine_info)\n                          for p, pH, cnt in zip(self.thatSide, self.thatSideHelp, range(proc_num))]\n        for proc in self.proc_pool:\n            proc.daemon = False\n            proc.start()\n            time.sleep(0.001)\n        # shut down\n        for i in range(proc_num):\n            self.thatSide[i].close()\n            self.thatSideHelp[i].close()\n\n\n\n    # add an object of some class, initialize it proc_num=64 times, assigning them to proc_num/fold_num=16 python\n    # processes\n    def add_target(self, name, lam, args_list=None):\n        lam_list = None\n        if isinstance(lam, list): lam_list = lam\n        for j in range(self.proc_num):\n            tuple_list_to_be_send = []\n            for i in range(self.task_fold):\n                name_fold = name + str(i)\n                args = None if args_list is None else args_list[i + j*self.task_fold]\n                if lam_list is not None: lam = lam_list[i + j*self.task_fold]\n                tuple_list_to_be_send.append((name_fold, lam, args))\n            self.thisSide[j].send(0)    # switch to helper channel\n            self.thisSideHelp[j].send(tuple_list_to_be_send)\n\n    # if there is index, execute one, otherwise execute all\n    def exec_target(self, name, dowhat, args_list = None, index_list = None):\n        if index_list is None:\n            for j in range(self.proc_num):\n                tuple_list_to_be_send = []\n                for i in range(self.task_fold):\n                    name_fold = name + str(i)\n                    args = None if args_list is None else args_list[i + j*self.task_fold]\n                    tuple_list_to_be_send.append((name_fold, dowhat, args))\n                self.thisSide[j].send(tuple_list_to_be_send)\n            res_sort = []\n            for j in range(self.proc_num):\n                res_sort.extend(self.thisSide[j].recv())\n            return res_sort\n        else:\n            tuple_List_List = [[None for _ in range(self.task_fold)] for _ in range(self.proc_num)]\n            do_task_flag = [False for _ in range(self.proc_num)]\n            do_task_fold = [[] for _ in range(self.proc_num)]\n            result_recv_List_List = [[None for _ in range(self.task_fold)] for _ in range(self.proc_num)]\n            # sort args\n            for i, index in enumerate(index_list):\n                which_proc = index // self.task_fold\n                which_fold = index % self.task_fold\n                name_fold = name + str(which_fold)\n                args = None if args_list is None else args_list[i]\n                tuple_List_List[which_proc][which_fold] = (name_fold, dowhat, args)\n                do_task_flag[which_proc] = True\n                \n            # send args\n            for which_proc in range(self.proc_num):\n                tuple_send_buffer = []\n                for which_fold, item in enumerate(tuple_List_List[which_proc]):\n                    if item is None: continue\n                    tuple_send_buffer.append(item)\n                    do_task_fold[which_proc].append(which_fold)\n                if do_task_flag[which_proc]:\n                    assert len(tuple_send_buffer) > 0\n                    self.thisSide[which_proc].send(tuple_send_buffer)\n\n            # receive returns\n            for which_proc in range(self.proc_num):\n                if not do_task_flag[which_proc]:\n                    continue\n                recv_tmp = self.thisSide[which_proc].recv()\n                for index, recv_item in enumerate(recv_tmp):\n                    which_fold = do_task_fold[which_proc][index]\n                    result_recv_List_List[which_proc][which_fold] = recv_item\n\n            # sort returns\n            res_sort = [None] * len(index_list)\n            for i, index in enumerate(index_list):\n                which_proc = index // self.task_fold\n                which_fold = index % self.task_fold\n                res_sort[i] = result_recv_List_List[which_proc][which_fold]\n            return res_sort\n\n    def party_over(self):\n        self.__del__()\n\n    def __del__(self):\n        print('[win_pool]: executing superpool del')\n\n        if hasattr(self, 'terminated'): \n            print_red('[shm_pool]: already terminated, skipping ~')\n            return\n\n        print('[win_pool]: Sending exit command to workers ...')\n        try:\n            for i in range(self.proc_num):\n                self.thisSide[i].send(-1)    # switch to helper channel\n            self.terminated = True\n        except: pass\n\n        print('[win_pool]: Closing pipe ...')\n        for i in range(self.proc_num):\n            try:\n                self.thisSide[i].close()\n                self.thisSideHelp[i].close()\n            except: pass\n\n        N_SEC_WAIT = 2\n        for i in range(N_SEC_WAIT):\n            print_red('[win_pool]: terminate in %d'%(N_SEC_WAIT-i));time.sleep(1)\n\n        # 杀死shm_pool创建的所有子进程，以及子进程的孙进程\n        print_red('[win_pool]: kill_process_and_its_children(proc)')\n        for proc in self.proc_pool: \n            try: kill_process_and_its_children(proc)\n            except Exception as e: print_red('[win_pool]: error occur when kill_process_and_its_children:\\n', e)\n            \n\n\n        print_green('[shm_pool]: __del__ finish')\n        self.terminated = True\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/README.md",
    "content": "# Visual Hybrid Multi-Agent Playground (VHMAP 使用说明书)\n## 面向场景和特点\n面向场景：\n- 科研，尤其是多智能体强化学习领域\n- 3D演示\n- 娱乐\n\n应用特点：\n- Python接口简化到极致\n- 渲染在客户端，自动插帧，纵享丝滑帧率\n- 服务端依赖少\n- 占用服务端资源极少\n- 基于ThreeJs，支持拖动，支持手机触屏\n- 支持透视和投影两种视图的切换\n- 支持回放\n- 使用zlib压缩数据流，网络带宽需求小\n\n## 安装 \n```shell\npip install vhmap\n```\n\n## 20行代码-展示VHMAP的简单、丝滑\n实现下图，仅需要20行python代码(含初始化)\n<div align=\"center\">\n<img src=\"md_imgs/动画x7.gif\" width=\"700\" >\n</div>\n\n界面功能、操作介绍：\n- 鼠标右键平移，左键旋转，滚轮缩放\n- 支持触屏，如果你笔记本或手机有触控屏幕\n- 左上角显示渲染刷新率\n- play fps：每秒播放多少关键帧(小于渲染刷新率，则插帧；大于渲染刷新率，则超出部分无效)\n- pause：暂停\n- next frame：暂停并切换下一帧\n- previous frame：暂停并切换上一帧\n- loop to start：播放完所有数据，回到第一帧\n- ppt step：以极慢极慢的速度播放一帧，方便录屏，按下后会卡顿几秒\n- use orthcam：切换透视视图（物体近大远小）/投影视图（工程制图学过没），\n- P.S. 第一次切换到投影视图时，需要用鼠标滚轮放大画面\n\n```python\nfrom VISUALIZE.mcom import mcom\nimport numpy as np\nclass TestVhmap():\n    def render(self, t):\n        if not hasattr(self, '可视化桥'):\n            self.可视化桥 = mcom(path='TEMP/v2d_logger/', draw_mode='Threejs')\n            self.可视化桥.初始化3D()\n            self.可视化桥.设置样式('gray')\n            self.可视化桥.其他几何体之旋转缩放和平移('box', 'BoxGeometry(1,1,1)',   0,0,0,  1,1,1, 0,0,0) \n\n        x = np.cos(t); y=np.sin(t); z= np.cos(t)*np.sin(t)  # 此帧的x,y,z坐标\n        self.可视化桥.发送几何体(\n            'box|2233|Red|0.1',     # 填入 ‘形状|几何体之ID标识|颜色|大小’即可\n            x, y, z, ro_x=0, ro_y=0, ro_z=np.sin(t),    # 三维位置+欧拉旋转变换，六自由度\n            track_n_frame=20)                           # 显示历史20帧留下的轨迹\n        self.可视化桥.结束关键帧()\n\nif __name__ == '__main__':\n    x = TestVhmap()\n    for step in range(1000): x.render(t=step/np.pi)\n    import time; time.sleep(1000)   # 启动后打开输出的url地址即可\n# 这是第21行，已经写完了 :joy: \n```\n\n## 50行代码-演示3维N体运动(低精度定步长)\n- 代码1详情请见：VISUALIZE/examples/nb.py\n运行方法：\n```\npip install vhmap\n\npython -m VISUALIZE.examples.nb\n```\n<div align=\"center\">\n<img src=\"md_imgs/动画9.gif\" width=\"700\" >\n</div>\n\n## 90行代码-使用dop853求解常微分方程演示三体、N体运动\n- 代码2详情请见：VISUALIZE/examples/nb_3body_specials.py\n- 代码3详情请见：VISUALIZE/examples/nb_nbody_specials.py\n```\npip install vhmap\n\npython -m VISUALIZE.examples.nb_3body_specials\n```\n<div align=\"center\">\n<img src=\"md_imgs/动画11.gif\" width=\"300\", height=\"275\"  >\n<img src=\"md_imgs/动画13.gif\"  width=\"300\" >\n<img src=\"md_imgs/动画12-1.gif\"  width=\"700\" >\n</div>\n感谢 Xiaoming LI and Shijun LIAO, Shanghai Jiaotong University, China 的三体初始值：\nhttps://numericaltank.sjtu.edu.cn/three-body/three-body-movies.htm\n\n## 如何回放\nVHMAP在接收到数据后，会自动地在 TEMP/v2d_logger/ 路径下生成backup.dp数据文件，该文件可以用于回放。\n\n警告：数据文件会在下一次运行时被新的日志文件覆盖，必要时请手动备份！\n```\npython -m VISUALIZE.threejs_replay -f TEMP/v2d_logger/backup.dp.gz -p 8085\n```\n其中-f后面的是回放文件的路径，-p接端口号例如8085，之后打开 http://localhost:8085 即可。\n\n\n## API-中文\n\n引入\n```python\nfrom VISUALIZE.mcom import mcom\n```\n\n### 初始化\n```python\n可视化桥 = mcom(path='TEMP/v2d_logger/', draw_mode='Threejs')\n可视化桥.初始化3D()\n```\n\n### 设置样式\n```python\n可视化桥.设置样式('star')       # 布置星空\n可视化桥.设置样式('grid')       # 布置2维网格\n可视化桥.设置样式('grid3d')     # 布置3维网格\n可视化桥.设置样式('earth')      # 在场景中放一个地球\n可视化桥.设置样式('background', color='White') # 注意不可以省略参数键值'color=' ！可用颜色（JS颜色，支持Hex颜色）参考 https://www.w3schools.com/colors/colors_names.asp\n\n# 如果label要使用中文字符，需要设置字体，否则字符会变成问号'?'\n# 如果label要使用中文字符，而且需要换行，则还需要额外设置行距 fontLineHeight\n可视化桥.设置样式('font', fontPath='/examples/fonts/ttf/simhei.ttf', fontLineHeight=1500)   \n\n\n可视化桥.设置样式('skybox', path='/wget/shabby.jpg')    # 设置天空盒子，注意不可以省略参数键值'path='\n可视化桥.设置样式('skybox6side',    # 设置天空盒子，注意不可以省略参数键值 !!\n    posx='/wget/mars_textures/mars_posx.jpg',   \n    negx='/wget/mars_textures/mars_negx.jpg',   \n    posy='/wget/mars_textures/mars_posy.jpg',\n    negy='/wget/mars_textures/mars_negy.jpg',\n    posz='/wget/mars_textures/mars_posz.jpg',\n    negz='/wget/mars_textures/mars_negz.jpg',\n)\n\n\n```\n\n### 声明几何体\n```python\n# declare geo 'oct1', init with OctahedronGeometry, then (1)rotate & (2)scale & (3)translate\n可视化桥.其他几何体之旋转缩放和平移('oct1', 'OctahedronGeometry(1,0)', 0,0,0,  1,1,1, 0,0,0)   # 八面体\n# 需要换成其他几何体，请把'OctahedronGeometry(1,0)'替换，参考网址 https://threejs.org/docs/index.html?q=Geometry\n可视化桥.其他几何体之旋转缩放和平移('any_name_you_want', 'TorusGeometry(10,3,16,100)',   0,0,0,  1,1,1, 0,0,0) # 甜甜圈\n# declare geo 'ball'\n可视化桥.其他几何体之旋转缩放和平移('ball', 'SphereGeometry(1)',   0,0,0,  1,1,1, 0,0,0) # 球体\n# declare geo 'box'\n可视化桥.其他几何体之旋转缩放和平移('box', 'BoxGeometry(1,1,1)',   0,0,0,  1,1,1, 0,0,0) # 长方体\n# declare geo 'Plane', 使用fbx模型，路径为/VISUALIZE/threejsmod/examples/files/plane.fbx\n可视化桥.其他几何体之旋转缩放和平移('Plane', 'fbx=/examples/files/plane.fbx', -np.pi/2, 0, np.pi/2,  1,1,1, 0,0,0)   # 八面体\n\n```\n\n### 发送几何体，可用颜色（JS颜色，支持Hex颜色）参考 https://www.w3schools.com/colors/colors_names.asp\n```python\n# 注意不可以省略参数键值\nx=1; y=2; z=3\n可视化桥.发送几何体(\n    'ball|8848|MidnightBlue|0.5',  # 填入核心参量： “已声明的形状|几何体的唯一ID标识|颜色|整体大小”\n    x, y, z,                # 三维位置，3/6dof\n    ro_x=0, ro_y=0, ro_z=0, # 欧拉旋转变换，3/6dof\n    # ro_order='XYZ',       # （测试中，勿使用）欧拉旋转顺序，详情见 https://threejs.org/docs/index.html?q=object#api/en/math/Euler\n    opacity=1,              # 透明度，1为不透明\n    renderOrder=0,          # 渲染顺序。合理使用，能解决透明物体异常遮蔽的情况\n    label='',               # 显示标签，空白不显示，用'\\n'换行\n    label_color='White',    # 标签颜色\n    # label_offset=np.array([0,2,2]), # 标签与物体之间的相对位置，实验选项，测试中，勿使用\n    # label_size=0.5, # 测试中，勿使用\n    track_n_frame=3,        # 是否显示轨迹（0代表否），轨迹由最新的track_n_frame次位置连接而成\n    track_tension=0.1,      # 轨迹曲线的平滑度，0为不平滑，推荐设置0不平滑\n    track_color='Green',    # 轨迹的颜色显示，输入js颜色名或者hex值均可\n    )\n```\n\n\n```python\n# 高级文本颜色，一句话添加不同颜色文字，支持换行，支持半透明背景色（测试中）\nn_blue=1\nn_red=2\nwho_is_winning = '<Blue>Blue(MARL AI)<Black> is leading' if n_blue>n_red else '<Red>Red(Script AI)<Black> is leading'\n可视化桥.发送几何体('tower2|1004|Gray|0.2', 0, 0, 1, ro_x=0, ro_y=0, ro_z=0, \n    # 文本的背景色\n    label_bgcolor='GhostWhite',\n    # 带不同颜色的文本区\n    label='<Blue>Blue(MARL AI)<Black>Agents Remain: <Blue>%d\\n<Red>Red(Script AI)<Black>Agents Remain: <Red>%d \\n%s<End>'%(n_blue, n_red, who_is_winning), \n    # 默认颜色\n    label_color='DarkGreen', \n    # 透明度\n    opacity=0\n)\n```\n\n其中的“renderOrder”选项比较难以理解，如果没有显示异常，则设置为0，或者干脆删除该键值（默认0）。\n\n用它解决的问题是简单的：\n```\nIf: 一个（透明）物体A 被 一个透明物体B遮挡，但A部分或全部不可见\nThen: 增加B的renderOrder，或者减小A的renderOrder（取值范围0~127）\n\n此外，label标签的渲染顺序renderOrder是128，任意全透明物体的渲染顺序renderOrder是256\n```\n### 发送曲线\n```python\n# 画一条(0,0,0) -> (1,1,0) -> (2,2,0) -> (3,3,0) 的线\n# 注意不可以省略参数键值!!\n可视化桥.发送线条(\n    'simple|3999|MidnightBlue|0.004', # 填入核心参量： “simple|线条的唯一ID标识|颜色|整体大小”\n    x_arr=np.array([0, 1, 2, 3]),   # 曲线的x坐标列表\n    y_arr=np.array([0, 1, 2, 3]),   # 曲线的y坐标列表\n    z_arr=np.array([0, 0, 0, 0]),   # 曲线的z坐标列表\n    tension=0,  # 曲线的平滑度，0为不平滑，推荐不平滑\n    opacity=1,  # 透明度，1为不透明，不稳定仍然在测试中\n)\n\n# fat 型线条，支持调节宽度、虚线、透明度等，但是不稳定仍然在测试中\n# 注意不可以省略参数键值!!\n可视化桥.发送线条(\n    'fat|3999|MidnightBlue|0.004', # 填入核心参量： “fat|线条的唯一ID标识|颜色|整体大小”\n    x_arr=np.array([0, 1, 2, 3]),   # 曲线的x坐标列表\n    y_arr=np.array([0, 1, 2, 3]),   # 曲线的y坐标列表\n    z_arr=np.array([0, 0, 0, 0]),   # 曲线的z坐标列表\n    dashScale=20,   # 此数越大，单位长度上的虚线切割越多\n    dashSize=1,     # 虚线切割之实线\n    gapSize=1,      # 虚线切割之实线间隔\n    tension=0,      # 曲线的平滑度，0为不平滑，推荐不平滑\n    opacity=1,      # 透明度，1为不透明\n)\n```\n\n### 发射光束（从几何体src到几何体dst）\n```python\n# 注意不可以省略参数键值!!\n可视化桥.发射光束(\n    'beam',         # 有 beam 和 lightning 两种选择\n    src=index_ID,   # 发射者的几何体的唯一ID标识\n    dst=index_ID2,  # 接收者的几何体的唯一ID标识\n    dur=0.5,        # 光束持续时间，单位秒，绝对时间，不受播放fps的影响\n    size=0.03,      # 光束粗细\n    color='DeepSkyBlue' # 光束颜色\n)\n\n```\n\n\n终结这一帧（并开始下一帧）\n```python\nself.可视化桥.结束关键帧()\n```\n\n\n### 测试中-添加贴图\n```python\n可视化桥.advanced_geometry_material('ball', \n    map='/examples/planets/images/earthmap1k.jpg',\n    bumpMap='/examples/planets/images/earthmap1k.jpg',\n    bumpScale = 0.05,\n    specularMap='/examples/images/earthmap1k.jpg',\n    specular='Gray'\n)  \n```\n\n\n## API-Eng\n\nIn fact, this project is developed in Eng API,\nbut I do not have time to write document.\nThe api alignment can be found in mcom.py:\n```\n别名对齐 = [\n    ('初始化3D', 'v2d_init'),\n    ('设置样式', 'set_style'),\n    ('形状之旋转缩放和平移','geometry_rotate_scale_translate'),\n    ('其他几何体之旋转缩放和平移','advanced_geometry_rotate_scale_translate'),\n    ('发送几何体','v2dx'),\n    ('结束关键帧','v2d_show'),\n    ('发送线条','line3d'),\n    ('发射光束','flash'),\n]\n```"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/__init__.py",
    "content": ""
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/color.html",
    "content": "<div align=\"center\">\n    <center>\n        <div class=\"table-box\">\n            <table id=\"color\" border=\"1\" cellspacing=\"4\" cellpadding=\"4\" width=\"600\">\n                <tbody>\n                    <tr align=\"middle\" bgcolor=\"#000000\">\n                        <td><strong><span style=\"color:#ffffff\">颜色</span></strong></td>\n                        <td><strong><span style=\"color:#ffffff\">英文代码</span></strong></td>\n                        <td><strong><span style=\"color:#ffffff\">形像颜色</span></strong></td>\n                        <td><strong><span style=\"color:#ffffff\">HEX格式</span></strong></td>\n                        <td><strong><span style=\"color:#ffffff\">RGB格式</span></strong></td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#330000\">　</td>\n                        <td title=\"\"> </td>\n                        <td> </td>\n                        <td title=\"\">#330000</td>\n                        <td> </td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#000033\">　</td>\n                        <td title=\"\"> </td>\n                        <td> </td>\n                        <td title=\"\">#000033</td>\n                        <td> </td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightpink\">　</td>\n                        <td title=\"\">LightPink</td>\n                        <td>浅粉红</td>\n                        <td title=\"\">#FFB6C1</td>\n                        <td>255,182,193</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"pink\">　</td>\n                        <td title=\"\">Pink</td>\n                        <td>粉红</td>\n                        <td title=\"\">#FFC0CB</td>\n                        <td>255,192,203</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"crimson\">　</td>\n                        <td title=\"\">Crimson</td>\n                        <td>猩红</td>\n                        <td title=\"\">#DC143C</td>\n                        <td>220,20,60</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lavenderblush\">　</td>\n                        <td title=\"\">LavenderBlush</td>\n                        <td>脸红的淡紫色</td>\n                        <td title=\"\">#FFF0F5</td>\n                        <td>255,240,245</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"palevioletred\">　</td>\n                        <td title=\"\">PaleVioletRed</td>\n                        <td>苍白的紫罗兰红色</td>\n                        <td title=\"\">#DB7093</td>\n                        <td>219,112,147</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"hotpink\">　</td>\n                        <td title=\"\">HotPink</td>\n                        <td>热情的粉红</td>\n                        <td title=\"\">#FF69B4</td>\n                        <td>255,105,180</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"deeppink\">　</td>\n                        <td title=\"\">DeepPink</td>\n                        <td>深粉色</td>\n                        <td title=\"\">#FF1493</td>\n                        <td>255,20,147</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumvioletred\">　</td>\n                        <td title=\"\">MediumVioletRed</td>\n                        <td>适中的紫罗兰红色</td>\n                        <td title=\"\">#C71585</td>\n                        <td>199,21,133</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"orchid\">　</td>\n                        <td title=\"\">Orchid</td>\n                        <td>兰花的紫色</td>\n                        <td title=\"\">#DA70D6</td>\n                        <td>218,112,214</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"thistle\">　</td>\n                        <td title=\"\">Thistle</td>\n                        <td>蓟</td>\n                        <td title=\"\">#D8BFD8</td>\n                        <td>216,191,216</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"plum\">　</td>\n                        <td title=\"\">plum</td>\n                        <td>李子</td>\n                        <td title=\"\">#DDA0DD</td>\n                        <td>221,160,221</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"violet\">　</td>\n                        <td title=\"\">Violet</td>\n                        <td>紫罗兰</td>\n                        <td title=\"\">#EE82EE</td>\n                        <td>238,130,238</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"magenta\">　</td>\n                        <td title=\"\">Magenta</td>\n                        <td>洋红</td>\n                        <td title=\"\">#FF00FF</td>\n                        <td>255,0,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"fuchsia\">　</td>\n                        <td title=\"\">Fuchsia</td>\n                        <td>灯笼海棠(紫红色)</td>\n                        <td title=\"\">#FF00FF</td>\n                        <td>255,0,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkmagenta\">　</td>\n                        <td title=\"\">DarkMagenta</td>\n                        <td>深洋红色</td>\n                        <td title=\"\">#8B008B</td>\n                        <td>139,0,139</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"purple\">　</td>\n                        <td title=\"\">Purple</td>\n                        <td>紫色</td>\n                        <td title=\"\">#800080</td>\n                        <td>128,0,128</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumorchid\">　</td>\n                        <td title=\"\">MediumOrchid</td>\n                        <td>适中的兰花紫</td>\n                        <td title=\"\">#BA55D3</td>\n                        <td>186,85,211</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#da00e0\">　</td>\n                        <td title=\"\">DarkVoilet</td>\n                        <td>深紫罗兰色</td>\n                        <td title=\"\">#9400D3</td>\n                        <td>148,0,211</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkorchid\">　</td>\n                        <td title=\"\">DarkOrchid</td>\n                        <td>深兰花紫</td>\n                        <td title=\"\">#9932CC</td>\n                        <td>153,50,204</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"indigo\">　</td>\n                        <td title=\"\">Indigo</td>\n                        <td>靛青</td>\n                        <td title=\"\">#4B0082</td>\n                        <td>75,0,130</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"blueviolet\">　</td>\n                        <td title=\"\">BlueViolet</td>\n                        <td>深紫罗兰的蓝色</td>\n                        <td title=\"\">#8A2BE2</td>\n                        <td>138,43,226</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumpurple\">　</td>\n                        <td title=\"\">MediumPurple</td>\n                        <td>适中的紫色</td>\n                        <td title=\"\">#9370DB</td>\n                        <td>147,112,219</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumslateblue\">　</td>\n                        <td title=\"\">MediumSlateBlue</td>\n                        <td>适中的板岩暗蓝灰色</td>\n                        <td title=\"\">#7B68EE</td>\n                        <td>123,104,238</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"slateblue\">　</td>\n                        <td title=\"\">SlateBlue</td>\n                        <td>板岩暗蓝灰色</td>\n                        <td title=\"\">#6A5ACD</td>\n                        <td>106,90,205</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkslateblue\">　</td>\n                        <td title=\"\">DarkSlateBlue</td>\n                        <td>深岩暗蓝灰色</td>\n                        <td title=\"\">#483D8B</td>\n                        <td>72,61,139</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lavender\">　</td>\n                        <td title=\"\">Lavender</td>\n                        <td>熏衣草花的淡紫色</td>\n                        <td title=\"\">#E6E6FA</td>\n                        <td>230,230,250</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"ghostwhite\">　</td>\n                        <td title=\"\">GhostWhite</td>\n                        <td>幽灵的白色</td>\n                        <td title=\"\">#F8F8FF</td>\n                        <td>248,248,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"blue\">　</td>\n                        <td title=\"\">Blue</td>\n                        <td>纯蓝</td>\n                        <td title=\"\">#0000FF</td>\n                        <td>0,0,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumblue\">　</td>\n                        <td title=\"\">MediumBlue</td>\n                        <td>适中的蓝色</td>\n                        <td title=\"\">#0000CD</td>\n                        <td>0,0,205</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"midnightblue\">　</td>\n                        <td title=\"\">MidnightBlue</td>\n                        <td>午夜的蓝色</td>\n                        <td title=\"\">#191970</td>\n                        <td>25,25,112</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkblue\">　</td>\n                        <td title=\"\">DarkBlue</td>\n                        <td>深蓝色</td>\n                        <td title=\"\">#00008B</td>\n                        <td>0,0,139</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"navy\">　</td>\n                        <td title=\"\">Navy</td>\n                        <td>海军蓝</td>\n                        <td title=\"\">#000080</td>\n                        <td>0,0,128</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"royalblue\">　</td>\n                        <td title=\"\">RoyalBlue</td>\n                        <td>皇军蓝</td>\n                        <td title=\"\">#4169E1</td>\n                        <td>65,105,225</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"cornflowerblue\">　</td>\n                        <td title=\"\">CornflowerBlue</td>\n                        <td>矢车菊的蓝色</td>\n                        <td title=\"\">#6495ED</td>\n                        <td>100,149,237</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightsteelblue\">　</td>\n                        <td title=\"\">LightSteelBlue</td>\n                        <td>淡钢蓝</td>\n                        <td title=\"\">#B0C4DE</td>\n                        <td>176,196,222</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightslategray\">　</td>\n                        <td title=\"\">LightSlateGray</td>\n                        <td>浅石板灰</td>\n                        <td title=\"\">#778899</td>\n                        <td>119,136,153</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"slategray\">　</td>\n                        <td title=\"\">SlateGray</td>\n                        <td>石板灰</td>\n                        <td title=\"\">#708090</td>\n                        <td>112,128,144</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#d0e000\">　</td>\n                        <td title=\"\">DoderBlue</td>\n                        <td>道奇蓝</td>\n                        <td title=\"\">#1E90FF</td>\n                        <td>30,144,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"aliceblue\">　</td>\n                        <td title=\"\">AliceBlue</td>\n                        <td>爱丽丝蓝</td>\n                        <td title=\"\">#F0F8FF</td>\n                        <td>240,248,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"steelblue\">　</td>\n                        <td title=\"\">SteelBlue</td>\n                        <td>钢蓝</td>\n                        <td title=\"\">#4682B4</td>\n                        <td>70,130,180</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightskyblue\">　</td>\n                        <td title=\"\">LightSkyBlue</td>\n                        <td>淡蓝色</td>\n                        <td title=\"\">#87CEFA</td>\n                        <td>135,206,250</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"skyblue\">　</td>\n                        <td title=\"\">SkyBlue</td>\n                        <td>天蓝色</td>\n                        <td title=\"\">#87CEEB</td>\n                        <td>135,206,235</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"deepskyblue\">　</td>\n                        <td title=\"\">DeepSkyBlue</td>\n                        <td>深天蓝</td>\n                        <td title=\"\">#00BFFF</td>\n                        <td>0,191,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightblue\">　</td>\n                        <td title=\"\">LightBLue</td>\n                        <td>淡蓝</td>\n                        <td title=\"\">#ADD8E6</td>\n                        <td>173,216,230</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"powderblue\">　</td>\n                        <td title=\"\">PowDerBlue</td>\n                        <td>火药蓝</td>\n                        <td title=\"\">#B0E0E6</td>\n                        <td>176,224,230</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"cadetblue\">　</td>\n                        <td title=\"\">CadetBlue</td>\n                        <td>军校蓝</td>\n                        <td title=\"\">#5F9EA0</td>\n                        <td>95,158,160</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"azure\">　</td>\n                        <td title=\"\">Azure</td>\n                        <td>蔚蓝色</td>\n                        <td title=\"\">#F0FFFF</td>\n                        <td>240,255,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightcyan\">　</td>\n                        <td title=\"\">LightCyan</td>\n                        <td>淡青色</td>\n                        <td title=\"\">#E1FFFF</td>\n                        <td>225,255,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"paleturquoise\">　</td>\n                        <td title=\"\">PaleTurquoise</td>\n                        <td>苍白的绿宝石</td>\n                        <td title=\"\">#AFEEEE</td>\n                        <td>175,238,238</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"cyan\">　</td>\n                        <td title=\"\">Cyan</td>\n                        <td>青色</td>\n                        <td title=\"\">#00FFFF</td>\n                        <td>0,255,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"aqua\">　</td>\n                        <td title=\"\">Aqua</td>\n                        <td>水绿色</td>\n                        <td title=\"\">#00FFFF</td>\n                        <td>0,255,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkturquoise\">　</td>\n                        <td title=\"\">DarkTurquoise</td>\n                        <td>深绿宝石</td>\n                        <td title=\"\">#00CED1</td>\n                        <td>0,206,209</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkslategray\">　</td>\n                        <td title=\"\">DarkSlateGray</td>\n                        <td>深石板灰</td>\n                        <td title=\"\">#2F4F4F</td>\n                        <td>47,79,79</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkcyan\">　</td>\n                        <td title=\"\">DarkCyan</td>\n                        <td>深青色</td>\n                        <td title=\"\">#008B8B</td>\n                        <td>0,139,139</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"teal\">　</td>\n                        <td title=\"\">Teal</td>\n                        <td>水鸭色</td>\n                        <td title=\"\">#008080</td>\n                        <td>0,128,128</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumturquoise\">　</td>\n                        <td title=\"\">MediumTurquoise</td>\n                        <td>适中的绿宝石</td>\n                        <td title=\"\">#48D1CC</td>\n                        <td>72,209,204</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightseagreen\">　</td>\n                        <td title=\"\">LightSeaGreen</td>\n                        <td>浅海洋绿</td>\n                        <td title=\"\">#20B2AA</td>\n                        <td>32,178,170</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"turquoise\">　</td>\n                        <td title=\"\">Turquoise</td>\n                        <td>绿宝石</td>\n                        <td title=\"\">#40E0D0</td>\n                        <td>64,224,208</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#a0a000\">　</td>\n                        <td title=\"\">Auqamarin</td>\n                        <td>绿玉\\碧绿色</td>\n                        <td title=\"\">#7FFFAA</td>\n                        <td>127,255,170</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumaquamarine\">　</td>\n                        <td title=\"\">MediumAquamarine</td>\n                        <td>适中的碧绿色</td>\n                        <td title=\"\">#00FA9A</td>\n                        <td>0,250,154</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mediumspringgreen\">　</td>\n                        <td title=\"\">MediumSpringGreen</td>\n                        <td>适中的春天的绿色</td>\n                        <td title=\"\">#F5FFFA</td>\n                        <td>245,255,250</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mintcream\">　</td>\n                        <td title=\"\">MintCream</td>\n                        <td>薄荷奶油</td>\n                        <td title=\"\">#00FF7F</td>\n                        <td>0,255,127</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"springgreen\">　</td>\n                        <td title=\"\">SpringGreen</td>\n                        <td>春天的绿色</td>\n                        <td title=\"\">#3CB371</td>\n                        <td>60,179,113</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"seagreen\">　</td>\n                        <td title=\"\">SeaGreen</td>\n                        <td>海洋绿</td>\n                        <td title=\"\">#2E8B57</td>\n                        <td>46,139,87</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"honeydew\">　</td>\n                        <td title=\"\">Honeydew</td>\n                        <td>蜂蜜</td>\n                        <td title=\"\">#F0FFF0</td>\n                        <td>240,255,240</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightgreen\">　</td>\n                        <td title=\"\">LightGreen</td>\n                        <td>淡绿色</td>\n                        <td title=\"\">#90EE90</td>\n                        <td>144,238,144</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"palegreen\">　</td>\n                        <td title=\"\">PaleGreen</td>\n                        <td>苍白的绿色</td>\n                        <td title=\"\">#98FB98</td>\n                        <td>152,251,152</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkseagreen\">　</td>\n                        <td title=\"\">DarkSeaGreen</td>\n                        <td>深海洋绿</td>\n                        <td title=\"\">#8FBC8F</td>\n                        <td>143,188,143</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"limegreen\">　</td>\n                        <td title=\"\">LimeGreen</td>\n                        <td>酸橙绿</td>\n                        <td title=\"\">#32CD32</td>\n                        <td>50,205,50</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lime\">　</td>\n                        <td title=\"\">Lime</td>\n                        <td>酸橙色</td>\n                        <td title=\"\">#00FF00</td>\n                        <td>0,255,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"forestgreen\">　</td>\n                        <td title=\"\">ForestGreen</td>\n                        <td>森林绿</td>\n                        <td title=\"\">#228B22</td>\n                        <td>34,139,34</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"green\">　</td>\n                        <td title=\"\">Green</td>\n                        <td>纯绿</td>\n                        <td title=\"\">#008000</td>\n                        <td>0,128,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkgreen\">　</td>\n                        <td title=\"\">DarkGreen</td>\n                        <td>深绿色</td>\n                        <td title=\"\">#006400</td>\n                        <td>0,100,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"chartreuse\">　</td>\n                        <td title=\"\">Chartreuse</td>\n                        <td>查特酒绿</td>\n                        <td title=\"\">#7FFF00</td>\n                        <td>127,255,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lawngreen\">　</td>\n                        <td title=\"\">LawnGreen</td>\n                        <td>草坪绿</td>\n                        <td title=\"\">#7CFC00</td>\n                        <td>124,252,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"greenyellow\">　</td>\n                        <td title=\"\">GreenYellow</td>\n                        <td>绿黄色</td>\n                        <td title=\"\">#ADFF2F</td>\n                        <td>173,255,47</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"olivedrab\">　</td>\n                        <td title=\"\">OliveDrab</td>\n                        <td>橄榄土褐色</td>\n                        <td title=\"\">#556B2F</td>\n                        <td>85,107,47</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"beige\">　</td>\n                        <td title=\"\">Beige</td>\n                        <td>米色(浅褐色)</td>\n                        <td title=\"\">#6B8E23</td>\n                        <td>107,142,35</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightgoldenrodyellow\">　</td>\n                        <td title=\"\">LightGoldenrodYellow</td>\n                        <td>浅秋麒麟黄</td>\n                        <td title=\"\">#FAFAD2</td>\n                        <td>250,250,210</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"ivory\">　</td>\n                        <td title=\"\">Ivory</td>\n                        <td>象牙</td>\n                        <td title=\"\">#FFFFF0</td>\n                        <td>255,255,240</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightyellow\">　</td>\n                        <td title=\"\">LightYellow</td>\n                        <td>浅黄色</td>\n                        <td title=\"\">#FFFFE0</td>\n                        <td>255,255,224</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"yellow\">　</td>\n                        <td title=\"\">Yellow</td>\n                        <td>纯黄</td>\n                        <td title=\"\">#FFFF00</td>\n                        <td>255,255,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"olive\">　</td>\n                        <td title=\"\">Olive</td>\n                        <td>橄榄</td>\n                        <td title=\"\">#808000</td>\n                        <td>128,128,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkkhaki\">　</td>\n                        <td title=\"\">DarkKhaki</td>\n                        <td>深卡其布</td>\n                        <td title=\"\">#BDB76B</td>\n                        <td>189,183,107</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lemonchiffon\">　</td>\n                        <td title=\"\">LemonChiffon</td>\n                        <td>柠檬薄纱</td>\n                        <td title=\"\">#FFFACD</td>\n                        <td>255,250,205</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#a00d00\">　</td>\n                        <td title=\"\">PaleGodenrod</td>\n                        <td>灰秋麒麟</td>\n                        <td title=\"\">#EEE8AA</td>\n                        <td>238,232,170</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"khaki\">　</td>\n                        <td title=\"\">Khaki</td>\n                        <td>卡其布</td>\n                        <td title=\"\">#F0E68C</td>\n                        <td>240,230,140</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"gold\">　</td>\n                        <td title=\"\">Gold</td>\n                        <td>金</td>\n                        <td title=\"\">#FFD700</td>\n                        <td>255,215,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#c00000\">　</td>\n                        <td title=\"\">Cornislk</td>\n                        <td>玉米色</td>\n                        <td title=\"\">#FFF8DC</td>\n                        <td>255,248,220</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"goldenrod\">　</td>\n                        <td title=\"\">GoldEnrod</td>\n                        <td>秋麒麟</td>\n                        <td title=\"\">#DAA520</td>\n                        <td>218,165,32</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"floralwhite\">　</td>\n                        <td title=\"\">FloralWhite</td>\n                        <td>花的白色</td>\n                        <td title=\"\">#FFFAF0</td>\n                        <td>255,250,240</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"oldlace\">　</td>\n                        <td title=\"\">OldLace</td>\n                        <td>老饰带</td>\n                        <td title=\"\">#FDF5E6</td>\n                        <td>253,245,230</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"wheat\">　</td>\n                        <td title=\"\">Wheat</td>\n                        <td>小麦色</td>\n                        <td title=\"\">#F5DEB3</td>\n                        <td>245,222,179</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"moccasin\">　</td>\n                        <td title=\"\">Moccasin</td>\n                        <td>鹿皮鞋</td>\n                        <td title=\"\">#FFE4B5</td>\n                        <td>255,228,181</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"orange\">　</td>\n                        <td title=\"\">Orange</td>\n                        <td>橙色</td>\n                        <td title=\"\">#FFA500</td>\n                        <td>255,165,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"papayawhip\">　</td>\n                        <td title=\"\">PapayaWhip</td>\n                        <td>番木瓜</td>\n                        <td title=\"\">#FFEFD5</td>\n                        <td>255,239,213</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"blanchedalmond\">　</td>\n                        <td title=\"\">BlanchedAlmond</td>\n                        <td>漂白的杏仁</td>\n                        <td title=\"\">#FFEBCD</td>\n                        <td>255,235,205</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"navajowhite\">　</td>\n                        <td title=\"\">NavajoWhite</td>\n                        <td>Navajo白</td>\n                        <td title=\"\">#FFDEAD</td>\n                        <td>255,222,173</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"antiquewhite\">　</td>\n                        <td title=\"\">AntiqueWhite</td>\n                        <td>古代的白色</td>\n                        <td title=\"\">#FAEBD7</td>\n                        <td>250,235,215</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"tan\">　</td>\n                        <td title=\"\">Tan</td>\n                        <td>晒黑</td>\n                        <td title=\"\">#D2B48C</td>\n                        <td>210,180,140</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"#b00000\">　</td>\n                        <td title=\"\">BrulyWood</td>\n                        <td>结实的树</td>\n                        <td title=\"\">#DEB887</td>\n                        <td>222,184,135</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"bisque\">　</td>\n                        <td title=\"\">Bisque</td>\n                        <td>(浓汤)乳脂,番茄等</td>\n                        <td title=\"\">#FFE4C4</td>\n                        <td>255,228,196</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkorange\">　</td>\n                        <td title=\"\">DarkOrange</td>\n                        <td>深橙色</td>\n                        <td title=\"\">#FF8C00</td>\n                        <td>255,140,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"linen\">　</td>\n                        <td title=\"\">Linen</td>\n                        <td>亚麻布</td>\n                        <td title=\"\">#FAF0E6</td>\n                        <td>250,240,230</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"peru\">　</td>\n                        <td title=\"\">Peru</td>\n                        <td>秘鲁</td>\n                        <td title=\"\">#CD853F</td>\n                        <td>205,133,63</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"peachpuff\">　</td>\n                        <td title=\"\">PeachPuff</td>\n                        <td>桃色</td>\n                        <td title=\"\">#FFDAB9</td>\n                        <td>255,218,185</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"sandybrown\">　</td>\n                        <td title=\"\">SandyBrown</td>\n                        <td>沙棕色</td>\n                        <td title=\"\">#F4A460</td>\n                        <td>244,164,96</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"chocolate\">　</td>\n                        <td title=\"\">Chocolate</td>\n                        <td>巧克力</td>\n                        <td title=\"\">#D2691E</td>\n                        <td>210,105,30</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"saddlebrown\">　</td>\n                        <td title=\"\">SaddleBrown</td>\n                        <td>马鞍棕色</td>\n                        <td title=\"\">#8B4513</td>\n                        <td>139,69,19</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"seashell\">　</td>\n                        <td title=\"\">SeaShell</td>\n                        <td>海贝壳</td>\n                        <td title=\"\">#FFF5EE</td>\n                        <td>255,245,238</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"sienna\">　</td>\n                        <td title=\"\">Sienna</td>\n                        <td>黄土赭色</td>\n                        <td title=\"\">#A0522D</td>\n                        <td>160,82,45</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightsalmon\">　</td>\n                        <td title=\"\">LightSalmon</td>\n                        <td>浅鲜肉(鲑鱼)色</td>\n                        <td title=\"\">#FFA07A</td>\n                        <td>255,160,122</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"coral\">　</td>\n                        <td title=\"\">Coral</td>\n                        <td>珊瑚</td>\n                        <td title=\"\">#FF7F50</td>\n                        <td>255,127,80</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"orangered\">　</td>\n                        <td title=\"\">OrangeRed</td>\n                        <td>橙红色</td>\n                        <td title=\"\">#FF4500</td>\n                        <td>255,69,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darksalmon\">　</td>\n                        <td title=\"\">DarkSalmon</td>\n                        <td>深鲜肉(鲑鱼)色</td>\n                        <td title=\"\">#E9967A</td>\n                        <td>233,150,122</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"tomato\">　</td>\n                        <td title=\"\">Tomato</td>\n                        <td>番茄</td>\n                        <td title=\"\">#FF6347</td>\n                        <td>255,99,71</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"mistyrose\">　</td>\n                        <td title=\"\">MistyRose</td>\n                        <td>薄雾玫瑰</td>\n                        <td title=\"\">#FFE4E1</td>\n                        <td>255,228,225</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"salmon\">　</td>\n                        <td title=\"\">Salmon</td>\n                        <td>鲜肉(鲑鱼)色</td>\n                        <td title=\"\">#FA8072</td>\n                        <td>250,128,114</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"snow\">　</td>\n                        <td title=\"\">Snow</td>\n                        <td>雪</td>\n                        <td title=\"\">#FFFAFA</td>\n                        <td>255,250,250</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightcoral\">　</td>\n                        <td title=\"\">LightCoral</td>\n                        <td>淡珊瑚色</td>\n                        <td title=\"\">#F08080</td>\n                        <td>240,128,128</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"rosybrown\">　</td>\n                        <td title=\"\">RosyBrown</td>\n                        <td>玫瑰棕色</td>\n                        <td title=\"\">#BC8F8F</td>\n                        <td>188,143,143</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"indianred\">　</td>\n                        <td title=\"\">IndianRed</td>\n                        <td>印度红</td>\n                        <td title=\"\">#CD5C5C</td>\n                        <td>205,92,92</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"red\">　</td>\n                        <td title=\"\">Red</td>\n                        <td>纯红</td>\n                        <td title=\"\">#FF0000</td>\n                        <td>255,0,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"brown\">　</td>\n                        <td title=\"\">Brown</td>\n                        <td>棕色</td>\n                        <td title=\"\">#A52A2A</td>\n                        <td>165,42,42</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"firebrick\">　</td>\n                        <td title=\"\">FireBrick</td>\n                        <td>耐火砖</td>\n                        <td title=\"\">#B22222</td>\n                        <td>178,34,34</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkred\">　</td>\n                        <td title=\"\">DarkRed</td>\n                        <td>深红色</td>\n                        <td title=\"\">#8B0000</td>\n                        <td>139,0,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"maroon\">　</td>\n                        <td title=\"\">Maroon</td>\n                        <td>栗色</td>\n                        <td title=\"\">#800000</td>\n                        <td>128,0,0</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"white\">　</td>\n                        <td title=\"\">White</td>\n                        <td>纯白</td>\n                        <td title=\"\">#FFFFFF</td>\n                        <td>255,255,255</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"whitesmoke\">　</td>\n                        <td title=\"\">WhiteSmoke</td>\n                        <td>白烟</td>\n                        <td title=\"\">#F5F5F5</td>\n                        <td>245,245,245</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"gainsboro\">　</td>\n                        <td title=\"\">Gainsboro</td>\n                        <td>Gainsboro</td>\n                        <td title=\"\">#DCDCDC</td>\n                        <td>220,220,220</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"lightgrey\">　</td>\n                        <td title=\"\">LightGrey</td>\n                        <td>浅灰色</td>\n                        <td title=\"\">#D3D3D3</td>\n                        <td>211,211,211</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"silver\">　</td>\n                        <td title=\"\">Silver</td>\n                        <td>银白色</td>\n                        <td title=\"\">#C0C0C0</td>\n                        <td>192,192,192</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"darkgray\">　</td>\n                        <td title=\"\">DarkGray</td>\n                        <td>深灰色</td>\n                        <td title=\"\">#A9A9A9</td>\n                        <td>169,169,169</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"gray\">　</td>\n                        <td title=\"\">Gray</td>\n                        <td>灰色</td>\n                        <td title=\"\">#808080</td>\n                        <td>128,128,128</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"dimgray\">　</td>\n                        <td title=\"\">DimGray</td>\n                        <td>暗淡的灰色</td>\n                        <td title=\"\">#696969</td>\n                        <td>105,105,105</td>\n                    </tr>\n                    <tr>\n                        <td bgcolor=\"black\">　</td>\n                        <td title=\"\">Black</td>\n                        <td>纯黑</td>\n                        <td title=\"\">#000000</td>\n                        <td>0,0,0</td>\n                    </tr>\n                </tbody>\n            </table>\n        </div>\n    </center>\n</div>"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom.py",
    "content": "import os, copy, atexit, time, gzip, threading, setproctitle\nimport numpy as np\nfrom multiprocessing import Process\nfrom UTIL.colorful import *\nfrom UTIL.network import get_host_ip, find_free_port\nfrom .mcom_def import fn_names, align_names, find_where_to_log\n\n\nclass mcom():\n    \"\"\"\n        2D/3D visualizer interface\n        The Design Principle: Under No Circumstance should this program interrupt the main program!\n        args:\n            draw_mode: ('Web', 'Native', 'Img', 'Threejs')\n            rapid_flush: flush data instantly. set 'False' if you'd like your SSD to survive longer\n            digit: the precision of float number. Choose from -1 (auto), 4, 8, 16\n            tag: give a name for debugging when multiple mcom object is used\n            resume_mod: resume previous session\n            resume_file: resume from which file\n            image_path: if draw_mode=='Img', where to save image\n            figsize: if draw_mode=='Img', determine the size of the figure, default is (12, 6)\n            rec_exclude: if draw_mode=='Img', blacklist some vars\n    \"\"\"\n    def __init__(self, path=None, digit=-1, rapid_flush=True, draw_mode=\"Img\", tag='default', resume_mod=False, **kargs):\n        self.draw_mode = draw_mode\n        self.rapid_flush = rapid_flush\n        self.path = path\n        self.digit = digit\n        self.tag = tag\n        self.resume_mod = resume_mod\n        self.kargs = kargs\n        if self.kargs is None: self.kargs = {}\n        \n        self.flow_cnt = 0\n\n        if draw_mode in ['Web', 'Native', 'Img', 'Threejs']:\n            self.draw_process = True\n            self.init_draw_subprocess()\n            if draw_mode in ['Web', 'Native', 'Img']:\n                self.init_2d_kernel()\n        else:\n            print亮红('[mcom.py]: Draw process off! No plot will be done')\n            self.draw_process = False\n\n        atexit.register(lambda: self.__del__())\n\n    def init_draw_subprocess(self):\n        port = find_free_port()\n        print红('[mcom.py]: draw process active!')\n        self.draw_tcp_port = ('localhost', port)\n        self.kargs.update({\n            'draw_mode': self.draw_mode,\n            'draw_udp_port': self.draw_tcp_port,\n            'port': self.draw_tcp_port,\n            'backup_file': self.path + '/backup.dp.gz'\n        })\n        DP = DrawProcess if self.draw_mode != 'Threejs' else DrawProcessThreejs\n        self.draw_proc = DP(**self.kargs)\n        self.draw_proc.start()\n        from UTIL.network import QueueOnTcpClient\n        self.draw_tcp_client = QueueOnTcpClient('localhost:%d'%port)\n\n\n\n    def init_2d_kernel(self):\n        if self.resume_mod:\n            if \"resume_file\" in self.kargs:\n                # if resume_file is specified, use it\n                self.starting_file = self.kargs[\"resume_file\"]\n            else:\n                # otherwise find previous log path\n                _, _, self.current_buffer_index = find_where_to_log(self.path)\n                self.starting_file = self.path + '/mcom_buffer_%d____starting_session.txt' % (self.current_buffer_index-1)\n            # open the previous file, transfer previous data\n            self.file_handle = open(self.starting_file, 'r', encoding = \"utf-8\")\n            for line in self.file_handle.readlines(): self.draw_tcp_client.send_str(line)\n            self.file_handle.close()\n            print蓝('previous data transfered')\n            # open this file again with append mode\n            self.file_handle = open(self.starting_file, 'a+', encoding = \"utf-8\")\n\n        else:\n            _, _, self.current_buffer_index = find_where_to_log(self.path)\n            self.starting_file = self.path + '/mcom_buffer_%d____starting_session.txt' % (self.current_buffer_index)\n            print蓝('[mcom.py]: log file at:' + self.starting_file)\n            self.file_handle = open(self.starting_file, 'w+', encoding = \"utf-8\")\n\n\n    # on the end of the program\n    def __del__(self):\n        if hasattr(self,'_deleted_'): return    # avoid exit twice\n        else: self._deleted_ = True     # avoid exit twice\n        # print红('[mcom.py]: mcom exiting! tag: %s'%self.tag)\n        if hasattr(self, 'file_handle') and self.file_handle is not None:\n            end_file_flag = ('><EndTaskFlag\\n')\n            self.file_handle.write(end_file_flag)\n            self.file_handle.close()\n        if hasattr(self, 'port') and self.port is not None:\n            self.disconnect()\n        if hasattr(self, 'draw_proc') and self.draw_proc is not None:\n            try:\n                self.draw_proc.terminate()\n                self.draw_proc.join()\n            except:\n                pass\n        # print蓝('[mcom.py]: mcom exited! tag: %s'%self.tag)\n\n\n    def disconnect(self):\n        # self.draw_udp_client.close()\n        self.draw_tcp_client.close()\n\n\n    def recall(self, starting_file):\n        with open(starting_file,'rb') as f:\n            lines = f.readlines()\n        r = None\n        for l in lines:\n            if 'rec_show' in str(l, encoding='utf8'): \n                r = copy.deepcopy(l)\n                continue\n            self.draw_tcp_client.send_str(l)\n        if r is not None:\n            self.draw_tcp_client.send_str(r)\n        return None\n\n    '''\n        mcom core function: send out/write str\n    '''\n    def send(self, data):\n        # step 1: send directive to draw process\n        if self.draw_process: \n            self.draw_tcp_client.send_str(data)\n\n\n        # step 2: add to file\n        if self.draw_mode=='Threejs': return\n        self.file_handle.write(data)\n        if self.rapid_flush: \n            self.file_handle.flush()\n        elif self.flow_cnt>500:\n            self.file_handle.flush()\n            self.flow_cnt = 0\n        else:\n            self.flow_cnt += 1\n        return\n\n\n    def rec_init(self, color='k'):\n        str_tmp = '>>rec_init(\\'%s\\')\\n' % color\n        self.send(str_tmp)\n\n    def rec_show(self):\n        self.send('>>rec_show\\n')\n\n    def rec_end(self):\n        self.send('>>rec_end\\n')\n\n    def rec_save(self):\n        self.send('>>rec_save\\n')\n\n    def rec_end_hold(self):\n        self.send('>>rec_end_hold\\n')\n\n    def rec_clear(self, name):\n        str_tmp = '>>rec_clear(\"%s\")\\n' % (name)\n        self.send(str_tmp)\n\n    def rec(self, value, name):\n        value = float(value)\n        if self.digit == -1:\n            str_tmp = '>>rec(%.16g,\"%s\")\\n' % (value, name)\n        elif self.digit == 16:\n            str_tmp = '>>rec(%.16e,\"%s\")\\n' % (value, name)\n        elif self.digit == 8:\n            str_tmp = '>>rec(%.8e,\"%s\")\\n' % (value, name)\n        elif self.digit == 4:\n            str_tmp = '>>rec(%.4e,\"%s\")\\n' % (value, name)\n        self.send(str_tmp)\n\n    def other_cmd(self, func_name, *args, **kargs):\n        strlist = ['>>', func_name, '(']\n        for _i_ in range(len(args)):\n            if isinstance(args[_i_], np.ndarray):\n                strlist = self._process_ndarray(args[_i_], strlist)\n            else:\n                strlist = self._process_scalar(args[_i_], strlist)\n        if len(kargs)>0:\n            for _key_ in kargs:\n                if isinstance(kargs[_key_], np.ndarray):\n                    strlist = self._process_ndarray(kargs[_key_], strlist, _key_)\n                else:\n                    strlist = self._process_scalar(kargs[_key_], strlist, _key_)\n        if strlist[len(strlist) - 1] == \"(\": strlist.append(\")\\n\")\n        else: strlist[len(strlist) - 1] = \")\\n\" # 把逗号换成后括号\n        self.send(''.join(strlist))\n\n    def _process_scalar(self, arg, strlist,key=None):\n        if key is not None: strlist += '%s='%key\n        if isinstance(arg, int):\n            strlist.append(\"%d\" % arg)\n            strlist.append(\",\")\n        elif isinstance(arg, float):\n            if self.digit == -1:    strlist.append(\"%.16g\" % arg)\n            elif self.digit == 16:  strlist.append(\"%.16e\" % arg)\n            elif self.digit == 8:   strlist.append(\"%.8e\" % arg)\n            elif self.digit == 4:   strlist.append(\"%.4e\" % arg)\n            strlist.append(\",\")\n        elif isinstance(arg, str):\n            assert '$' not in arg\n            strlist.extend([\"\\'\", arg.replace('\\n', '$'), \"\\'\", \",\"])\n        elif isinstance(arg, list):\n            strlist.append(str(arg))\n            strlist.append(\",\")\n        elif hasattr(arg, 'dtype') and np.issubdtype(arg.dtype, np.integer):\n            strlist.append(\"%d\" % arg)\n            strlist.append(\",\")\n        elif hasattr(arg, 'dtype') and np.issubdtype(arg.dtype, np.floating):\n            if self.digit == -1:    strlist.append(\"%.16g\" % arg)\n            elif self.digit == 16:  strlist.append(\"%.16e\" % arg)\n            elif self.digit == 8:   strlist.append(\"%.8e\" % arg)\n            elif self.digit == 4:   strlist.append(\"%.4e\" % arg)\n            strlist.append(\",\")\n        else:\n            print('unknown input type | 输入的参数类型不能处理', arg.__class__)\n        return strlist\n\n    def _process_ndarray(self, args, strlist, key=None):\n        if args.ndim == 1:\n            if key is not None: strlist += '%s='%key\n            d = len(args)\n            sub_list = [\"[\"] + [\"%.3e,\"%t if (i+1)!=d else \"%.3e\"%t for i, t in enumerate(args)] + [\"]\"]\n            strlist += sub_list\n            strlist.append(\",\")\n        else:\n            print红('[mcom]: input dimension > 1, unable to process | 输入数组的维度大于2维')\n        return strlist\n\n    for fn_name in fn_names:\n        build_exec_cmd = 'def %s(self,*args,**kargs):\\n self.other_cmd(\"%s\", *args,**kargs)\\n'%(fn_name, fn_name)\n        exec(build_exec_cmd)\n\n    for align, fn_name in align_names:\n        build_exec_cmd = '%s = %s\\n'%(align, fn_name)\n        exec(build_exec_cmd)\n\n\n\n\n\n\n\n\n\n\n\n\nclass DrawProcessThreejs(Process):\n    def __init__(self, draw_udp_port, draw_mode, **kargs):\n        super(DrawProcessThreejs, self).__init__()\n        from UTIL.network import QueueOnTcpServer\n        self.draw_mode = draw_mode\n        self.draw_udp_port = draw_udp_port\n        self.tcp_connection = QueueOnTcpServer(self.draw_udp_port)\n        self.buffer_list = []\n        self.backup_file = kargs['backup_file']\n        self.allow_backup = False if self.backup_file is None else True\n        if self.allow_backup:\n            if os.path.exists(self.backup_file):\n                print亮红('[mcom.py]: warning, purge previous 3D visual data!')\n                try: os.remove(self.backup_file)\n                except: pass\n            self.tflush_buffer = []\n        self.client_tokens = {}\n\n    def flush_backup(self):\n        while True:\n            time.sleep(20)\n            if not os.path.exists(os.path.dirname(self.backup_file)):\n                os.makedirs(os.path.dirname(self.backup_file))\n            # print('Flush backup')\n            with gzip.open(self.backup_file, 'at') as f:\n                f.writelines(self.tflush_buffer)\n            self.tflush_buffer = []\n            # print('Flush backup done')\n\n    def init_threejs(self):\n        t = threading.Thread(target=self.run_flask, args=(find_free_port(),))\n        t.daemon = True\n        t.start()\n\n        if self.allow_backup:\n            self.tflush = threading.Thread(target=self.flush_backup)\n            self.tflush.daemon = True\n            self.tflush.start()\n\n    def run(self):\n        setproctitle.setproctitle('ThreejsVisualWorker')\n        \n        self.init_threejs()\n        try:\n            from queue import Empty\n            queue = self.tcp_connection.get_queue()\n            self.tcp_connection.wait_connection() # after this, the queue begin to work\n            while True:\n                buff_list = []\n                buff_list.extend(queue.get(timeout=600))\n                for _ in range(queue.qsize()): buff_list.extend(queue.get(timeout=600))\n                self.run_handler(buff_list)\n        except KeyboardInterrupt:\n            self.__del__()\n        self.__del__()\n\n    def __del__(self):\n        return\n        \n    def run_handler(self, new_buff_list):\n        self.buffer_list.extend(new_buff_list)\n        self.tflush_buffer.extend(new_buff_list)\n\n        # too many, delete with fifo\n        if len(self.buffer_list) > 1e9: \n            # 当存储的指令超过十亿后，开始删除旧的\n            del self.buffer_list[:len(new_buff_list)]\n\n    def run_flask(self, port):\n        from flask import Flask, request, send_from_directory\n        from waitress import serve\n        from mimetypes import add_type\n        add_type('application/javascript', '.js')\n        add_type('text/css', '.css')\n\n        app = Flask(__name__)\n        dirname = os.path.dirname(__file__) + '/threejsmod'\n        import zlib\n\n        self.init_cmd_captured = False\n        init_cmd_list = []\n        def init_cmd_capture_fn(tosend):\n            for strx in tosend:\n                if '>>v2d_show()\\n'==strx:\n                    self.init_cmd_captured = True\n                init_cmd_list.append(strx)    \n                if self.init_cmd_captured:\n                    break\n            return\n            \n        @app.route(\"/up\", methods=[\"POST\"])\n        def up():\n\n            # 本次正常情况下，需要发送的数据\n            # dont send too much in one POST, might overload the network traffic\n            if len(self.buffer_list)>35000:\n                tosend = self.buffer_list[:30000]\n                self.buffer_list = self.buffer_list[30000:]\n            else:\n                tosend = self.buffer_list\n                self.buffer_list = []\n\n            # 处理断线重连的情况，断线重连时，会出现新的token\n            token = request.data.decode('utf8')\n            if token not in self.client_tokens:\n                print('[mcom.py] Establishing new connection, token:', token)\n                self.client_tokens[token] = 'connected'\n                if (len(self.client_tokens)==0) or (not self.init_cmd_captured):  \n                    # 尚未捕获初始化命令，或者第一次client \n                    buf = \"\".join(tosend)\n                else:\n                    print('[mcom.py] If there are other tabs, please close them now.')\n                    buf = \"\".join(init_cmd_list + tosend)\n            else:\n                # 正常连接\n                buf = \"\".join(tosend)\n\n            # 尝试捕获并保存初始化部分的命令\n            if not self.init_cmd_captured:\n                init_cmd_capture_fn(tosend)\n\n            # use zlib to compress output command, worked out like magic\n            buf = bytes(buf, encoding='utf8')   \n            zlib_compress = zlib.compressobj()\n            buf = zlib_compress.compress(buf) + zlib_compress.flush(zlib.Z_FINISH)\n            return buf\n\n        @app.route(\"/<path:path>\")\n        def static_dirx(path):\n            if path=='favicon.ico': \n                return send_from_directory(\"%s/\"%dirname, 'files/HMP.ico')\n            return send_from_directory(\"%s/\"%dirname, path)\n\n        @app.route(\"/\")\n        def main_app():\n            with open('%s/examples/abc.html'%dirname, 'r', encoding = \"utf-8\") as f:\n                buf = f.read()\n            return buf\n\n        print('\\n--------------------------------')\n        print('JS visualizer online: http://%s:%d'%(get_host_ip(), port))\n        print('JS visualizer online (localhost): http://localhost:%d'%(port))\n        print('--------------------------------')\n        # app.run(host='0.0.0.0', port=port)\n        serve(app, threads=8, ipv4=True, ipv6=True, listen='*:%d'%port)\n\n\nclass DrawProcess(Process):\n    def __init__(self, draw_udp_port, draw_mode, **kargs):\n        from UTIL.network import QueueOnTcpServer\n        super(DrawProcess, self).__init__()\n        self.draw_mode = draw_mode\n        self.draw_udp_port = draw_udp_port\n        self.tcp_connection = QueueOnTcpServer(self.draw_udp_port)\n        self.kwargs = kargs\n\n        return\n\n    def init_matplot_lib(self):\n        if self.draw_mode in ['Web', 'Img']:\n            import matplotlib\n            matplotlib.use('Agg') # set the backend before importing pyplot\n            import matplotlib.pyplot as plt\n            self.gui_reflesh = lambda: time.sleep(1) # plt.pause(0.1)\n        elif self.draw_mode == 'Native':\n            import matplotlib\n            # matplotlib.use('Agg') # set the backend before importing pyplot\n            matplotlib.use('Qt5Agg')\n            import matplotlib.pyplot as plt\n            self.gui_reflesh = lambda: plt.pause(0.2)\n        elif self.draw_mode == 'Threejs':\n            assert False\n        else:\n            assert False\n\n        from config import GlobalConfig\n        logdir = GlobalConfig.logdir\n        if not os.path.exists(logdir):\n            os.makedirs(logdir)\n        if self.draw_mode == 'Web':\n            self.avail_port = find_free_port()\n            my_http = MyHttp('%s/html.html'%logdir, self.avail_port)\n            my_http.daemon = True\n            my_http.start()\n    \n        self.libs_family = {\n            \"rec_disable_percentile_clamp\": 'rec', \"rec_enable_percentile_clamp\": 'rec',\n            'rec_init': 'rec', 'rec': 'rec', 'rec_show': 'rec',\n            'v2d_init': 'v2d', 'v2dx':'v2d', 'v2d_show': 'v2d', 'v2d_pop':'v2d',\n            'v2d_line_object':'v2d', 'v2d_clear':'v2d', 'v2d_add_terrain': 'v2d',\n        }\n        self.libs_init_fns = {\n            'rec': self.rec_init_fn,\n            'v2d': self.v2d_init_fn,\n        }\n\n    def run(self):\n        setproctitle.setproctitle('HmapPlotProcess')\n        self.init_matplot_lib()\n        try:\n            # self.tcp_connection.set_handler(self.run_handler)\n            from queue import Empty\n            queue = self.tcp_connection.get_queue()\n            # self.tcp_connection.set_handler(self.run_handler)\n            self.tcp_connection.wait_connection() # after this, the queue begin to work\n            while True:\n                try: \n                    buff_list = []\n                    buff_list.extend(queue.get(timeout=0.1))\n                    for _ in range(queue.qsize()): buff_list.extend(queue.get(timeout=0.1))\n                    self.run_handler(buff_list)\n                except Empty: self.gui_reflesh()\n\n        except KeyboardInterrupt:\n            self.__del__()\n        self.__del__()\n\n    def run_handler(self, buff_list):\n        while True:\n            if len(buff_list) == 0: break\n            buff = buff_list.pop(0)\n            if (buff=='>>rec_show\\n') and ('>>rec_show\\n' in buff_list): continue # skip\n            self.process_cmd(buff)\n\n        #     # print('成功处理指令:', buff)\n\n    def __del__(self):\n        self.tcp_connection.close()\n\n\n\n    def process_cmd(self, cmd_str):\n        if '>>' in cmd_str:\n            cmd_str_ = cmd_str[2:].strip('\\n')\n            if ')' not in cmd_str_:\n                cmd_str_ = cmd_str_+'()'\n            prefix = self.get_cmd_lib(cmd_str_)\n            if prefix is not None: \n                eval('%s.%s'%(prefix, cmd_str_))\n\n    def get_cmd_lib(self, cmd):\n        cmd_key = None\n        func_name = cmd.split('(')[0]\n        if func_name not in self.libs_family:\n            print蓝('绘图函数不能处理：', cmd)\n            return None\n        family_name = self.libs_family[func_name]\n        if self.libs_init_fns[family_name] is not None:\n            self.libs_init_fns[family_name]()\n            self.libs_init_fns[family_name] = None\n        return 'self.%s'%family_name\n\n    def rec_init_fn(self):\n        from VISUALIZE.mcom_rec import rec_family\n        self.rec = rec_family('r', \n            self.draw_mode, \n            **self.kwargs\n        )\n\n    def v2d_init_fn(self):\n        from VISUALIZE.mcom_v2d import v2d_family\n        self.v2d = v2d_family(self.draw_mode)\n\n\n\n\nclass MyHttp(Process):\n    def __init__(self, path_to_html, avail_port):\n        super(MyHttp, self).__init__()\n        self.path_to_html = path_to_html\n        self.avail_port = avail_port\n\n    def run(self):\n        from flask import Flask\n        app = Flask(__name__)\n        @app.route(\"/\")\n        def hello():\n            try:\n                with open(self.path_to_html,'r') as f:\n                    html = f.read()\n            except:\n                html = \"no plot yet please wait\"\n            return html\n        app.run(port=self.avail_port)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_def.py",
    "content": "import os\n\nfn_names = [\n    \"v2dx\", \"flash\", \"plot\", \"figure\", \"hold\", \"box\", \"pause\", \"clf\", \"xlim\", \"ylim\", \"xlabel\", \n    \"ylabel\", \"drawnow\", \"v2d\", \"v2d_init\", \"v3d_init\", \"v2L\", \"title\", \"plot3\", \"grid\", \"v3dx\", \"v2d_show\", \n    \"v2d_pop\", \"v2d_line_object\", \"v2d_clear\", \"v2d_add_terrain\", \"set_style\", \"set_env\", \"use_geometry\", \n    \"rec_disable_percentile_clamp\", \"rec_enable_percentile_clamp\",\n    \"geometry_rotate_scale_translate\", \"test_function_terrain\", 'line3d', 'advanced_geometry_rotate_scale_translate',\n    \"advanced_geometry_material\", \"skip\"\n]\nalign_names = [\n    ('初始化3D', 'v2d_init'),\n    ('设置样式', 'set_style'),\n    ('形状之旋转缩放和平移','geometry_rotate_scale_translate'),\n    ('其他几何体之旋转缩放和平移','advanced_geometry_rotate_scale_translate'),\n    ('其他几何体之材质','advanced_geometry_material'),\n    ('发送几何体','v2dx'),\n    ('结束关键帧','v2d_show'),\n    ('发送线条','line3d'),\n    ('发射光束','flash'),\n    ('空指令','skip'),\n]\n\ndef find_where_to_log(path):\n    if not os.path.exists(path): os.makedirs(path)\n\n    def find_previous_start_end():\n        start = None; end = None; t = 0\n        while True:\n            is_body = os.path.exists(path + '/mcom_buffer_%d.txt' % t)\n            is_head = os.path.exists(path + '/mcom_buffer_%d____starting_session.txt' % t)\n            if is_head: start = t\n            if is_head or is_body: end = t; t += 1\n            else:\n                new = t\n                return (start, end, new)\n\n    prev_start, prev_end, new = find_previous_start_end()\n    return prev_start, prev_end, new\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_rec.py",
    "content": "import os, fnmatch, matplotlib\nimport numpy as np\nfrom functools import lru_cache\nfrom config import GlobalConfig\n# 设置matplotlib正常显示中文和负号\n# matplotlib.rcParams['font.sans-serif']=['SimHei']   # 用黑体显示中文\n# matplotlib.rcParams['axes.unicode_minus']=False     # 正常显示负号\nStandardPlotFig = 1\nComparePlotFig = 2\nclass rec_family(object):\n    def __init__(self, colorC=None, draw_mode='Native', image_path=None, figsize=None, smooth_level=None, rec_exclude=[], **kwargs):\n        # the list of vars' name (with order), string\n        self.name_list = []\n        # the list of vars' value sequence (with order), float\n        self.line_list = []\n        # the list of vars' time sequence (with order), float\n        self.time_list = []\n        # the list of line plotting handles\n        self.line_plot_handle = []\n        self.line_plot_handle2 = []\n        # subplot list\n        self.subplots = {}\n        self.subplots2 = {}\n        # working figure handle\n        self.working_figure_handle = None\n        self.working_figure_handle2 = None\n        # recent time\n        self.current_time = None\n        self.time_index = None\n        self.smooth_level = smooth_level\n        self.figsize_given = figsize\n        self.colorC = 'k' if colorC is None else colorC\n        self.Working_path = 'Testing-beta'\n        self.image_num = -1\n        self.draw_mode = draw_mode\n        self.rec_exclude = rec_exclude\n        self.vis_95percent = True\n        self.enable_percentile_clamp = True\n        logdir = GlobalConfig.logdir\n        self.plt = None\n        if not os.path.exists(logdir):\n            os.makedirs(logdir)\n        if self.draw_mode == 'Web':\n            import matplotlib.pyplot as plt, mpld3\n            self.html_to_write = '%s/html.html'%logdir\n            self.plt = plt; self.mpld3 = mpld3\n        elif self.draw_mode =='Native':\n            import matplotlib.pyplot as plt\n            plt.ion()\n            self.plt = plt\n        elif self.draw_mode =='Img':\n            matplotlib.use('Agg')\n            import matplotlib.pyplot as plt\n            self.plt = plt\n            self.img_to_write = '%s/rec.jpg'%logdir\n            self.img_to_write2 = '%s/rec.jpeg'%logdir\n            if image_path is not None:\n                self.img_to_write = image_path\n                self.img_to_write2 = image_path+'.jpg'\n        else:\n            assert False\n            \n    def rec_disable_percentile_clamp(self):\n        self.enable_percentile_clamp = False\n\n    def rec_enable_percentile_clamp(self):\n        self.enable_percentile_clamp = True\n\n    def rec_init(self, colorC=None):\n        if colorC is not None: self.colorC = colorC\n        return\n    \n    @lru_cache(500)\n    def match_exclude(self, name):\n        for n in self.rec_exclude:\n            if fnmatch.fnmatch(name, n): return True\n        return False\n\n    @lru_cache(500)\n    def get_index(self, name):\n        return self.name_list.index(name)\n\n    def rec(self, var, name):\n        if self.match_exclude(name):\n            # if var is backlisted\n            return\n            \n        if name in self.name_list:\n            # if var is already known, skip\n            pass\n        else:\n            # if var is new, prepare lists\n            self.name_list.append(name)\n            self.line_list.append([])  #新建一个列表\n            self.time_list.append([])\n            self.line_plot_handle.append(None)\n            self.line_plot_handle2.append(None)\n        \n        # get the index of the var\n        index = self.get_index(name)\n\n        if name=='time': \n            # special var: time\n            self.current_time = var\n            if self.time_index is None:\n                self.time_index = index\n                self.handle_all_missing_time()\n            else:\n                assert self.time_index == index\n        else:\n            # normal vars: if time is available, add it\n            if self.time_index is not None:\n                if len(self.line_list[index]) != len(self.time_list[index]):\n                    self.handle_missing_time(self.line_list[index], self.time_list[index])\n                self.time_list[index].append(self.current_time)\n\n        # finally, add var value\n        self.line_list[index].append(var)\n\n    def handle_all_missing_time(self):\n        for name in self.name_list:\n            if name=='time': continue\n            index = self.get_index(name)\n            if len(self.line_list[index]) != len(self.time_list[index]):\n                self.handle_missing_time(self.line_list[index], self.time_list[index])\n\n    def handle_missing_time(self, line_arr, time_arr):\n        assert len(line_arr) > len(time_arr)\n        for i in range(len(line_arr) - len(time_arr)):\n            time_arr.append(self.current_time - i - 1)\n    \n    def get_figure_size(self, image_num):\n        if self.figsize_given is None:\n            expand_ratio = max((image_num - 10)/4, 1)\n            return (12*expand_ratio, 6*expand_ratio)\n        else:\n            return self.figsize_given\n            \n    def rec_show(self):\n        # the number of total subplots | 一共有多少条曲线\n        image_num = len(self.line_list)\n        # 是否启动高级曲线绘制\n        draw_advance_fig = False\n        for name in self.name_list:\n            if 'of=' in name: draw_advance_fig = True\n\n        if self.working_figure_handle is None:\n            self.working_figure_handle = self.plt.figure(StandardPlotFig, figsize=self.get_figure_size(image_num), dpi=100)\n            if self.draw_mode == 'Native': \n                self.working_figure_handle.canvas.set_window_title(self.Working_path)\n                self.plt.show()\n        \n        # default row=1\n        rows = 1\n\n        # check whether the time var exists 检查是否有时间轴，若有，做出修改\n        time_var_met = [False] # time_var_met is list because we need it to be mutable | 有时间轴\n        time_explicit = ('time' in self.name_list)\n        if time_explicit:\n            assert self.time_index == self.get_index('time')\n            image_num_to_show = image_num - 1\n        else:\n            image_num_to_show = image_num\n\n        if image_num_to_show >= 3:\n            rows = 2 #大与3张图，则放2行\n        if image_num_to_show > 8:\n            rows = 3 #大与8张图，则放3行\n        if image_num_to_show > 12:\n            rows = 4 #大与12张图，则放4行\n        \n        cols = int(np.ceil(image_num/rows)) #根据行数求列数\n        if self.image_num!=image_num:\n            # 需要刷新布局，所有已经绘制的图作废\n            self.subplots = {}\n            self.working_figure_handle.clf()\n            for q, handle in enumerate(self.line_plot_handle): self.line_plot_handle[q] = None\n            # 需要刷新布局，所有已经绘制的图作废\n            if draw_advance_fig: \n                self.subplots2 = {}\n                if self.working_figure_handle2 is not None: self.working_figure_handle2.clf()\n                for q, handle in enumerate(self.line_plot_handle2): self.line_plot_handle2[q] = None\n\n        self.image_num = image_num\n        self.plot_classic(image_num, rows, time_explicit, time_var_met, self.time_index, cols)\n            \n        # draw advanced figure, current disabled\n        if draw_advance_fig:\n            self.plot_advanced()\n\n        # now end, output images\n        if self.draw_mode == 'Web':\n            content = self.mpld3.fig_to_html(self.working_figure_handle)\n            with open(self.html_to_write, 'w+') as f:\n                f.write(content)\n            return\n        elif self.draw_mode == 'Native':\n            self.plt.pause(0.01)\n            return\n        elif self.draw_mode == 'Img':\n            if self.working_figure_handle is not None: \n                self.working_figure_handle.tight_layout()\n                self.working_figure_handle.savefig(self.img_to_write)\n            if self.working_figure_handle2 is not None: \n                self.working_figure_handle2.tight_layout()\n                self.working_figure_handle2.savefig(self.img_to_write2)\n\n    def smooth(self, data, sm_lv=1):\n        if sm_lv > 1:\n            y = np.ones(sm_lv)*1.0/sm_lv\n            d = np.convolve(y, data, 'same')#\"same\")\n        else:\n            d = data\n        return np.array(d)\n\n    def plot_advanced(self):\n        #画重叠曲线，如果有的话\n\n        group_name = []\n        group_member = []\n        time_explicit = ('time' in self.name_list)\n        \n        image_num = len(self.line_list)\n        for index in range(image_num):\n            if 'of=' not in self.name_list[index]:\n                #没有的直接跳过\n                continue\n            # 找出组别\n            g_name_ = self.name_list[index].split('of=')[0]\n            if g_name_ in group_name:\n                i = group_name.index(g_name_)\n                group_member[i].append(index)\n            else:\n                group_name.append(g_name_)\n                group_member.append([index])\n\n        \n        num_group = len(group_name)\n        image_num_multi = num_group\n        rows = 1\n        if image_num_multi >= 3:\n            rows = 2 #大与3张图，则放2行\n        if image_num_multi > 8:\n            rows = 3 #大与8张图，则放3行\n        if image_num_multi > 12:\n            rows = 4 #大与12张图，则放4行\n        \n        cols = int(np.ceil(image_num_multi/rows))#根据行数求列数\n\n        if self.working_figure_handle2 is None:\n            self.working_figure_handle2 = self.plt.figure(ComparePlotFig, figsize=self.get_figure_size(num_group), dpi=100)\n            if self.draw_mode == 'Native': \n                self.working_figure_handle2.canvas.set_window_title('Working-Comp')\n                self.plt.show()\n        \n        for i in range(num_group):\n\n            subplot_index = i+1\n            subplot_name = '%d,%d,%d'%(rows,cols,subplot_index)\n            if subplot_name in self.subplots2: \n                target_subplot = self.subplots2[subplot_name]\n            else:\n                target_subplot = self.working_figure_handle2.add_subplot(rows,cols,subplot_index)\n                self.subplots2[subplot_name] = target_subplot\n\n            tar_true_name=group_name[i]\n            num_member = len(group_member[i])\n\n            _xdata_min_ = np.inf\n            _xdata_max_ = -np.inf\n            _ydata_min_ = np.inf\n            _ydata_max_ = -np.inf\n\n            for j in range(num_member):\n                index = group_member[i][j]\n\n                _ydata_ = np.array(self.line_list[index], dtype=np.double)\n                if self.smooth_level is not None:\n                    _ydata_ = self.smooth(_ydata_, sm_lv=self.smooth_level)\n                # 如果有时间数据，把x轴绑定时间\n                if time_explicit:\n                    _xdata_ = np.array(self.time_list[index], dtype=np.double)\n                else:\n                    _xdata_ = np.arange(len(self.line_list[index]), dtype=np.double)\n\n                limx1 = _xdata_.min() \n                if limx1 < _xdata_min_: _xdata_min_ = limx1\n                limx2 = _xdata_.max()\n                if limx2 > _xdata_max_: _xdata_max_ = limx2\n                limy1 = _ydata_.min()\n                if limy1 < _ydata_min_: _ydata_min_ = limy1\n                limy2 = _ydata_.max()\n                if limy2 > _ydata_max_: _ydata_max_ = limy2\n\n                name_tmp = self.name_list[index]\n                name_tmp = name_tmp.replace('=',' ')\n                if (self.line_plot_handle2[index] is None):\n                    # 第一次绘制\n                    if time_explicit:\n                        self.line_plot_handle2[index], =  target_subplot.plot(_xdata_, _ydata_, lw=1,label=name_tmp)\n                    else:\n                        self.line_plot_handle2[index], =  target_subplot.plot(_ydata_, lw=1, label=name_tmp)\n                else:\n                    # 非第一次，则只需要更新数据即可\n                    self.line_plot_handle2[index].set_data((_xdata_, _ydata_))\n\n            #标题\n            target_subplot.set_title(tar_true_name)\n            target_subplot.set_xlabel('time')\n            target_subplot.set_ylabel(tar_true_name)\n            self.change_target_figure_lim(target_subplot, _xdata_min_, _xdata_max_, _ydata_min_, _ydata_max_)\n            target_subplot.grid(visible=True)\n            target_subplot.legend(loc='best')\n\n\n    def plot_classic(self, image_num, rows, time_explicit, time_var_met, time_index, cols):\n        for index in range(image_num):\n            if time_explicit:\n                if time_index == index:\n                    time_var_met[0] = True \n                    # skip time var\n                    continue\n            # 有时间轴时，因为不绘制时间，所以少算一个subplot\n            subplot_index = index if time_var_met[0] else index+1\n            subplot_name = '%d,%d,%d'%(rows,cols,subplot_index)\n            if subplot_name in self.subplots: \n                target_subplot = self.subplots[subplot_name]\n            else:\n                target_subplot = self.working_figure_handle.add_subplot(rows,cols,subplot_index)\n                self.subplots[subplot_name] = target_subplot\n\n            _ydata_ = np.array(self.line_list[index], dtype=np.double)\n            # 如果有时间数据，把x轴绑定时间\n            if time_explicit:\n                _xdata_ = np.array(self.time_list[index], dtype=np.double)\n            else:\n                _xdata_ = np.arange(len(self.line_list[index]), dtype=np.double)\n\n            if (self.line_plot_handle[index] is None):\n                # 第一次绘制\n                if time_explicit:\n                    self.line_plot_handle[index], =  target_subplot.plot(_xdata_, _ydata_, lw=1,c=self.colorC)\n                else:\n                    self.line_plot_handle[index], =  target_subplot.plot(_ydata_, lw=1, c=self.colorC)\n                        \n            else:\n                # 后续绘制，更新数据\n                self.line_plot_handle[index].set_data((_xdata_, _ydata_))\n\n            if 'of=' in self.name_list[index]:\n                #把等号替换成空格\n                name_tmp = self.name_list[index]\n                name_tmp = name_tmp.replace('=',' ')\n                target_subplot.set_title(name_tmp)\n                target_subplot.set_xlabel('time')\n                target_subplot.set_ylabel(name_tmp)\n                target_subplot.grid(visible=True)\n            else:\n                target_subplot.set_title(self.name_list[index])\n                target_subplot.set_xlabel('time')\n                target_subplot.set_ylabel(self.name_list[index])\n                target_subplot.grid(visible=True)\n\n            _xdata_min_ = _xdata_.min() #target_subplot.dataLim.xmin\n            _xdata_max_ = _xdata_.max() #target_subplot.dataLim.xmax\n            _ydata_min_ = _ydata_.min() #min(self.line_list[index])\n            _ydata_max_ = _ydata_.max() #max(self.line_list[index])\n\n            if self.enable_percentile_clamp and len(_ydata_)>220 and self.vis_95percent:\n                limy1 = np.percentile(_ydata_, 3, interpolation='midpoint') # 3%\n                limy2 = np.percentile(_ydata_, 97, interpolation='midpoint') # 97%\n\n            self.change_target_figure_lim(target_subplot, _xdata_min_, _xdata_max_, _ydata_min_, _ydata_max_)\n\n    def change_target_figure_lim(self, target_subplot, limx1, limx2, limy1, limy2):\n        if limy1!=limy2:\n            meany = limy1/2 + limy2/2\n            limy1 = (limy1 - meany)*1.2+meany\n            limy2 = (limy2 - meany)*1.2+meany\n            target_subplot.set_ylim(limy1,limy2)\n\n        if limx1 != limx2:\n            meanx = limx1/2 + limx2/2\n            limx1 = (limx1 - meanx)*1.1+meanx\n            limx2 = (limx2 - meanx)*1.1+meanx\n            target_subplot.set_xlim(limx1,limx2)\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_replay.py",
    "content": "# import os\n# print(os.getcwd())\nimport os, sys, gzip\nimport argparse\nfrom VISUALIZE.mcom import *\n\n# DEBUG_OOM = True\n\nclass RecallProcessThreejs(Process):\n    def __init__(self, file_path, port):\n        super(RecallProcessThreejs, self).__init__()\n        self.buffer_list = []\n        self.file_path = file_path\n        self.port = port\n        self.client_send_pointer = {}\n\n    def init_threejs(self):\n        import threading\n        t = threading.Thread(target=self.run_flask, args=(self.port,))\n        t.daemon = True\n        t.start()\n\n    def __del__(self):\n        pass\n    \n    def run(self):\n        self.init_threejs()\n        try:\n            new_buff_list = []\n            with gzip.open(self.file_path, 'rt') as zip:\n                try:\n                    for line in zip:\n                        new_buff_list.append(line)\n                        if len(new_buff_list) > 1e2:\n                            self.run_handler(new_buff_list)\n                            new_buff_list = []\n                except:\n                    print('File has bad ending! EOFError: Compressed file ended before the end-of-stream marker was reached!')\n                    print('存档的末尾是破碎的, 少量数据可能丢失了. 完整的部分已经读取完成.')\n            self.run_handler(new_buff_list)\n            new_buff_list = []\n            # if DEBUG_OOM:\n            #     for i in range(100):\n            #         print(i)\n            #         with gzip.open(self.file_path, 'rt') as zip:\n            #             try:\n            #                 for line in zip:\n            #                     if 'v2d_init' in line: continue\n            #                     new_buff_list.append(line)\n            #                     if len(new_buff_list) > 1e2:\n            #                         self.run_handler(new_buff_list)\n            #                         new_buff_list = []\n            #             except:\n            #                 print('File has bad ending! EOFError: Compressed file ended before the end-of-stream marker was reached!')\n            #                 print('存档的末尾是破碎的, 少量数据可能丢失了. 完整的部分已经读取完成.')\n            #         self.run_handler(new_buff_list)\n            #         new_buff_list = []\n            while True: \n                time.sleep(1000)\n        except KeyboardInterrupt:\n            self.__del__()\n\n        self.__del__()\n\n    def run_handler(self, new_buff_list):\n        self.buffer_list.extend(new_buff_list)\n        # too many, delete with fifo\n        if len(self.buffer_list) > 1e9: # 当存储的指令超过十亿后，开始删除旧的\n            del self.buffer_list[:len(new_buff_list)]\n\n    def run_flask(self, port):\n        from flask import Flask, url_for, jsonify, request, send_from_directory, redirect\n        from waitress import serve\n        from mimetypes import add_type\n        add_type('application/javascript', '.js')\n        add_type('text/css', '.css')\n\n        app = Flask(__name__)\n        dirname = os.path.dirname(__file__) + '/threejsmod'\n        import zlib\n\n        @app.route(\"/up\", methods=[\"POST\"])\n        def upvote():\n            # dont send too much in one POST, might overload the network traffic\n            token = request.data.decode('utf8')\n            if token not in self.client_send_pointer:\n                print('[mcom_replay.py] Establishing new connection, token:', token)\n                current_pointer = 0\n            else:\n                current_pointer = self.client_send_pointer[token]\n\n            if len(self.buffer_list)-current_pointer>35000:\n                tosend = self.buffer_list[current_pointer:current_pointer+30000]\n                current_pointer = current_pointer+30000\n            else:\n                tosend = self.buffer_list[current_pointer:]\n                current_pointer = len(self.buffer_list)\n                \n            self.client_send_pointer[token] = current_pointer\n\n            # use zlib to compress output command, worked out like magic\n            buf = \"\".join(tosend)\n            buf = bytes(buf, encoding='utf8')   \n            zlib_compress = zlib.compressobj()\n            buf = zlib_compress.compress(buf) + zlib_compress.flush(zlib.Z_FINISH)\n            return buf\n\n        @app.route(\"/<path:path>\")\n        def static_dirx(path):\n            if path=='favicon.ico': \n                return app.send_static_file('%s/files/favicon.ico'%dirname)\n            return send_from_directory(\"%s/\"%dirname, path)\n\n        @app.route(\"/\")\n        def main_app():\n            with open('%s/examples/abc.html'%dirname, 'r', encoding = \"utf-8\") as f:\n                buf = f.read()\n            return buf\n\n        print('\\n--------------------------------')\n        print('JS visualizer online: http://%s:%d'%(get_host_ip(), port))\n        print('JS visualizer online (localhost): http://localhost:%d'%(port))\n        print('--------------------------------')\n        # app.run(host='0.0.0.0', port=port)\n        serve(app, threads=8, ipv4=True, ipv6=True, listen='*:%d'%port)\n\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(description='HMP')\n    parser.add_argument('-p', '--path', help='directory of chosen file')\n    args, unknown = parser.parse_known_args()\n    if hasattr(args, 'path'):\n        path = args.path\n    else:\n        assert False, (r\"parser.add_argument('-p', '--path', help='The node name is?')\")\n\n    load_via_json = (hasattr(args, 'cfg') and args.cfg is not None)\n    \n    rp = RecallProcessThreejs(path)\n    rp.daemon = True\n    rp.start()\n    rp.join()\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_rt.py",
    "content": "import os, copy, atexit, time, gzip, threading, zlib, asyncio\nimport numpy as np\nfrom colorama import init\nfrom multiprocessing import Process\nfrom UTIL.colorful import *\nfrom UTIL.network import get_host_ip, find_free_port\n\nmcom_fn_list_define = [\n    \"v2dx\", \"flash\", \"plot\", \"figure\", \"hold\", \"box\", \"pause\", \"clf\", \"xlim\", \"ylim\", \"xlabel\", \n    \"ylabel\", \"drawnow\", \"v2d\", \"v2d_init\", \"v3d_init\", \"v2L\", \"title\", \"plot3\", \"grid\", \"v3dx\", \"v2d_show\", \n    \"v2d_pop\", \"v2d_line_object\", \"v2d_clear\", \"v2d_add_terrain\", \"set_style\", \"set_env\", \"use_geometry\", \n    \"geometry_rotate_scale_translate\", \"test_function_terrain\", 'line3d', 'advanced_geometry_rotate_scale_translate',\n    \"advanced_geometry_material\", \"skip\"\n]\n别名对齐 = [\n    ('初始化3D', 'v2d_init'),\n    ('设置样式', 'set_style'),\n    ('形状之旋转缩放和平移','geometry_rotate_scale_translate'),\n    ('其他几何体之旋转缩放和平移','advanced_geometry_rotate_scale_translate'),\n    ('其他几何体之材质','advanced_geometry_material'),\n    ('发送几何体','v2dx'),\n    ('结束关键帧','v2d_show'),\n    ('发送线条','line3d'),\n    ('发射光束','flash'),\n    ('空指令','skip'),\n]\n\n# The Design Principle: Under No Circumstance should this program Interrupt the main program!\nclass mcom():\n    def __init__(self, path=None, digit=8, rapid_flush=True, draw_mode=False, tag='default', **kargs):\n        # digit 默认8，可选4,16，越小程序负担越轻 (All data is float, you do not need anything else)\n        # rapid_flush 当数据流不大时，及时倾倒文件缓存内容 (set 'False' if you'd like your SSD to survive longer)\n        self.draw_mode = draw_mode\n        self.path = path\n        self.digit = digit\n        self.tag = tag\n        if kargs is None: kargs = {}\n\n        if draw_mode in ['Web', 'Native', 'Img', 'Threejs']:\n            self.draw_process = True\n            port = find_free_port()\n            print红('[mcom.py]: draw process active!')\n            self.draw_tcp_port = ('localhost', port)\n            kargs.update({\n                'draw_mode': draw_mode,\n                'draw_udp_port': self.draw_tcp_port,\n                'port': self.draw_tcp_port,\n                'backup_file': self.path + '/backup.dp.gz'\n            })\n            DP = DrawProcess if draw_mode != 'Threejs' else DrawProcessThreejs\n            self.draw_proc = DP(**kargs)\n            self.draw_proc.start()\n            from UTIL.network import QueueOnTcpClient\n            self.draw_tcp_client = QueueOnTcpClient('localhost:%d'%port)\n        else:\n            print亮红('[mcom.py]: Draw process off! No plot will be done')\n            self.draw_process = False\n\n\n        if not self.draw_mode=='Threejs':\n            _, _, self.current_buffer_index = find_where_to_log(self.path)\n            self.starting_file = self.path + '/mcom_buffer_%d____starting_session.txt' % (self.current_buffer_index)\n            self.file_lines_cnt = 0\n            self.file_max_lines = 5e8  # limit file lines to avoid a very large file\n            self.rapid_flush = rapid_flush\n            self.flow_cnt = 0\n            print蓝('[mcom.py]: log file at:' + self.starting_file)\n            self.current_file_handle = open(self.starting_file, 'w+', encoding = \"utf-8\")\n\n        atexit.register(lambda: self.__del__())\n\n\n    # on the end of the program\n    def __del__(self):\n        if hasattr(self,'_deleted_'): return    # avoid exit twice\n        else: self._deleted_ = True     # avoid exit twice\n\n        # print红('[mcom.py]: mcom exiting! tag: %s'%self.tag)\n        if hasattr(self, 'current_file_handle') and self.current_file_handle is not None:\n            end_file_flag = ('><EndTaskFlag\\n')\n            self.current_file_handle.write(end_file_flag)\n            self.current_file_handle.close()\n        if hasattr(self, 'port') and self.port is not None:\n            self.disconnect()\n        if hasattr(self, 'draw_proc') and self.draw_proc is not None:\n            try:\n                self.draw_proc.terminate()\n                self.draw_proc.join()\n            except:\n                pass\n        # print蓝('[mcom.py]: mcom exited! tag: %s'%self.tag)\n\n\n    def disconnect(self):\n        # self.draw_udp_client.close()\n        self.draw_tcp_client.close()\n\n\n    def recall(self, starting_file):\n        with open(starting_file,'rb') as f:\n            lines = f.readlines()\n        r = None\n        for l in lines:\n            if 'rec_show' in str(l, encoding='utf8'): \n                r = copy.deepcopy(l)\n                continue\n            self.draw_tcp_client.send_str(l)\n        if r is not None:\n            self.draw_tcp_client.send_str(r)\n        return None\n\n    '''\n        mcom core function: send out/write str\n    '''\n    def send(self, data):\n        # # step 1: send directive to draw process\n        if self.draw_process: \n            # self.draw_udp_client.sendto(data, self.draw_udp_port)\n            self.draw_tcp_client.send_str(data)\n\n        # ! vhmap has its own backup method\n        if self.draw_mode=='Threejs': return\n\n        # step 2: add to file\n        self.file_lines_cnt += 1\n        self.current_file_handle.write(data)\n        if self.rapid_flush: \n            self.current_file_handle.flush()\n        elif self.flow_cnt>500:\n            self.current_file_handle.flush()\n            self.flow_cnt = 0\n\n        # step 3: check whether the file is too large, if so, move on to next file.\n        if self.file_lines_cnt > self.file_max_lines:\n            end_file_flag = ('><EndFileFlag\\n')\n            self.current_file_handle.write(end_file_flag)\n            self.current_file_handle.close()\n            self.current_buffer_index += 1\n            self.current_file_handle = open((self.path + '/mcom_buffer_%d.txt' % self.current_buffer_index), 'wb+')\n            self.file_lines_cnt = 0\n        return\n\n\n    def rec_init(self, color='k'):\n        str_tmp = '>>rec_init(\\'%s\\')\\n' % color\n        self.send(str_tmp)\n\n    def rec_show(self):\n        self.send('>>rec_show\\n')\n\n    def rec_end(self):\n        self.send('>>rec_end\\n')\n\n    def rec_save(self):\n        self.send('>>rec_save\\n')\n\n    def rec_end_hold(self):\n        self.send('>>rec_end_hold\\n')\n\n    def rec_clear(self, name):\n        str_tmp = '>>rec_clear(\"%s\")\\n' % (name)\n        self.send(str_tmp)\n\n    def rec(self, value, name):\n        value = float(value)\n        if self.digit == 16:\n            str_tmp = '>>rec(%.16e,\"%s\")\\n' % (value, name)\n        elif self.digit == 8:\n            str_tmp = '>>rec(%.8e,\"%s\")\\n' % (value, name)\n        elif self.digit == 4:\n            str_tmp = '>>rec(%.4e,\"%s\")\\n' % (value, name)\n        self.send(str_tmp)\n\n    def 发送虚幻4数据流(self, x, y, z, pitch, yaw, roll):\n        x = float(x)\n        y = float(y)\n        z = float(z)\n        pitch = float(pitch)\n        yaw = float(yaw)\n        roll = float(roll)\n        str_tmp = 'UE4>>(\\\"agent#1\\\",%.6e,%.6e,%.6e,%.6e,%.6e,%.6e)\\n' % (x, y, z, pitch, yaw, roll)\n        self.send(str_tmp)\n\n    def 发送虚幻4数据流_多智能体(self, x_, y_, z_, pitch_, yaw_, roll_):\n        str_list = ['UE4>>']\n        for x, y, z, pitch, yaw, roll in zip(x_, y_, z_, pitch_, yaw_, roll_):\n            x = float(x)\n            y = float(y)\n            z = float(z)\n            pitch = float(pitch)\n            yaw = float(yaw)\n            roll = float(roll)\n            str_tmp = '(\\\"agent#1\\\",%.5e,%.5e,%.5e,%.5e,%.5e,%.5e)' % (x, y, z, pitch, yaw, roll)\n            str_list.append(str_tmp)\n            str_list.append(';')\n        str_list.append('\\n')\n\n        cmd = ''.join(str_list)\n        self.send(cmd)\n\n    def other_cmd(self, func_name, *args, **kargs):\n        # func_name = traceback.extract_stack()[-2][2]\n        strlist = ['>>', func_name, '(']\n\n        for _i_ in range(len(args)):\n            if isinstance(args[_i_], np.ndarray):\n                strlist = self._process_ndarray(args[_i_], strlist)\n            else:\n                strlist = self._process_scalar(args[_i_], strlist)\n        if len(kargs)>0:\n            for _key_ in kargs:\n                if isinstance(kargs[_key_], np.ndarray):\n                    strlist = self._process_ndarray(kargs[_key_], strlist, _key_)\n                else:\n                    strlist = self._process_scalar(kargs[_key_], strlist, _key_)\n\n        if strlist[len(strlist) - 1] == \"(\": strlist.append(\")\\n\")\n        else: strlist[len(strlist) - 1] = \")\\n\" # 把逗号换成后括号\n        self.send(''.join(strlist))\n\n    def _process_scalar(self, arg, strlist,key=None):\n        if key is not None: strlist += '%s='%key\n        if isinstance(arg, int):\n            strlist.append(\"%d\" % arg)\n            strlist.append(\",\")\n        elif isinstance(arg, float):\n            if self.digit == 16:  strlist.append(\"%.16e\" % arg)\n            elif self.digit == 8: strlist.append(\"%.8e\" % arg)\n            elif self.digit == 4: strlist.append(\"%.4e\" % arg)\n            strlist.append(\",\")\n        elif isinstance(arg, str):\n            assert '$' not in arg\n            strlist.extend([\"\\'\", arg.replace('\\n', '$'), \"\\'\", \",\"])\n        elif isinstance(arg, list):\n            strlist.append(str(arg))\n            strlist.append(\",\")\n        elif hasattr(arg, 'dtype') and np.issubdtype(arg.dtype, np.integer):\n            strlist.append(\"%d\" % arg)\n            strlist.append(\",\")\n        elif hasattr(arg, 'dtype') and np.issubdtype(arg.dtype, np.floating):\n            if self.digit == 16:  strlist.append(\"%.16e\" % arg)\n            elif self.digit == 8: strlist.append(\"%.8e\" % arg)\n            elif self.digit == 4: strlist.append(\"%.4e\" % arg)\n            strlist.append(\",\")\n        else:\n            print('输入的参数类型不能处理',arg.__class__)\n        return strlist\n\n    def _process_ndarray(self, args, strlist, key=None):\n        if args.ndim == 1:\n            if key is not None: strlist += '%s='%key\n            d = len(args)\n            sub_list = [\"[\"] + [\"%.3e,\"%t if (i+1)!=d else \"%.3e\"%t for i, t in enumerate(args)] + [\"]\"]\n            strlist += sub_list\n            strlist.append(\",\")\n        elif args.ndim == 2:\n            print红('[mcom]: 输入数组的维度大于1维, 目前处理不了。')\n        else:\n            print红('[mcom]: 输入数组的维度大于2维, 目前处理不了。')\n        return strlist\n\n    for fn_name in mcom_fn_list_define:\n        build_exec_cmd = 'def %s(self,*args,**kargs):\\n self.other_cmd(\"%s\", *args,**kargs)\\n'%(fn_name, fn_name)\n        exec(build_exec_cmd)\n\n    for 别名, fn_name in 别名对齐:\n        build_exec_cmd = '%s = %s\\n'%(别名, fn_name)\n        exec(build_exec_cmd)\n\n\n\n\n\n\n\ndef find_where_to_log(path):\n    if not os.path.exists(path): os.makedirs(path)\n\n    def find_previous_start_end():\n        start = None; end = None; t = 0\n        while True:\n            is_body = os.path.exists(path + '/mcom_buffer_%d.txt' % t)\n            is_head = os.path.exists(path + '/mcom_buffer_%d____starting_session.txt' % t)\n            if is_head: start = t\n            if is_head or is_body: end = t; t += 1\n            else:\n                new = t\n                return (start, end, new)\n\n    prev_start, prev_end, new = find_previous_start_end()\n    return prev_start, prev_end, new\n\n\n\n\n\n\nclass DrawProcessThreejs(Process):\n    def __init__(self, draw_udp_port, draw_mode, **kargs):\n        super(DrawProcessThreejs, self).__init__()\n        from UTIL.network import QueueOnTcpServer\n        self.draw_mode = draw_mode\n        self.draw_udp_port = draw_udp_port\n        self.tcp_connection = QueueOnTcpServer(self.draw_udp_port)\n        self.buffer_list = []\n        self.backup_file = kargs['backup_file']\n        self.allow_backup = False if self.backup_file is None else True\n        if self.allow_backup:\n            if os.path.exists(self.backup_file):\n                print亮红('[mcom.py]: warning, purge previous 3D visual data!')\n                try: os.remove(self.backup_file)\n                except: pass\n            self.tflush_buffer = []\n        self.client_tokens = {}\n\n    def flush_backup(self):\n        while True:\n            time.sleep(20)\n            if not os.path.exists(os.path.dirname(self.backup_file)):\n                os.makedirs(os.path.dirname(self.backup_file))\n            # print('Flush backup')\n            with gzip.open(self.backup_file, 'at') as f:\n                f.writelines(self.tflush_buffer)\n            self.tflush_buffer = []\n            # print('Flush backup done')\n\n    def init_threejs(self):\n        http_port = find_free_port()\n        ws_port = 8765 # http_port+1\n        t = threading.Thread(target=self.run_flask, args=(http_port,))\n        t.daemon = True\n        t.start()\n        t2 = threading.Thread(target=self.run_ws, args=(ws_port,))\n        t2.daemon = True\n        t2.start()\n        time.sleep(2)\n\n        if self.allow_backup:\n            self.tflush = threading.Thread(target=self.flush_backup)\n            self.tflush.daemon = True\n            self.tflush.start()\n\n    def run_ws(self, port):\n        import asyncio\n        import websockets\n\n        self.connected_ws = None\n        self.new_ws_connection_flag = False\n        \n        async def echo(websocket):\n            self.connected_ws = websocket\n            self.new_ws_connection_flag = True\n            while True:\n                try:\n                    # not supposed to receive anything, just to maintain connection\n                    await self.connected_ws.recv()   \n                except websockets.ConnectionClosed:\n                    print(f\"Previous Websocket Terminated\")\n                    self.connected_ws = None\n                    break\n\n        async def run_ws():\n            async with websockets.serve(echo, \"localhost\", port):\n                await asyncio.Future()  # run forever\n\n        self.init_cmd_captured = False\n        init_cmd_list = []\n        def init_cmd_capture_fn(tosend):\n            for strx in tosend:\n                if '>>v2d_show()\\n'==strx:\n                    self.init_cmd_captured = True\n                init_cmd_list.append(strx)    \n                if self.init_cmd_captured:\n                    break\n            return\n\n        async def run_ws_main():\n            while True:\n                await asyncio.sleep(0.01)\n                if self.connected_ws is not None:\n                    # 本次正常情况下，需要发送的数据\n                    # dont send too much in one POST, might overload the network traffic\n\n                    if len(self.buffer_list)>35000:\n                        tosend = self.buffer_list[:30000]\n                        self.buffer_list = self.buffer_list[30000:]\n                    else:\n                        tosend = self.buffer_list\n                        self.buffer_list = []\n\n                    # 处理断线重连的情况，断线重连时，会出现新的token\n                    if self.new_ws_connection_flag:\n                        self.new_ws_connection_flag = False\n                        if (not self.init_cmd_captured):  \n                            # 尚未捕获初始化命令，或者第一次client\n                            buf = \"\".join(tosend)\n                        else:\n                            print('[mcom.py] If there are other tabs, please close them now.')\n                            tosend = [\"\"]\n                            buf = \"\".join(init_cmd_list + tosend)\n                    else:\n                        # 正常连接\n                        buf = \"\".join(tosend)\n\n                    # 尝试捕获并保存初始化部分的命令\n                    if not self.init_cmd_captured:\n                        init_cmd_capture_fn(tosend)\n                    # use zlib to compress output command, worked out like magic\n                    buf = bytes(buf, encoding='utf8')   \n                    zlib_compress = zlib.compressobj()\n                    buf = zlib_compress.compress(buf) + zlib_compress.flush(zlib.Z_FINISH)\n                    print('await start')\n                    if not self.connected_ws.open: continue\n                    await self.connected_ws.send(buf)\n                    print('await done')\n\n        async def main():\n            task1 = asyncio.create_task(run_ws())\n            task2 = asyncio.create_task(run_ws_main())\n            await task1\n            await task2\n\n        asyncio.run(main())\n\n\n    def run_flask(self, port):\n        import json\n        from flask import Flask, request, send_from_directory\n        from waitress import serve\n        from mimetypes import add_type\n        add_type('application/javascript', '.js')\n        add_type('text/css', '.css')\n\n        app = Flask(__name__)\n        dirname = os.path.dirname(__file__) + '/threejsmod'\n        import zlib\n\n        self.init_cmd_captured = False\n        init_cmd_list = []\n\n        @app.route(\"/<path:path>\")\n        def static_dirx(path):\n            if path=='favicon.ico': \n                return send_from_directory(\"%s/\"%dirname, 'files/HMP.ico')\n            return send_from_directory(\"%s/\"%dirname, path)\n\n        @app.route(\"/\")\n        def main_app():\n            with open('%s/examples/abc_rt.html'%dirname, 'r', encoding = \"utf-8\") as f:\n                buf = f.read()\n            return buf\n\n        print('\\n--------------------------------')\n        print('JS visualizer online: http://%s:%d'%(get_host_ip(), port))\n        print('JS visualizer online (localhost): http://localhost:%d'%(port))\n        print('--------------------------------')\n        # app.run(host='0.0.0.0', port=port)\n        serve(app, threads=8, ipv4=True, ipv6=True, listen='*:%d'%port)\n\n    def run(self):\n        self.init_threejs()\n        try:\n            from queue import Empty\n            queue = self.tcp_connection.get_queue()\n            self.tcp_connection.wait_connection() # after this, the queue begin to work\n            while True:\n                buff_list = []\n                buff_list.extend(queue.get(timeout=600))\n                for _ in range(queue.qsize()): buff_list.extend(queue.get(timeout=600))\n                self.run_handler(buff_list)\n\n        except KeyboardInterrupt:\n            self.__del__()\n        self.__del__()\n\n    def __del__(self):\n        return\n        \n    def run_handler(self, new_buff_list):\n        self.buffer_list.extend(new_buff_list)\n        self.tflush_buffer.extend(new_buff_list)\n\n        # too many, delete with fifo\n        if len(self.buffer_list) > 1e9: \n            # 当存储的指令超过十亿后，开始删除旧的\n            del self.buffer_list[:len(new_buff_list)]\n\n\n\nclass DrawProcess(Process):\n    def __init__(self, draw_udp_port, draw_mode, **kargs):\n        from UTIL.network import QueueOnTcpServer\n        super(DrawProcess, self).__init__()\n        self.draw_mode = draw_mode\n        self.draw_udp_port = draw_udp_port\n        self.tcp_connection = QueueOnTcpServer(self.draw_udp_port)\n        self.image_path = kargs['image_path'] if 'image_path' in kargs else None\n        return\n\n    def init_matplot_lib(self):\n        if self.draw_mode in ['Web', 'Img']:\n            import matplotlib\n            matplotlib.use('Agg') # set the backend before importing pyplot\n            import matplotlib.pyplot as plt\n            self.gui_reflesh = lambda: time.sleep(1) # plt.pause(0.1)\n        elif self.draw_mode == 'Native':\n            import matplotlib\n            # matplotlib.use('Agg') # set the backend before importing pyplot\n            matplotlib.use('Qt5Agg')\n            import matplotlib.pyplot as plt\n            self.gui_reflesh = lambda: plt.pause(0.2)\n        elif self.draw_mode == 'Threejs':\n            assert False\n        else:\n            assert False\n\n        from config import GlobalConfig\n        logdir = GlobalConfig.logdir\n        if not os.path.exists(logdir):\n            os.makedirs(logdir)\n        if self.draw_mode == 'Web':\n            self.avail_port = find_free_port()\n            my_http = MyHttp('%s/html.html'%logdir, self.avail_port)\n            my_http.daemon = True\n            my_http.start()\n        self.libs_family = {\n            'rec_init': 'rec', 'rec': 'rec', 'rec_show': 'rec',\n            'v2d_init': 'v2d', 'v2dx':'v2d', 'v2d_show': 'v2d', 'v2d_pop':'v2d',\n            'v2d_line_object':'v2d', 'v2d_clear':'v2d', 'v2d_add_terrain': 'v2d',\n        }\n        self.libs_init_fns = {\n            'rec': self.rec_init_fn,\n            'v2d': self.v2d_init_fn,\n        }\n\n    def run(self):\n        self.init_matplot_lib()\n        try:\n            # self.tcp_connection.set_handler(self.run_handler)\n            from queue import Empty\n            queue = self.tcp_connection.get_queue()\n            # self.tcp_connection.set_handler(self.run_handler)\n            self.tcp_connection.wait_connection() # after this, the queue begin to work\n            while True:\n                try: \n                    buff_list = []\n                    buff_list.extend(queue.get(timeout=0.1))\n                    for _ in range(queue.qsize()): buff_list.extend(queue.get(timeout=0.1))\n                    self.run_handler(buff_list)\n                except Empty: self.gui_reflesh()\n\n        except KeyboardInterrupt:\n            self.__del__()\n        self.__del__()\n\n    def run_handler(self, buff_list):\n        while True:\n            if len(buff_list) == 0: break\n            buff = buff_list.pop(0)\n            if (buff=='>>rec_show\\n') and ('>>rec_show\\n' in buff_list): continue # skip\n            self.process_cmd(buff)\n\n        #     # print('成功处理指令:', buff)\n\n    def __del__(self):\n        self.tcp_connection.close()\n\n\n\n    def process_cmd(self, cmd_str):\n        if '>>' in cmd_str:\n            cmd_str_ = cmd_str[2:].strip('\\n')\n            if ')' not in cmd_str_:\n                cmd_str_ = cmd_str_+'()'\n            prefix = self.get_cmd_lib(cmd_str_)\n            if prefix is not None: \n                eval('%s.%s'%(prefix, cmd_str_))\n\n    def get_cmd_lib(self, cmd):\n        cmd_key = None\n        func_name = cmd.split('(')[0]\n        if func_name not in self.libs_family:\n            print蓝('绘图函数不能处理：', cmd)\n            return None\n        family_name = self.libs_family[func_name]\n        if self.libs_init_fns[family_name] is not None:\n            self.libs_init_fns[family_name]()\n            self.libs_init_fns[family_name] = None\n        return 'self.%s'%family_name\n\n    def rec_init_fn(self):\n        from VISUALIZE.mcom_rec import rec_family\n        self.rec = rec_family('r', self.draw_mode, self.image_path)\n\n    def v2d_init_fn(self):\n        from VISUALIZE.mcom_v2d import v2d_family\n        self.v2d = v2d_family(self.draw_mode)\n\n\n\n\nclass MyHttp(Process):\n    def __init__(self, path_to_html, avail_port):\n        super(MyHttp, self).__init__()\n        self.path_to_html = path_to_html\n        self.avail_port = avail_port\n\n    def run(self):\n        from flask import Flask\n        app = Flask(__name__)\n        @app.route(\"/\")\n        def hello():\n            try:\n                with open(self.path_to_html,'r') as f:\n                    html = f.read()\n            except:\n                html = \"no plot yet please wait\"\n            return html\n        app.run(port=self.avail_port)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_test.py",
    "content": "def validate_path():\n    import os, sys\n    dir_name = os.path.dirname(__file__)\n    root_dir_assume = os.path.abspath(os.path.dirname(__file__) +  '/..')\n    os.chdir(root_dir_assume)\n    sys.path.append(root_dir_assume)\n    \nvalidate_path() # validate path so you can run from base directory\n\nfrom VISUALIZE.mcom import mcom\n\nmcv = mcom(\n    path='./TEMP',  # path to generate log\n    draw_mode='Img',    # draw mode\n    resume_mod=True,    # resume from previous session\n    # figsize=(48,12),  # manual fig size\n    resume_file='ZHECKPOINT/RVE-drone2-ppoma-run1/logger/mcom_buffer_0____starting_session.txt',   # pick up from a specific session txt\n    image_path='./temp2.jpg',   # target image directory\n    smooth_level=40,  # smooth line level\n    # rec_exclude=[\"r*\", \"n*\", \n    #     \"*0.00*\", \n    #     \"*0.01*\", \n    #     \"*0.04*\", \n    #     \"*0.06*\", \n    #     \"*0.11*\", \n    #     \"*0.18*\", \n    #     \"*0.25*\", \n    # ],\n)\n\ninput('wait complete')\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/mcom_v2d.py",
    "content": "import numpy as np\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport time\nplt.ion()\nV2dPlotFig = 3\n# import matplotlib.rcsetup as rcsetup; print(rcsetup.all_backends)\n'''\n['GTK3Agg', 'GTK3Cairo', 'MacOSX', 'nbAgg', 'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', \n'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo', 'agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg', 'template']\n'''\nclass v2d_family():\n    def __init__(self, draw_mode) -> None:\n        self.v_name_list = {}\n        self.style_list = {}\n        self.trival_line_list = {}\n        self.trival_line_pair = []\n        self.v2d_fig_handle = None\n        # self.v_name_list = {  'char_index':{ 'color': ?,'pos':(x,y),'shape':(xxxx,yyyy) }   }\n        # self.style_list = {'red': {'style_name_list':[], 'plot_handle':handle} }\n        self.draw_mode = draw_mode\n        assert draw_mode=='Native', ('only support native')\n        pass\n    \n    def v2d_init(self):\n        pass\n    \n    def v2dx(self, name, xpos, ypos, dir=0, **kargs):\n        str = ' '\n        split_res = name.split('|')\n        char_shape = 'cir'\n        char_color = 'k'\n        radius = 4\n        if len(split_res) >= 1: char_shape = split_res[0]\n        if len(split_res) >= 2: char_index = split_res[1]\n        if len(split_res) >= 3: char_color = split_res[2]\n        if len(split_res) >= 4: radius = float(split_res[3])\n\n        if char_index in self.v_name_list.keys():\n            # 曲线已经被注册\n            previous_color = self.v_name_list[char_index]['color']\n            # 样式发生变化\n            if previous_color != char_color:\n                # 取消旧的样式\n                assert char_index in self.style_list[previous_color]['style_name_list']\n                self.style_list[previous_color]['style_name_list'].remove(char_index)\n                # 注册新的样式\n                self.add_to_style(char_index, char_color)\n                self.v_name_list[char_index]['color'] = char_color\n        else:\n            # 第一次出现\n            self.v_name_list[char_index] = {'color':char_color}\n            if self.v2d_fig_handle is None:\n                self.init_fig()\n            self.add_to_style(char_index, char_color)\n        # \n        if char_shape == 'cir':\n            xc, yc = self.circle_data(0,0,radius,0,360)\n        elif char_shape == 'rec':\n            xc, yc = self.rec_data(0,0,radius,0)\n        elif char_shape == 'tank':\n            xc, yc = self.tank_data(0,0,radius,dir, **kargs)\n\n        self.v_name_list[char_index]['pos'] = (xpos, ypos)\n        self.v_name_list[char_index]['shape'] = (xc, yc)\n        \n        \n    def init_fig(self):\n        self.v2d_fig_handle = plt.figure(V2dPlotFig, figsize=(8, 8), dpi=100)\n        # self.v2d_fig_handle.canvas.set_window_title('V2dPlotFig')\n        self.v2d_fig_handle.show()\n        plt.show(block=False)\n\n    def add_to_style(self, char_index, char_color):\n        if char_color not in self.style_list.keys():\n            self.style_list[char_color] = {'plot_handle':None,  'style_name_list':[]}\n            self.style_list[char_color]['plot_handle'] = self.v2d_fig_handle.gca().plot(0,0, lw=1, c=char_color)[0]\n        # 样式已经被注册\n        self.style_list[char_color]['style_name_list'].append(char_index)\n\n    def rec_data(self, x,y,r,dir):\n        tmp = np.array([[r, 0, -r, 0, r], [0, r, 0, -r, 0]])\n        dir = -dir\n        tmp = np.array([[np.cos(dir), np.sin(dir)],[-np.sin(dir),np.cos(dir)]]).dot(tmp)\n        xp = tmp[0,:]\n        yp = tmp[1,:]\n        return xp,yp\n\n    def tank_data(self, x,y,r,dir,**kargs):\n        x_ = np.array([-0.74, -0.74, -0.55, -0.55, -0.05, -0.05, -0.55, -0.55, -0.55,\n       -0.05, -0.05,  0.07,  0.07,  0.07,  0.07,  0.57,  0.57,  0.07,\n        0.57,  0.57,  0.75,  0.75,   np.nan, -0.74, -0.74, -0.55, -0.55,\n       -0.05, -0.05, -0.55, -0.55, -0.55, -0.05, -0.05,  0.07,  0.07,\n        0.07,  0.07,  0.57,  0.57,  0.07,  0.57,  0.57,  0.75,  0.75,\n        np.nan])*r\n        y_ = np.array([ 0.45, -0.53, -0.53, -0.62, -0.62, -0.42, -0.42, -0.53, -0.42,\n            -0.42, -0.53, -0.53, -0.42, -0.53, -0.62, -0.62, -0.42, -0.42,\n            -0.42, -0.53, -0.53,  0.45,   np.nan, -0.45,  0.53,  0.53,  0.62,\n                0.62,  0.42,  0.42,  0.53,  0.42,  0.42,  0.53,  0.53,  0.42,\n                0.53,  0.62,  0.62,  0.42,  0.42,  0.42,  0.53,  0.53, -0.45,\n                np.nan])*r\n        tx_ = np.array([-0.15, -0.15,  0.45,  0.45, -0.15,  0.45,  0.45,  1.45,  1.45,\n                0.45,   np.nan])*r\n        ty_ = np.array([-0.14,  0.14,  0.14, -0.14, -0.14, -0.14, -0.04, -0.04,  0.04,\n                0.04,   np.nan])*r\n        if 'vel_dir' in kargs:\n            theta_gun = dir\n            theta_ve = kargs['vel_dir']\n        else:\n            theta_gun = dir\n            theta_ve = dir\n\n\n        tx = tx_*np.cos(theta_gun) - ty_*np.sin(theta_gun)\n        ty = tx_*np.sin(theta_gun) + ty_*np.cos(theta_gun)\n        x = x_*np.cos(theta_ve) - y_*np.sin(theta_ve)\n        y = x_*np.sin(theta_ve) + y_*np.cos(theta_ve)\n        xp = np.concatenate((x,tx))\n        yp = np.concatenate((y,ty))\n\n        # fan\n        if len(kargs)>0:\n            fan_r = 1 if kargs is None else kargs['attack_range']\n            xfan, yfan = self.fan_data(0,0,fan_r, dir, np.pi/4)\n            xp = np.concatenate((xp,xfan))\n            yp = np.concatenate((yp,yfan))\n\n        return xp,yp\n\n    def circle_data(self, x,y,r,dir,rad,step=15):\n        dir = -dir\n        rads = dir - rad/2\n        rade = dir + rad/2\n        ang = np.arange(start=rads, stop=rade+1e-5, step=step) # rads:45:rade \n        xp=r*np.cos(ang*np.pi/180)+x\n        yp=r*np.sin(ang*np.pi/180)+y\n        return xp, yp\n    \n    @staticmethod\n    def dotify_vec(p_arr):\n        lxp = len(p_arr)\n        new_arr_len = lxp + lxp//2 if lxp%2 !=0 else lxp + lxp//2 - 1\n        dot_arr = np.arange(new_arr_len)%3\n        dot_index=dot_arr + 2*(np.arange(new_arr_len)//3)\n        # dot_index[==2] = 0\n        p_arr = p_arr[dot_index]; p_arr[dot_arr==2] = np.nan\n        return p_arr\n\n    @staticmethod\n    def line(p1,p2, sep):\n        from UTIL.tensor_ops import repeat_at\n        lam = np.arange(start=0.7, stop=1. + 1e-5, step=0.05)\n        p1s = repeat_at(p1, insert_dim=0, n_times=len(lam))\n        p2s = repeat_at(p2, insert_dim=0, n_times=len(lam))\n        lam = repeat_at(lam, insert_dim=-1, n_times=2)\n        p_arr_line = p1s*lam + p2s*(1-lam)\n        p_arr_line = np.concatenate((p_arr_line, sep),0)\n        return p_arr_line\n\n\n    def fan_data(self, x,y,r,dir,rad):  #to do: dotted line\n        rads = dir - rad/2\n        rade = dir + rad/2\n        ang = np.arange(start=rads, stop=rade+1e-5, step=np.pi/45) # rads:45:rade \n        xp=r*np.cos(ang)+x\n        yp=r*np.sin(ang)+y\n        sep = np.array([[np.nan,np.nan]])\n\n        p_arr = np.stack((xp,yp)).transpose()\n        p_arr = self.dotify_vec(p_arr)\n        \n        orin = np.array([x,y])\n\n        L1_arr = self.dotify_vec(self.line(p_arr[0], orin, sep))\n        L2_arr = self.dotify_vec(self.line(p_arr[-1], orin, sep))\n        p_arr = np.concatenate((p_arr, sep),0)\n\n        arr = np.concatenate((L1_arr, p_arr, L2_arr),0)\n        return arr[:,0], arr[:,1]\n\n    @staticmethod\n    def get_terrain(arr, theta):\n        A = 0.05; B=0.2; X=arr[:,0]; Y=arr[:,1]\n        X_ = X*np.cos(theta) + Y*np.sin(theta)\n        Y_ = -X*np.sin(theta) + Y*np.cos(theta)\n        Z = -1 +B*( (0.1*X_) ** 2 + (0.1*Y_) ** 2 )- A * np.cos(2 * np.pi * (0.3*X_))  - A * np.cos(2 * np.pi * (0.5*Y_))\n        return -Z\n\n    def v2d_add_terrain(self, theta):\n        self.theta = theta\n        return\n\n    def v2d_draw(self):\n        # self.v_name_list = {  'char_index':{ 'color': ?,'pos':(x,y),'shape':(xxxx,yyyy) }   }\n        # self.style_list = {'red': {'style_name_list':[], 'plot_handle':handle} }\n\n        # from UTIL.tensor_ops import my_view\n        # X = np.arange(-6, 6, 0.1)\n        # Y = np.arange(-6, 6, 0.1)\n        # X, Y = np.meshgrid(X, Y)    # 100\n        # X = my_view(X, [-1,1])\n        # Y = my_view(Y, [-1,1])\n        # arr = np.concatenate((X,Y), -1)\n        # Z = self.get_terrain(arr, self.theta)\n        # d = int(np.sqrt(X.shape[0]))\n        # X = X.reshape(d,d)\n        # Y = Y.reshape(d,d)\n        # Z = Z.reshape(d,d)\n        # plt.contourf(X, Y, Z)\n        from UTIL.tensor_ops import my_view\n        X = np.arange(-6, 6, 0.1)\n        Y = np.arange(-6, 6, 0.1)\n        X, Y = np.meshgrid(X, Y)    # 100\n        X = my_view(X, [-1,1])\n        Y = my_view(Y, [-1,1])\n        arr = np.concatenate((X,Y), -1)\n        Z = self.get_terrain(arr, self.theta)\n        d = int(np.sqrt(X.shape[0]))\n        X = X.reshape(d,d)\n        Y = Y.reshape(d,d)\n        Z = Z.reshape(d,d)\n        from matplotlib.colors import LinearSegmentedColormap\n        cmap_name = 'my_list'\n        colors = [(0.4,0.4,0.4),(0.7,0.7,0.7)]\n        cmap = LinearSegmentedColormap.from_list(cmap_name, colors, N=10)\n        plt.contourf(X, Y, Z, levels= 10,cmap=cmap)\n\n        for style in self.style_list.keys(): \n            style_name_list = self.style_list[style]['style_name_list']\n            line_handle = self.style_list[style]['plot_handle']\n\n            x_data_concat = (np.nan,)\n            y_data_concat = (np.nan,)\n\n            for char_name in style_name_list:\n                xpos, ypos = self.v_name_list[char_name]['pos']\n                xc, yc = self.v_name_list[char_name]['shape']\n                xc_ = xc + xpos\n                yc_ = yc + ypos\n                x_data_concat = np.concatenate((x_data_concat, (np.nan,), xc_))\n                y_data_concat = np.concatenate((y_data_concat, (np.nan,), yc_)) # [y_data_concat, np.nan, yc_]\n\n            line_handle.set_data((x_data_concat,y_data_concat))\n            axes_handle = self.v2d_fig_handle.gca()\n            axes_handle.relim()\n            axes_handle.axis('equal')\n            axes_handle.autoscale_view(True,True,True)\n            # self.v2d_fig_handle.gca().set_xlim(-2,2)\n            # self.v2d_fig_handle.gca().set_ylim(-2,2)\n        for AB in self.trival_line_pair:\n            indexA, indexB = AB\n            self.v2d_line_object(indexA, indexB)\n            \n    def v2d_line_object(self, indexA, indexB):\n        indexA = str(int(indexA))\n        indexB = str(int(indexB))\n        line_name = 'line:%s->%s'%(indexA,indexB)\n        x1,y1=self.v_name_list[indexA]['pos']\n        x2,y2=self.v_name_list[indexB]['pos']\n        if line_name not in self.trival_line_list:\n            self.trival_line_pair.append([indexA,indexB])\n            self.trival_line_list[line_name] = self.v2d_fig_handle.gca().plot([x1,x2],[y1,y2], lw=1, c='k')[0]\n        else:\n            self.trival_line_list[line_name].set_data(([x1,x2],[y1,y2]))\n\n    def v2d_clear(self):\n        self.v_name_list = {}\n        self.style_list = {}\n        self.trival_line_list = {}\n        self.trival_line_pair = []\n        if self.v2d_fig_handle is not None:\n            self.v2d_fig_handle.clf()\n\n\n    def v2d_show(self):\n        self.v2d_draw()\n        plt.draw()\n        plt.pause(0.02)\n        # print('v2d_show')\n\n\nif __name__ == '__main__':\n    v2d = v2d_family('Native')\n\n    # v2d.v2dx('cir|0|r|0.42', xpos=1, ypos=0)\n    # v2d.v2dx('cir|1|r|0.45', xpos=1, ypos=1)\n    # v2d.v2dx('rec|2|b|0.1',0,0)\n    # v2d.v2dx('rec|0|b|0.1',1,0)\n    # plt.pause(15)\n    v2d.v2d_init()\n    v2d.v2dx('cir|0|b|0.04',1.6974286259771310e+00,5.1136334271362971e+00)\n    v2d.v2dx('cir|1|b|0.04',1.7323874630544438e+00,4.8441353012872579e+00)\n    v2d.v2dx('cir|2|b|0.04',1.7466729589216059e+00,4.5011681684285119e+00)\n    v2d.v2dx('cir|3|b|0.04',1.9834755845632670e+00,4.5303563729102958e+00)\n    v2d.v2dx('cir|4|b|0.04',1.7282481584048974e+00,4.2215967719027470e+00)\n    v2d.v2dx('cir|5|b|0.04',1.6961323762900244e+00,4.2162281616241906e+00)\n    v2d.v2dx('cir|6|b|0.04',1.9538386636567124e+00,3.8049981311503873e+00)\n    v2d.v2dx('cir|7|b|0.04',1.8802200761399963e+00,3.6045336461487691e+00)\n    v2d.v2dx('cir|8|b|0.04',2.0157571619860644e+00,3.4048652151235701e+00)\n    v2d.v2dx('cir|9|b|0.04',1.7335519021352239e+00,3.2864204601228306e+00)\n    v2d.v2dx('cir|10|b|0.04',1.7570958563011358e+00,2.9889104070926833e+00)\n    v2d.v2dx('cir|11|b|0.04',1.8973304349256952e+00,2.8428589222323897e+00)\n    v2d.v2dx('cir|12|b|0.04',1.9387993015459224e+00,2.8276797742219721e+00)\n    v2d.v2dx('cir|13|b|0.04',1.9015358683680221e+00,2.3111528346056227e+00)\n    v2d.v2dx('cir|14|b|0.04',1.8004736376121440e+00,2.2936583436192697e+00)\n    v2d.v2dx('cir|15|b|0.04',2.0107659893617642e+00,2.1508119100878571e+00)\n    v2d.v2dx('cir|16|b|0.04',1.8444440354811558e+00,1.9332926354355557e+00)\n    v2d.v2dx('cir|17|b|0.04',1.8371937746969322e+00,1.7330254976821911e+00)\n    v2d.v2dx('cir|18|b|0.04',1.9129340340533809e+00,1.4154660738691236e+00)\n    v2d.v2dx('cir|19|b|0.04',2.0030211676034568e+00,1.4298957885890118e+00)\n    v2d.v2dx('cir|20|b|0.04',1.8856683487925392e+00,1.0541625663320517e+00)\n    v2d.v2dx('cir|21|b|0.04',2.1527191655945512e+00,1.0831699476943015e+00)\n    v2d.v2dx('cir|22|b|0.04',1.8649326792157024e+00,6.2217390250533311e-01)\n    v2d.v2dx('cir|23|b|0.04',2.0898862160995813e+00,6.2584505914386446e-01)\n    v2d.v2dx('cir|24|b|0.04',1.8549554422225740e+00,2.9299012314167772e-01)\n    v2d.v2dx('cir|25|b|0.04',1.9339051071960001e+00,1.7110109106481508e-01)\n    v2d.v2dx('cir|26|b|0.04',2.1857357920928222e+00,-2.2904476331429088e-01)\n    v2d.v2dx('cir|27|b|0.04',2.2898372430290381e+00,-2.7931351208642319e-01)\n    v2d.v2dx('cir|28|b|0.04',2.1052665060702345e+00,-5.2811184015975321e-01)\n    v2d.v2dx('cir|29|b|0.04',2.0113388563475842e+00,-5.6067217211506271e-01)\n    v2d.v2dx('cir|30|b|0.04',1.9572075843799210e+00,-1.0074934509032205e+00)\n    v2d.v2dx('cir|31|b|0.04',2.1019847044222217e+00,-1.2161640572227850e+00)\n    v2d.v2dx('cir|32|b|0.04',2.0810334514385351e+00,-1.3997226595437748e+00)\n    v2d.v2dx('cir|33|b|0.04',2.1243209345552660e+00,-1.4827073696986688e+00)\n    v2d.v2dx('cir|34|b|0.04',2.2812126964925952e+00,-1.6582712682744103e+00)\n    v2d.v2dx('cir|35|b|0.04',2.2908731504091189e+00,-2.0073663392281826e+00)\n    v2d.v2dx('cir|36|b|0.04',2.1953372997254261e+00,-2.1534518843321200e+00)\n    v2d.v2dx('cir|37|b|0.04',2.2671821392880260e+00,-2.2919289063843635e+00)\n    v2d.v2dx('cir|38|b|0.04',2.2425046928689309e+00,-2.5855765908809776e+00)\n    v2d.v2dx('cir|39|b|0.04',2.1873276051357129e+00,-2.8948300838305192e+00)\n    v2d.v2dx('cir|40|b|0.04',2.3101330238715105e+00,-2.7157436369318897e+00)\n    v2d.v2dx('cir|41|b|0.04',2.2267736125783628e+00,-3.2590231662938782e+00)\n    v2d.v2dx('cir|42|b|0.04',2.1349942847250238e+00,-3.2192762220613687e+00)\n    v2d.v2dx('cir|43|b|0.04',2.3555422145111429e+00,-3.4157163022751194e+00)\n    v2d.v2dx('cir|44|b|0.04',2.1962188506393736e+00,-3.7758337123121120e+00)\n    v2d.v2dx('cir|45|b|0.04',2.3299247859251206e+00,-3.8747477804098480e+00)\n    v2d.v2dx('cir|46|b|0.04',2.3401721136660556e+00,-4.0948149979730406e+00)\n    v2d.v2dx('cir|47|b|0.04',2.2628131518739241e+00,-4.3771607601792715e+00)\n    v2d.v2dx('cir|48|b|0.04',2.2953110060505013e+00,-4.4678125321108535e+00)\n    v2d.v2dx('cir|49|b|0.04',2.1851814684351956e+00,-4.6915756064926688e+00)\n    v2d.v2d_show()\n    v2d.v2dx('cir|100|g|0.04',-2.3340955768552440e+00,4.9801948853894231e+00)\n    v2d.v2dx('cir|101|g|0.04',-2.1605749241742314e+00,4.5709127191456567e+00)\n    v2d.v2dx('cir|102|g|0.04',-2.0823477095029426e+00,4.4102702646117429e+00)\n    v2d.v2dx('cir|103|g|0.04',-2.3605530527535703e+00,4.1945309409909948e+00)\n    v2d.v2dx('cir|104|g|0.04',-2.0230254518634014e+00,4.0238393739258154e+00)\n    v2d.v2dx('cir|105|g|0.04',-2.3192264590733878e+00,3.8879694623878889e+00)\n    v2d.v2dx('cir|106|g|0.04',-2.1216222051303726e+00,3.7463705645806860e+00)\n    v2d.v2dx('cir|107|g|0.04',-1.9886601275560660e+00,3.6043159982101840e+00)\n    v2d.v2dx('cir|108|g|0.04',-2.1645619676070860e+00,3.1803008917826565e+00)\n    v2d.v2dx('cir|109|g|0.04',-2.1347591528027725e+00,3.0688599530093263e+00)\n    v2d.v2dx('cir|1010|g|0.04',-2.0320894589688505e+00,2.7911223806467045e+00)\n    v2d.v2dx('cir|1011|g|0.04',-2.1154684593180639e+00,2.5239221640697265e+00)\n    v2d.v2dx('cir|1012|g|0.04',-2.0346350044844881e+00,2.5034762252365295e+00)\n    v2d.v2dx('cir|1013|g|0.04',-2.0529558583644394e+00,2.1800419006883898e+00)\n    v2d.v2dx('cir|1014|g|0.04',-2.0592568875840254e+00,1.8981848761033615e+00)\n    v2d.v2dx('cir|1015|g|0.04',-2.1020681290870282e+00,1.6573285174084387e+00)\n    v2d.v2dx('cir|1016|g|0.04',-2.0784374145153635e+00,1.5212721940167344e+00)\n    v2d.v2dx('cir|1017|g|0.04',-2.0408300876939038e+00,1.2753795759495554e+00)\n    v2d.v2dx('cir|1018|g|0.04',-2.0044318724067858e+00,1.1335091565257751e+00)\n    v2d.v2dx('cir|1019|g|0.04',-1.9962920458871403e+00,1.0817376787031874e+00)\n    v2d.v2dx('cir|1020|g|0.04',-1.9654666619042238e+00,8.6786883386340830e-01)\n    v2d.v2dx('cir|1021|g|0.04',-1.9629752482380298e+00,7.2412441693175056e-01)\n    v2d.v2dx('cir|1022|g|0.04',-1.9827771806940886e+00,4.1249121462499094e-01)\n    v2d.v2dx('cir|1023|g|0.04',-1.8148178315400298e+00,3.0620976611163397e-01)\n    v2d.v2dx('cir|1024|g|0.04',-1.9913627283935029e+00,-6.7511623558157152e-04)\n    v2d.v2dx('cir|1025|g|0.04',-1.9085102331050483e+00,-1.2091692519257527e-01)\n    v2d.v2dx('cir|1026|g|0.04',-1.7567025512845775e+00,-4.0655182374325061e-01)\n    v2d.v2dx('cir|1027|g|0.04',-1.7573126091984348e+00,-4.7886369068066464e-01)\n    v2d.v2dx('cir|1028|g|0.04',-1.7946528244114961e+00,-7.4632155601468431e-01)\n    v2d.v2dx('cir|1029|g|0.04',-2.0154230209100197e+00,-9.3702290308563940e-01)\n    v2d.v2dx('cir|1030|g|0.04',-1.8071068705401963e+00,-1.2335533997391062e+00)\n    v2d.v2dx('cir|1031|g|0.04',-1.7306796885227855e+00,-1.4972874453342571e+00)\n    v2d.v2dx('cir|1032|g|0.04',-1.9800566158586790e+00,-1.5443765055388390e+00)\n    v2d.v2dx('cir|1033|g|0.04',-1.7202354172897882e+00,-1.7671372514658545e+00)\n    v2d.v2dx('cir|1034|g|0.04',-1.8391528999004707e+00,-2.0335429832622496e+00)\n    v2d.v2dx('cir|1035|g|0.04',-1.8519982797134462e+00,-2.1369212711162078e+00)\n    v2d.v2dx('cir|1036|g|0.04',-1.6969571202242850e+00,-2.3581547587302154e+00)\n    v2d.v2dx('cir|1037|g|0.04',-1.6711310745287675e+00,-2.5002987709398945e+00)\n    v2d.v2dx('cir|1038|g|0.04',-1.8271627532433052e+00,-2.7791709457111011e+00)\n    v2d.v2dx('cir|1039|g|0.04',-1.7163698381455172e+00,-3.0549299320603716e+00)\n    v2d.v2dx('cir|1040|g|0.04',-1.8300183826082943e+00,-3.0255021281117158e+00)\n    v2d.v2dx('cir|1041|g|0.04',-1.7901509034098166e+00,-3.1658675039835322e+00)\n    v2d.v2dx('cir|1042|g|0.04',-1.7876326339702986e+00,-3.5312100308804002e+00)\n    v2d.v2dx('cir|1043|g|0.04',-1.6117960847909396e+00,-3.8080053094967141e+00)\n    v2d.v2dx('cir|1044|g|0.04',-1.7178237689728504e+00,-4.0787409605324969e+00)\n    v2d.v2dx('cir|1045|g|0.04',-1.6671618187791717e+00,-4.2900765366717764e+00)\n    v2d.v2dx('cir|1046|g|0.04',-1.6222484609708783e+00,-4.3662799716158425e+00)\n    v2d.v2dx('cir|1047|g|0.04',-1.7738583714976750e+00,-4.4039107431128137e+00)\n    v2d.v2dx('cir|1048|g|0.04',-1.7155332780077144e+00,-4.8044841550151869e+00)\n    v2d.v2dx('cir|1049|g|0.04',-1.7551213302728401e+00,-5.0045607725649397e+00)\n    v2d.v2d_show()\n    time.sleep(5)\n    input(\"enter omega: \")"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/read_group_replay.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"\\n\",\n    \"import os\\n\",\n    \"import socket\\n\",\n    \"import time\\n\",\n    \"import traceback\\n\",\n    \"import numpy as np\\n\",\n    \"from colorama import init\\n\",\n    \"from multiprocessing import Process, Pipe\\n\",\n    \"init()\\n\",\n    \"def get_files_to_read(base_path):\\n\",\n    \"    starting_file_index = -1\\n\",\n    \"    ending_file_index = -1\\n\",\n    \"    pointer = 0\\n\",\n    \"    while True:\\n\",\n    \"        es = os.path.exists(base_path+'mcom_buffer_%d____starting_session.txt'%pointer)\\n\",\n    \"        ee = os.path.exists(base_path+'mcom_buffer_%d.txt'%pointer)\\n\",\n    \"        if (not es) and (not ee): break\\n\",\n    \"        assert not (ee and es), ('?')\\n\",\n    \"        if es: starting_file_index = pointer; ending_file_index = pointer\\n\",\n    \"        if ee: ending_file_index = pointer\\n\",\n    \"        pointer += 1\\n\",\n    \"        assert pointer < 1e3\\n\",\n    \"    assert starting_file_index>=0 and ending_file_index>=0, ('查找日志失败:', base_path)\\n\",\n    \"\\n\",\n    \"    file_path = []\\n\",\n    \"    for i in range(starting_file_index, ending_file_index+1):\\n\",\n    \"        if i==starting_file_index: file_path.append(base_path+'mcom_buffer_%d____starting_session.txt'%i)\\n\",\n    \"        else: file_path.append(base_path+'mcom_buffer_%d.txt'%i)\\n\",\n    \"        assert os.path.exists(file_path[0]), ('?')\\n\",\n    \"    return file_path\\n\",\n    \"\\n\",\n    \"def read_experiment(base_path):\\n\",\n    \"    files_to_read = get_files_to_read(base_path)\\n\",\n    \"    cmd_lines = []\\n\",\n    \"    for file in files_to_read:\\n\",\n    \"        f = open(file, 'r')\\n\",\n    \"        lines = f.readlines()\\n\",\n    \"        cmd_lines.extend(lines)\\n\",\n    \"    dictionary = {}\\n\",\n    \"\\n\",\n    \"    def rec(value,name): \\n\",\n    \"        if name not in dictionary:\\n\",\n    \"            dictionary[name] = []\\n\",\n    \"        dictionary[name].append(value)\\n\",\n    \"        return\\n\",\n    \"\\n\",\n    \"    for cmd_str in cmd_lines:\\n\",\n    \"        if '>>' in cmd_str:\\n\",\n    \"            cmd_str_ = cmd_str[2:].strip('\\\\n')\\n\",\n    \"            if not cmd_str_.startswith('rec('): continue\\n\",\n    \"            eval('%s'%cmd_str_)\\n\",\n    \"    return dictionary\\n\",\n    \"\\n\",\n    \"def stack_cutlong(arr_list, min_len=None):\\n\",\n    \"    if min_len is None:\\n\",\n    \"        min_len = min([len(item) for item in arr_list])\\n\",\n    \"    print([len(item) for item in arr_list],'\\\\tselect:', min_len)\\n\",\n    \"    return np.stack([item[:min_len] for item in arr_list])\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def smooth(data, sm=1):\\n\",\n    \"    if sm > 1:\\n\",\n    \"        y = np.ones(sm)*1.0/sm\\n\",\n    \"        d = np.convolve(y, data, 'valid')#\\\"same\\\")\\n\",\n    \"    else:\\n\",\n    \"        d = data\\n\",\n    \"    return np.array(d)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def tsplot(ax, data, label, resize_x, smooth_sm=None, **kw):\\n\",\n    \"    if smooth_sm is not None:\\n\",\n    \"        print('警告 smooth_sm=',smooth_sm)\\n\",\n    \"        data = smooth(data, smooth_sm)\\n\",\n    \"\\n\",\n    \"    print('警告 resize_x=',resize_x)\\n\",\n    \"    x = np.arange(data.shape[1])\\n\",\n    \"    x = resize_x*x\\n\",\n    \"    est = np.mean(data, axis=0)\\n\",\n    \"    sd = np.std(data, axis=0)\\n\",\n    \"    cis = (est - sd, est + sd)\\n\",\n    \"    ax.fill_between(x,cis[0],cis[1],alpha=0.4, **kw)\\n\",\n    \"    ax.plot(x,est, linewidth=1.5, label=label, **kw)\\n\",\n    \"    ax.margins(x=0)\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 10,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"\\n\",\n    \"\\n\",\n    \"party = [\\n\",\n    \"    # {\\n\",\n    \"    #     \\\"Method\\\": \\\"AddBnAddAe\\\",\\n\",\n    \"    #     \\\"path\\\": [\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-original-addbn-addae-r1\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-original-addbn-addae-r2\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-original-addbn-addae-r3\\\",\\n\",\n    \"    #     ]\\n\",\n    \"    # },\\n\",\n    \"\\n\",\n    \"# ZHECKPOINT/pymarl-starcraft-sim-AddBn-r1\\n\",\n    \"    # {\\n\",\n    \"    #     \\\"Method\\\": \\\"AddBn\\\",\\n\",\n    \"    #     \\\"path\\\": [\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-AddBn-r1\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-AddBn-r2\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-AddBn-r3\\\",\\n\",\n    \"    #     ]\\n\",\n    \"    # },\\n\",\n    \"\\n\",\n    \"    # {\\n\",\n    \"    #     \\\"Method\\\": \\\"Original\\\",\\n\",\n    \"    #     \\\"path\\\": [\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-original-r1\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-original-r2\\\",\\n\",\n    \"    #         \\\"ZHECKPOINT/pymarl-starcraft-sim-original-r3\\\",\\n\",\n    \"    #     ]\\n\",\n    \"    # },\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"    {\\n\",\n    \"        \\\"Method\\\": \\\"AddBn\\\",\\n\",\n    \"        \\\"path\\\": [\\n\",\n    \"            # \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-AddBn-r1\\\",\\n\",\n    \"            \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-AddBn-r2\\\",\\n\",\n    \"            \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-AddBn-r3\\\",\\n\",\n    \"        ]\\n\",\n    \"    },\\n\",\n    \"    {\\n\",\n    \"        \\\"Method\\\": \\\"Original\\\",\\n\",\n    \"        \\\"path\\\": [\\n\",\n    \"            \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-original-r1\\\",\\n\",\n    \"            \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-original-r2\\\",\\n\",\n    \"            \\\"ZHECKPOINT/pymarl-starcraft-5m-sim-original-r3\\\",\\n\",\n    \"        ]\\n\",\n    \"    },\\n\",\n    \"\\n\",\n    \"]\\n\",\n    \"for ex in party:\\n\",\n    \"    for i, path in enumerate(ex['path']):\\n\",\n    \"        ex['path'][i] = '../' + ex['path'][i] + '/logger/'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 11,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# smooth_sm = 4\\n\",\n    \"# main_key = 'reward'\\n\",\n    \"# main_key_name_on_graph = 'Mean Episode Rewards'\\n\",\n    \"# drop_data = 50 #None # 5\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"smooth_sm = 1\\n\",\n    \"main_key = 'test-win-rate'\\n\",\n    \"main_key_name_on_graph = 'Test Win Rate'\\n\",\n    \"drop_data = 1 #None # 5\\n\",\n    \"\\n\",\n    \"max_raw_x = {\\n\",\n    \"}\\n\",\n    \"# drop_data = 50 #None # 5\\n\",\n    \"# drop_data = 500 #None # 5\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 12,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"readings of ../ZHECKPOINT/pymarl-starcraft-5m-sim-AddBn-r2/logger/\\n\",\n      \"readings of ../ZHECKPOINT/pymarl-starcraft-5m-sim-AddBn-r3/logger/\\n\",\n      \"readings of ../ZHECKPOINT/pymarl-starcraft-5m-sim-original-r1/logger/\\n\",\n      \"readings of ../ZHECKPOINT/pymarl-starcraft-5m-sim-original-r2/logger/\\n\",\n      \"readings of ../ZHECKPOINT/pymarl-starcraft-5m-sim-original-r3/logger/\\n\",\n      \"警告 平滑系数smooth_sm= 1\\n\",\n      \"警告 平滑系数smooth_sm= 1\\n\",\n      \"警告 平滑系数smooth_sm= 1\\n\",\n      \"警告 平滑系数smooth_sm= 1\\n\",\n      \"警告 平滑系数smooth_sm= 1\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"samples = []\\n\",\n    \"\\n\",\n    \"for ex in party:\\n\",\n    \"    pathes = ex['path']\\n\",\n    \"    for path in pathes:\\n\",\n    \"        ex['readings of %s'%path] = read_experiment(path)\\n\",\n    \"        print('readings of %s'%path)\\n\",\n    \"\\n\",\n    \"def shift_x(x):\\n\",\n    \"    return x* 8\\n\",\n    \"\\n\",\n    \"for ex in party:\\n\",\n    \"    # ex_ydata_batch = []\\n\",\n    \"    for path in ex['path']:\\n\",\n    \"        ydata = ex['readings of %s'%path][main_key]\\n\",\n    \"        ydata = np.array(ydata)\\n\",\n    \"        if smooth_sm is not None:\\n\",\n    \"            ydata = smooth(ydata, smooth_sm); print('警告 平滑系数smooth_sm=',smooth_sm)\\n\",\n    \"        for x, y in enumerate(ydata):\\n\",\n    \"            if (drop_data is not None) and (not x%drop_data==0): continue\\n\",\n    \"            if (ex['Method'] in max_raw_x) and (x > max_raw_x[ex['Method']]): continue\\n\",\n    \"            samples.append({\\n\",\n    \"                'Training Episodes':shift_x(x),\\n\",\n    \"                main_key_name_on_graph: y,\\n\",\n    \"                # 'color':party[exp_name]['color'],\\n\",\n    \"                'Method':ex['Method'],\\n\",\n    \"            })                \"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 13,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"<ipython-input-13-505f44716fa5>:25: UserWarning: FixedFormatter should only be used together with FixedLocator\\n\",\n      \"  ax.set_xticklabels(xlabels)\\n\"\n     ]\n    },\n    {\n     \"data\": {\n      \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAAjIAAAFFCAYAAAD2Awe9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAADcB0lEQVR4nOx9Z4AcxbX1qeqeuDkqJ4QQCCUUQAJJZITA8MDGAQQODz6MsR+2weY5vYeNMfCwCTYYbIwJJhkQGQQSWSAJFBCKoBxX2hwnT3fX96M6d09a7Uq7oo8tdqarurq6Z6br9L3n3ksYYwwePHjw4MGDBw/9EPRwT8CDBw8ePHjw4KG78IiMBw8ePHjw4KHfwiMyHjx48ODBg4d+C4/IePDgwYMHDx76LTwi48GDBw8ePHjot/CIjAcPHjx48OCh30I83BPor2hpiUBRDi5yvaIijLa2WA/NyMOhgPeZ9T94n1n/xJH0udXUlBzuKRzR8CwyhxGiKBzuKXgoEN5n1v/gfWb9E97n5iFfeETGgwcPHjx48NBv4REZDx48ePDgwUO/hUdkPHjw4MGDBw/9Fh6R8eDBgwcPHjz0W3hExoMHDx48ePDQb+ERGQ8ePHjw4MFDv4VHZDx48ODBgwcP/RYekfHgwYMHDx489Ft4RMaDBw8ePHjw0G/hERkPHjx46GXsru+CJCuHexoePByR8IiMBw8ePPQyGBjSkkdkPHjoDXhExoMHDx56Gezg6st68OAhCzwi48GDBw8ePHjot/CIjAcPHg47FIXhQEv0cE+j1+AZZDx46D14RMaDBw+HHWlJQTItH+5pePDgoR/CIzIePHjoE/B0JB48eOgOPCLjwYOHPoEjmscc0SfnwcPhhUdkPHjw0CtojyQhK3mGHBMclsU+mkgjkZIytnfGUkhLhbm8kikZ0UTasb09kiz4+B4KA1MUKNG2wz0ND4cYHpHx4MFDr6A9kkQ82bd1L03tcTS3JzK2t3Ym0BFNFTRmc2cCTe1x21bmqgFqbIujuSPz8T0UCCkJFus43LPwcIjhERkPHjz0CgrXvBx6k0yuOXZXt+O2n6cBOjRg3oX+0sEjMh48eOgVFLqcHEnrj/1UMp3bEXTKHjwcNnhExoMHD0hLClI9GP5s133EEpLlSdn+XoMkK2jrSvb6U3UiJUGSlZxEIld7zEULY2Yt8aQERX3vOpbHZHoB3kX9skE83BPIhHg8jgceeAALFy5EQ0MDKisrcfrpp+O6665DZWVlQWO99NJLePbZZ7F582Ykk0lUVlZi+vTpuPrqqzF27NheOgMPHvoP6ltjkBUFIweW9th45vWksT2G2vIwwkFRf19THkJR0GfZr6UjgVhSgt9HHW09ifrWGMJB30GteYwxNLbHMXKgyzyZcZyq0mC2Ubo/AQ8ePADooxaZeDyO+fPn4+9//zsA4Mwzz0RJSQmefvppXHLJJWhpacl7rP/5n//Bf//3f2P9+vWYNGkSzjzzTBQXF+O1117DJZdcguXLl/fWaXjw0G/AGIPSg2sqN0Iw3bJiN7Bk05Aw8CCm3oTCMkzCjm5raLTzZlmtS0eSO61vwLugX0b0SSJz//33Y+PGjTjnnHPw5ptv4p577sFrr72GK664AnV1dbj11lvzGmfdunV49tlnUVVVhYULF+Lhhx/Gn//8ZyxcuBA33HADUqkUbrnlll4+Gw8e+gl6ksjk2GrnEcS9W++BOV50e5hcREUniJm6eWuvBw8HhT5HZFKpFJ566in4fD7cdNNNEEXD+3XjjTeisrISCxcuRGNjY86xVq5cCQD4j//4DwwbNszSdtVVVyEUCmHbtm2Ix+2hkh48fBlR+IqaSsv6P8W8oDMrUXEbOZWWIck8z8w1d36AJ9/a3O15aJAVRR8zO5jLKyt4uDSzvNdCqJNpGcy2c1oyrgFjfC6MMfzj1Y34w+OrLf2Sqcx6JIUxpKU88+9o+ygsz/PuGUiyAqUnTXg9Cm4FY1LusHmmyGCyl8env6PPEZnVq1cjEolg2rRpqK6utrT5/X6cfvrpUBQFS5YsyTmW3+/P2ScYDCIYzObD9uDhyEd3xLWxhIT9LVH9X0ekgHwrjCeb29sY0Tftboiocyl4KjrqW+PY1xTJ2U93Y2U5lrmIZVpScKAlqm/b3xyFbCMOdc1RtHUmTfvHoAD4Yk+7dlS934HWKOJJCYwBzEalWjsTqGvOfQ5mNLXnd949hX1NETQ6cuX0HbBkBErb/pz9lM4GKK37DsGMPPQm+hyR2byZP5WNGzfOtf34448HAGzZsiXnWDNnzgSlFK+88gr27t1raXvooYcQj8dx0UUXgZDe9sh78ND3USiBYGB8Idb/MUurrbOOVFrGLf9ahRWfN7oSqIMhMorCekx3wuxWJWYnQDwdMbO0GxYZRWGu5h7zNXM9hyxtmSD34HnnA/38+iQYoFrDciLffh76NPpc1NKBAwcAAAMHDnRtHzBggKVfNhx99NH42c9+hj/+8Y8477zzMH36dJSWlmLr1q3YvXs3vvGNb+DXv/51z03eg4d+ioO5lbuJc/XF2qV/Z4xbbpatP4Cpx9S4zKP7s2HMbt/I1K8bY6t/FZZZxOzsbXJhuVqBXETW7NCSkiMX+V5E72L3d/Q5IhOLxQAAoVDItT0cDgMAotGoa7sdV155JSoqKnDTTTdh6dKl+vaRI0dixowZebmf3FBVVdyt/eyoqSnpkXE8HDociZ9Ze0KGLCs5z02WFRBCwAD4YymkGNGJTEVJEFVlQTAALdE0AIby8jCqykJojaVRVV2C4pAPaZX2UIGioiJsOWZpWQiCX0R1dQmKQjysmTEGRWEQBMOArEVZCdRKoToSXKdSU1Oi78cYQ2VVsaVvcySF8pIgBH8Sfh/V5yDLCgSB4r7nPsPm3W344SWTUFkaREVpEF1p7kqqqipGazSNyqoidKVkVFUVgwBoi0soKw4gkZLgj6cRCoiIxo08M9q5tsb4turqYsQkBQGfYLkGEqEQo0nHZ8EY18H4RAGywkAAUPWcEgrPWZPz87PtZ39vhqJwUqhdN+3aQD3XUEC0HE+z0LiN9eSbX+CDNfvw4C/Pyjo/fp6Gyy7b+TBFBqGCdc4JihTCEMuKIYlxBHNcjxQrgpIWc/bz0LfR54hMT0JRFPz2t7/FggULcM011+CrX/0qysvLsWHDBvzhD3/A9ddfj/r6elx55ZUFj93SEjlo02pNTQmamroOagwPhxZH6mfW3haFrDA0NWXO3cIYw+6GLoT8IpKquFcPYyYELC2hvrETiZQEWeYEoqsrAWVgKdrbYwhSIB70YcPWJj6ewtDWFkNT2DhmQ2MX0pKCIpEipuac6Ygk0RZJWnLcNLXHEU2kLdsSKQlt7TEoCtAUEtHWlURHNInhQyqwp67N0retPQYlLaErloZPpGjyC5BkBfuaIhg5sBSLPt7Nr0t7DEySIKfSaG+LAiDYkkyjLZJEJJKALDOs6eRaEYUBcjKNZFpGNCkh7hMQTxpCUu1c29rUhzWBoL09Bp8ooCRgLMitbTF0xdKW6wIAjW0xRBMSRg0qxe76LoQCAmor+INda2sUibSMplD2W/qu+k6Egz7UlvMHRfs49uPFkzJGDCxBWlJQ1xzRr2Fbewwxn4CgSZywt7ELokAxqKrIMda/VSF3Pr8duXUfQCgGHjM2Y38mS1Ba90GoGWndnopBbouBSl1QIhGI/uzHk9siYFI6Z7+DxZH48NOX0Oc0MprFJVMkkWaxKSpy/ljseP755/HMM89g/vz5uO666zB06FAUFxdjxowZ+Pvf/45wOIx7770Xra2tPXcCHjz0Q2h6l5z9GBe+ygrD44s2Y8WmBr6fuq8kKWAMWLW5EU+8tcXVnZLtAUDLP2OGJDvnllaPY4aiaKHOvIFHDfEoIUceG4CTL5P7yy2XzgMvb9BFvZoGRpIVPSrLondR921qj+PeBevQGUvlruWU5zb7ObtGNuX5+Umm/bJFSKUlRVcAMbu7y+VYaYlBknvATSNLgOySMdkMloe2Je+peK6l/o4+R2QGDRoEAKivr3dtb2hosPTLhldffRUAMHfuXEfb4MGDMXHiRMTjcWzcuLG70/Xg4YhAvpoMZvq380AXFq/ca2oxXi1asRd7GtyjaLSehDiP61gwHaNn3kaIc6NTqWLaYDuQmYxoaO1MosvkHnILKWe2LR9vakA0IeHzXW2uM3cKffMXPOvaHPt29+4Hjcwh9G5zZj2SyPDQi289ItPf0eeIjFYyYNOmTa7tGuk45phjco6lkZ6SEneznra9s7Oz4Hl68NCf0BOLg2bsYIxh3bZmffuuA1145t1tRh4Z06E+3dKEp9+2RhgaU3HRZWQSuroRFNeIINvK6zKnDBuc+zvazJYrpv6f4e2Ve7Hyi0ZLP7ejGNutjElhbsc1LCFm6EJj5nYGuT9j63zyE0YD7iTRMTZjOLQBoBnZnvWvaxcGJRlF4v2HoHR5Fvn+jj5HZKZOnYri4mKsWrXKUYoglUrhvffeA6UUc+bMyTlWTQ2PiNiwYYOjTZZlnSwNHjy4B2buwUPh2FXfibQkO7btqu9Ec0ccexqsvvu0JGNXfaelr7bY7a7vQnskqW/XikAyxrBjf6fFhdDYFrPkSbE/52tzMP+LJSTwKBuGV5bu0vs+v2Q7tu7rQCIpW0YDgNeX78Zbq3ieDi3vCFP4PAjhRMB8Pkw1izgXRIb9zVF9DH7N3K0CjPH5RxJpG3kA9jVGUNcU4du1g6h99rfkCiBgeiZfPeEduAXmzU/2gDHwY7pEKmnYVd8JoWOvnoSNgbvaduzn11gL2WZq3x37Oy2J7jSdEsBdXA2t/HO0f4e0Y3VEko7tGuHcsb8TQsd+kFjmhTyTFUZz2ZnnZnZQ7arvRCSehpLogty0K+P4+RxVibXrY8hNu6DErQ+ecvNuKNE23rfDmiiVSSnX4yuRFqTWL4bSvBvSF+8XOD8PfQ19jsj4/X5cdtllSKfT+N3vfgdJMsRyd9xxB1pbW3HeeeehtrZW337nnXfi3HPPxRNPPGEZ64wzzgAA/OUvf8HOnTv17bIs46677kJdXR0GDx6MCRMm9PJZefDgDr4gOl0cjAGJlOxoUxT+z9JXa2PMRF6g6xUYtDwjxljxpIy4ObusbcHVcpkoivqX8fwvjAFf7G7LeU5u27Tt2trX1pXEuu3N+HDtAWu/DGMm0jISqnjW7RicGBn97dcJANKygrTM9RVdsRReXbZLJ3iaBsZxbHUcpnbSr7fCsK/RSjS168UnZB1n54EO7G+OYfHGCJgsWciarHB9jrlStkaW7ONruXIUhSGRkpFIya65ZxQFeiZi42SgXySZMRBFApGcZMeOt1fvw+e2z93x3WXWtrSkAFLq4K2BUtoU7s4AW8ZepihA2u0cGLiWxkUDJKeND9ZDv0efjFq69tprsXTpUixatAjz5s3D+PHjsW3bNmzZsgVDhgzBr371K0v/pqYm7Ny5E21t1h/a/PnzsXjxYqxZswYXXnghTjjhBJSXl2PTpk3Yu3cvAoEAbrnlFksZBA8eDiWcegnTeuB2/yfWxdbiKgB0K4Ob24HZ32U6rvqGaceyiVmefW97hv0YMvsWGLSV3VzK4PXle6y9XK6HpdHEDjISJmhEwHBTmT1MmpfkjY/3YP2OFhw1qBRHDy2zOXyyTME05hOLt9raTdfVNtjT72xDwEeRTCuYOUlx2c9kF9POw3Y9FMagKPwyKwogUKO/c65ON53d9ZYvyXhO/czPnzkya7+DpCx5wPwpuXzLzR84/0FkGUb7DLyEqP0dfc4iA/AcMk888QSuvvpqKIqCt956C+3t7fjWt76FBQsWoKqqKq9xAoEA/vWvf+HnP/85xowZg/Xr1+Odd95BOp3GhRdeiAULFuCUU07p5bPx4CEb7NYYZrlX22/VBNoC5bZy8T+b97RhtUmzAea0Nph3b+tK4p3Vey1PvbFEGotX7IEkyfr+mZCtbpB5atoQcpbIlrqmCD7d0pR5nAxiV4C71lZ83gCNBehaFtse7V1JLFl7QKd2THXTvLViL6Jxl7o7Jg+U9vF8trUZK79osHR7/7P9UBRFJ2runE4lmi5rsfYZLV6xB+1dCQfR0C1B6iuFMTAwbK/rwFqTZskYj1nrXzl7dCvypzOawtur9jmshZl3M949/fZWROJpvLN6H7bt68h+bNcxzAQ8E6Gxb3e3vMh71vIXHo/p9+izpohwOIwbbrgBN9xwQ86+t99+O26//XbXNr/fj6uuugpXXXVVT0/Rg4eDAte3AA1tMVSVBlESdk/OuKu+EwMqwmhsi2NQVRgKA3Ye6MSIgSUOMwoB8H9PrQEATD+uFqGAqNb0YYglJNS3xkCg+WD4zo+88Tk27GjFqMFlqCkPo6zYj/fW1GHN1mZUl4cwZUy1bhnIte7Rtj1gIWdEoXa4XfWdULKY9J98m1s4Zk3kY9SWh9EVT6nuLW7x2VXfqROVHfs7cNTgMuyq79TP+6RxA1Qhq5UEdESSAAMWvL8d+1tiGFRhXO+125rxyecN6IwlMazWmuySaQs246RHad+PRSud9XmWrj+ACaMrTcckTiKqkyKG+laeSkIjMIwBexujeP+z/fh8dxv+3wXH6+Hgmo5IYXw+jBKdhDytXrOJo6vQ2pmArDDUlIegwMXaxwBJLaxpJXouUCdr1lLJkVY8vrgOq7c0YeTgEgxXr1W6cSeglABM1K9VRzSJMlve0LdW7UUyLWGJ6k78x5VHgZYNAPFbE6BmJljudjOWTqjaK6a/B1OgdBzgn5uUhNJ2ALRiMIjoB8BMNZYImCKB0D67HHrIgT5pkfHg4csAMzmw6FVsJhlNL6OYHuN59WGtgzGeffxYQtKtCImUrBMC8yKvWUgYY4gm0gAzcr0wTY+h75PPU7izTzSWxtL1ByDLDAdaYjmvjZYTJpGS+OINhvauJD7eUI/d9V34Yncrvw6q9sdNqmGOymGMi3L3NXYZuU5M+pMv9rQB4LqiSNyZw4QxYNu+duw60AU5lcg47w3bs0fAaPPZuKsDO/Z3Yun6A1i7vRn1rTGucVJFu5qmSbcZaSSSMZ2k2a0tjAGReNqYv82iA9NYOw904oPP9uew2HAkTN9NlozqAl9m0SAxy/XWjuMGs0WOV6nOrdFxNVFarFkMFjGTesGYKh5imlZHz09j+8Ioua2KHvouPArqwcNhguEkIZZt1gWAQcvOwWC5R+elbzAv5PaFXYOWUV4nLxaaY2pjcGx3HtDd2vLqsl3YVd+FYbXFehRT1mFU9sU0CwQDXv5oJ/abSNCvLp9inIhtgdOuEzGN98y72wBAz2pr3uWd1XVqP7icPceCD3YAAH5zfmbX9kfrD2Dy0dUA1GPbhtLm885n9QCsubL++7IT9OR7miyJKca3RD8vGBYcO+zfHWubQUUfX7QZ+5qimDagHCFnUl/LGJbsMAymIrtWQgIXK5jbPKyunNzfYdtJOBm7Ngaxb9VsYjoLdD+s51rq9/AsMh48HEZks3Ew+wsGbNjRgqb2uGohsd7TzQu3vpupE1M7KbKClV80QtLDoFURrmKMY+YHmkVmx4FO7G1yT3JngHBdjQ2aG0WW84sUkZlmaVH/KoqFxGhz21bXgX1NERup41eVmRavfEWtCmOgNnGLfc/P9+eyIGSzTWWGrDBs2ct1I5QQNLRFsX0/dynVN8ewfT9vYwzYuLMNrZ1JfLKpwTFOBqMFtu/vxK4DXQADWjoTanseM8xw7ewSGTNXyHa5P9lkDpHO9wplOivtfab2TOzbRvKSMaQ2vZf398RD34JnkfHg4XDB9KAYS6TR1kWRMqXT1566CWFo60oCYPjrizwn0g3fmqzfdOtbY6pLwm1lMRET9e/a7S1YvHIv4kkJflEwwo/V/TujKRBiXQwUBvzt5ewZsJNpGWEAH3/uFOtq7ol8XBkAt0Qoqg5EATK6oxa8vwML3t+BX10+Vd+mKAxEIFAYQBgvpmhddPkb4rLqcg2JdY7RRBp1zQaBe35NdjKnDVdocjjGuOWK70vwz9e/AACcMKYaD73Oc1795OuToDCm97ONoP9hjIEmO0GYD3sPpIBoK25dwCPEfvPtqcZ3TAHkdAr1e/agprYKBzoZBhdL4JRYdLorbd8x4xz5FywtKUa+IsavXaLT6oqTLGQ2T+LAACaloBETpW0/wJiRI4YxsGTMNJ7pL1NUSyGD0tkEWh12HDe57ElIO1dBqBoGYcDR+c3JQ5+BZ5Hx4OGwwXrTbYsk9aRzZksCY4AkW+sFMYVhX2MEB5pjSKr5XRhgMcms296iR5bobgUGPddMPClBUQytheZaWr+zBa2d3OrQGU2hM5bG9n3tOc+GRy8xbNqdua+dJGSCrgdR36RcrDzm62HOtyLJPL/K5r3tUBhDNJ7GVsv8+Y4HOjRyZbTsqu/ER+v2W47D87vkNW0AwMZdXCezfkcLNu20JvXMxm3MJE+zYGnno6G5nRdyzARDVQPQeAdIog2bdzWho9OU/NBscVNJTzraCSnaibSsQIm0gsXaLf3dXpu5gJbDhzGYPiuGzmgK8ZRLJJjLGNaNzgaWipvIucnqpp1LvMPl5MwDaP0VSDs/NTUQsCS/PiydWf/koe/Cs8h48HCYwHTfDzEt3NZIF4UBAuEkQ6DW7bc+wW/Gv/n2NOiBSCa8snQXOqMpnDx+oK6rUGDKrQLNHWUKCWbAY29s1sdYtqEem3a1oj2SQi5ox2/uyLwYKPl5lrhOhxHdiuSuBzE2mkOBl22oR0tnAlv3daC69Fi89NEubN3bnnPefEzg/TVWImPWquQDjXjsaYg46k3Fs4SqKxncbmZX3RNvbcXxIyvymocmwH3qgzqUmMpUmwmTJdGc0QMgon7tFXe2YbXOaJ+TqqjJ20PD+Pc/b+gDayTFRFrM2SKz5DNSYh1IrX7RupnoQrH85+Khz8CzyHjwcBjBmP2f8UTN27WoIaCuKWrdUcXexi7E4jzaiNgWhV31XdhW14HWzqTVyqEehIGBqHcBBcw1NDofEgMAW/e2Y2dz9r5d0XwiVICd+3loemcshf3NhlbEDE0zAlgtJpF4Wi/V0NKZyEpiAPsi7t6eqQBmT2JLhrwqsm1+9a3xnGMlUhJ2tqR07XVXwlzmwOi3rUl112ht6n+0LSlJwi7btc/mMuP78ZSDluR0GZCfqzETk7WnbmbGNsuxba8Vl8raVFD7yJAbd0AxWaQ89H14FhkPHnIgLcmIxCWUFvnQ2BYrOMghEk+DEiAc9AHg+UyCAav+wHgyNpMYfqMXVI3Ko29+YfQ3jf/Ym5tRUx7Elecf5zj2rvou7KrnafRv+u50xwOwWSDMFOCzbS2OMfLFq8t25+yzOI+IJYBHCH37nDF47oMdGV0pC97fob9WLBYTw+pkrgmlobHdSqZyraWKAjy+eHP2Tj2ANz7Z47rdbqlp6cxi8VKZyD9e3YS127rw07OduYnMxO3tLxIYUhnAyCofEtEuUElCYzKKmkoKkmjBwmVN2LTHIFjRWAJMC1VORVHX0A4IPpTGVHIgJdFSHwFNJSGHcycufWdTF+bOHAAl0gISLgfRCIU213RC1b4ATK2nxN0/2jkoOkEx8sg4JO9g0Xb9Peu0f8cZt0ABYIqC+Es3gwSKUfyd+3LO30PfgGeR8eAhByJxCe2RJBIpGZ15Wic0MMawZkuTxd3S0pVEZzRl6mPqrz0Omx4iE2kZ+5utBQ0b2qzi16b2hCoMzjIXqHoYU5i1+cZf1xxBJObytHqYsGN/Z1Y9iBkHTNdnT0Mkq3urUMSSh/eaZEmEbIGZ7mji6IjL9auzfZeiSQXJtIyuWBL76pogyQpSaRkkGUFLh7VvVzSl1zoiqTiQiEBhQFc8hbqWGGgyAhZrx4HGdkPbk2X+rRFVpxTrBMz6FG3XaBu0WklMFewa0Wg21i9Lth+TST+jmzwBJW61fLFYO0DVpZCptcqSvW+B89Bz8IiMBw85YMm/kne4KMf7n+3HI298YS20yIybsVV0qT8/wvzqpQ934t/vWGv6/PudbS7zzOdcrPd+M/n5dEtzHuHVhw4fbXCGFmfCM+8Z16OzYDKW/cI9sXhLgeP1LPLVm8iSop+KoCYHSkvOnZ9YvMVSTFITzC7fFsfDS9uxXXc3MQgOZmwmCgpPSKgwrN6VwNPv7sTm/TE0dKbxz4868MFndcb4GUBMvyxkyk8DzdJiWF8sX2LT3Ky6HdXVxBiknSuhtB8AGHPoY5TWfSBEtQR5ifH6JTwi48FDPshbvWiFlt69tctwZ2gVkq2kRbtBG1lcmzviUPLMhEsJtxx0xTJbjNq6kmjpTCCtZWYFXyjMa1VHgRanIwHd+2QPHfIlz9wCwvtqRCaVjzlHdUdxAgO0x3j0mSQzdCbsC7slPR5aOlNISzI+r+f7tkUlRNR9GkwlGDJhc4P2fbO7hEyT007LVXBt/e1YD2bYqNJrFyL54SN8j85G2KF0qSkDPLFvv4SnkfHgIQfsEZ2aQDGelFCk6l4yQRPfKowhnpR4qDQYYknJQmHMxwKA1s4E/vHa5zhp3AAE/QLiySwhrAAoJbjvhQ1Z+9z7/HrHNoVZBcLKl/BG3k2OesiQpTSVBcs2HMBZ04YBAAQ1xC2Vzr2zon7Bt6ikQlEY2joTeG51FyIJ2/7MCHWPJBUseGsfAEP3RJU0GOPHJkyCLCtZidje1jRa2rpQ4XPvwxSrFYZHRSnGj9LuT9UsncmYzYVqbne5Bs271BcGcVOibaBF+UWIeTi8OCiLjKIoaG1txf79+3N39uChH8McshxLSIjG02hqzx09Yr7H1rfGsL85Csa4daS9K4l4UkJbhFtKtKillKToOob9zdGcJAbgRKZQdERSSKQkyxyj8SPfIhP09a+c9Il0fu6OLXvbIUkyoglJzxWUDweKJo1iiwAnMolkGpvrXb4LzEh41xxxmRdTdE0LZbIeKZcN8Ugss1mMmebGLKZMdZtmsbGTFZulxjJefmCJvuNm9ZAd3bLIfPjhh3jkkUfw6aefIplMghCCTZs26e0PPPAAtm3bhl//+teorKzMMpIHD30fzOx7V8N88rg/W8ew3E8ZHnptk6UYHwCcP3MExo2swPrtLXh7NX/KlRXm6OcGp5YhN3Ye6MR9L2zAhKOM3yhf1I5sJNLWD66PG2Twj1c35e4EoC2Swj0L1iFpssLIebiWFn8ex/GDjKVAK8Lp5skxf+dX7XaG0hNm9c5o7stskBUFYBm0OOa6GbYwai3rktLRAJaKQqgeCblpJ0i4HIh1gIRKQHxBy6Sl3Z9lnYvFetO/+O6XGgVbZO666y5cffXVWLZsGSRJgiiKji9qdXU1Fi5ciLfffrvHJurBw+FEJu98Lmj8gjEGSZaRkmT+xOtCTvY0dCGVVrC7oUvflsrzaZx0wyLjQUVfZzIFIGlzJeVZ2spC7tIyT8LnngMw+8UyV0jnRTNzX1xZIyn6j8U8oCFgZooCJhmVvbWng+SSfyL18b8BMKRXvYDUkoeRWvU8ksufVvuazm39mznnY8D7TfUXFERk3n77bTz44IOorq7GAw88gLVr12LChAmOfmeffbbe34OHfg+T+aW7egqFMdz+5Br8ZcF63PH0Z6591u9oxZ8XrMNWU2K0fMOIhYMgMqQb1hwP/QP51rZ64EPDjfLO1jQWfBbPmU3ZDQSGJYdX8FawpSF7FBlP/8IARQaTtb6Mvza5ltKblyC5+M9gUlLfllUILSXV9u5aGb3fRX9BQUTmySefBCEEd999N04//XQIguDar7y8HIMHD8YXX3zh2u7BQ3+C1Y3ErA05oIkt8zHxHwzsFZsLwZf9dq0XOTwCIXXze7er1f2aEJcv/Qn+nTgtwN1fvLSEUTSSpOLY1uym8WL4evhjDBFaeDZpxgs6yi2GcDjVuEfXxTA5DXkfF7OnVj7Pk/LZ/LvS7jUuh9ES5XUDX/YfRj9CQURm48aNGDBgAKZNm5azb1VVFdra2ro9MQ8e+gq0mjX8taUl577vreG5NORCqg52Ax3R7ot0127vfjbfIwHaZ3QkQs6yiFMoOFqsL2g8xfRb0PDd4g9xcdEq/oYxPPspF8Fva0pDT/BoQxFJYlZwC64teVt1f2mdtPmqhEiPWJKh1dJQmnaqIdRWIW96/eKCzsUN1hIfHpPpLyiIyCQSCZSXl+fVN5lMwufLHprqwUN/gKwYlaO7S0d6m8h48OCGbBqZuaF1+K/SxThKzD/xoKVWlwvsrizGsrt/CLSK1GYhr7az+h81G7XFBZqv2p7p/8kL5rl6Ltf+g4KITG1tLfbs2ZMz10QsFsPOnTsxZMiQg5qcBw99AVodH1kxMvHq99csBEUyrSKfbCpgsfDgoYfw/tbMRToHCu0AgFKSO42Aho37U+5h2SrsVcIXrOrAmn1O15I5rR7/XWnuKHveGrVNMSwyHAoSb96F9JaPss438cafIK3L31KT/vwD/XX02V9C2v953vt6OHwoiMjMnDkT8Xgc//73v7P2e/jhh5FKpTBnzpyDmpwHD30B2lNmW5dTeLu/JerYpmFvY1fGNg+HDyf5t2Kw0HrYjj9UaME0//aM7aOr3bWHvQVWgAvlk905hLs2K8kXGUiP1ksXB+sPCEytmQTweuzMcC85Et8B8vZPcs5Zrs+/4KfStMPyPv3Fkrz39XD4UBCRueqqq+Dz+XDbbbfhkUceQVeX9Ubd2tqKu+++G/fffz+Kiorw7W9/u0cn68HD4YKRSJRZtklZbPd9PWPskYpcy/Jlxcvx32WvHZK5uOHnZa/jiuKlGdvzJTJ+pFFSgDUFAMpIDAK6X0+oriPz972YxPHB9tzJGwHzZ8S4VZMppsKOagkNSeYWmVgbF/cSr6KOB3cU9M0YMWIE/vjHP4JSijvuuAMzZszA+vU87fmsWbMwa9YsPPjgg/D7/bjrrrswYMCAXpm0Bw+HFMzyx9KQjax4spjMOPekYfjaqUf1zuD9UNpw/GC/8XpAfnlKf1z6Jm6peA5njC/Pqz8Bw80VC/Dt4g/V9z2LP1Q8l3dfLfqJQHPPGmYZvdq1IoElY0h+8DCkLUstFhlWQIZeD0c+Cqa4c+fOxYIFC3D22WdDFEWk02kwxtDc3AxBEHD66afjmWeewamnntob8/Xg4bDCoUWEEdVkj+aw6wWOJJxzbOCg9h8xoARHDSzqodlYQcAjcn5W+hqO9WWPSBpcdmjdOOVB9+2zRgdRSzvwP2UvQJRzFwkFgKEijwqdODScV3+NPEz07c2r/6EAIUwl/DbxmepiYmnuzlVa9lgtMoesJtiR+xs+ktCtEgVjxozBX/7yF6TTaezevRudnZ0Ih8MYOXIkgsEMv1QPHvorTBlHTbdb/XVdUxSSemMdObCUtzOG7PEd/RsHm0hYkOPwd7b3yFzsIADGVSYxDK24tGg5bmq/JGNfXwYeM3dcGIs25UcoCsG3JgBQ5TEXTQzipXV8oRaJhNODG1EtRNDasRPASEz178S61HCk1dv0sb46NMjlaFOsBJDK+SVNzIRD8S2d7N+Fz1ODkQS3PFHTUY8bFAQU9VqrJEbpbEQqkgSLcQ0ai3cAZi+a0n33WEE4cn/CRxQOyuno8/lw9NFHY8qUKTj22GM9EuPhiEM8KeGJxZvREU3pPnzdwq3e5NIZdDJHciFp8SDlCgIBr2JcIPg+ObLLEuD4IaG8xvNnIDJVeRg5iFaJ2YSTR/kz9OYoCxj9jx9oHFw0uUoIAcaI9fh28Ue4MLxa3/6DkndwY9lrjmtAcmbbVUDAIOS43lRtp93OhOu+7yChDd8rXoJLi5ab5mS4lsJ+AqgZeLVMvMn3/4HWd/+F9KrnXY/D5ENV3NRjMv0BBd2OjjvuOFx++eV59b3iiiswbty4bk3Kg4e+gjVbm7B1XweWrjsAwH5bc+bMMLfkSufe13DRpPxcFJQcXCZhACgJCnmrof9zZjFmH8VzUt1T+QS+X/xuzn1mjMrt+jparIdPyHQeuef2veIPcE/lEzhxKDB9BD9erstivuGauwoUGFlptIYIjw4qp1arUJgkcU/lEzgv9JkxDsk+13sqn8A9lY/jT5VPWbYbWXoJBgrtuLvyCXw1vAJ3Vz6BkWJT9hPJgLsrn3BsCxAuAK4QnBF+BKq4V6upZK2umhHp1S91a34ejkwURGTcdAC5+nvw0J/hE/lTs6QoTg2MTQRs/7bvb84cmt0XMXaAD7XFuW8JE/x7MSy28aCORQjNmHBssn8XTvJv1d8PrxAslo5x/uy6F0IMl5F2BB8kXFq0DGFiuGGO8R2AaLLImPUrohTDN8PLskb4TPLvAQCce7SCkJ9YjpcJZosFUSR8M7wc5TQKSoCqMDWNkf3eOTe0PseRMsM+8pUl7+OMIP88Tw3ysjJuGX9DJIlLi5YiSFIYLLThgtBq13n6YQ3RdlsGzBYZiy4m30R3hwp9aS4eMqJbGpl8EI/HIYq9NrwHDweFlo4EJFnBgMrMVgi5aRdEwl0Uksx0K0tTu+Gst9/m6poiSKRkCALBk29vRV+BQHNXQiYAvj6lCI99EkUkmbnzfxa9B7QDwGhH28BSivrOzPtOHF2FscPKueWCACceXYYd9RE0RwzC8L1inrvjk9Yx6hYGUaQQc4QNl9MoKBi6UGxagPjfGYGtmBHYhiSz3pN8Jt5m/ixHti5HVXAbtkkDsTqVPbqKEqofjxJg8mCKz/ZnugbGUcRoI04ObkWN0AmB/ofOgqjJwqLleHGrcaQf/yDCqTWcFLDmtVFcKNmswBbMCGxHu1KEUwOfI0TTWBx3Fg0OkjRSLHNW96+PTWLlds3SpGWWlKG01QHBkqzn6sGDG3olMH/Hjh3YunWrF37toc8imkgjlsye84IxBpGqibdkZsolw0OrmW4K13dANCFBVpQ+9yCX0YNiBiGoKRFx3oT8o4mOqbUSgwvGW4lhccB6i7nwlFEYUlMMQrjgc96EYkweml1XAvDq3t80aSzsqCyi+F3587ip/AW+BNuuv0g4sTg5sMWy3SxaNn9mYgFqZl7l2dh52rDMt1ViHlfgi30pjYOatheHnCTg9GBmC1hNcbZSMM4vopDDFcX3cp6/RgKLSNLkznL2oznGn9X0DH5a+oa+NwMDk9JIfvgo0p++0rdkKV6Yd79AVpPJY489hn/961+WbRs2bMCZZ56ZcZ9kMomWFl6ELls/Dx76PpiuBdGK5emRSOprxjSNAlEXQgZF6Vv3YgB8ocynEjLJ7h4ZUm5VxzoizAnDrFEikoqAlbtd0uMTdS4gIOqzdz6iX0qA40ItyNRVMLmp/lj2L7DWcwAAIR/BsQN9ENr5jj5iDMBAINrK92goDhC9jxnH+/bi6pL37Kdkem3kFqqgEfy2/AXc03kuhlWH8LXUi2iLzzUdUNaPYac+hktMxp8r/4WsYJktMtmsG9naLgqvxsfJMYgzv6P/7KCRKfeOyqcd+15atAwlJIE7Oi+wbB8pNuPi8ErLNpEokJt2g/o5eVZadiO1/s2M8zrk6GtPJB5ckZXIdHV1oa7O8EcTQpBMJi3b3FBUVIRzzz0XP/nJT3pkkh489Aa6dYtinNQsW1+PzlgKZ00dCp8awsPJDtAVS2HH9s4enasZxwwtw5Z9HQXtI+Rje1VNGdkEq5dOKwE+zTIEAc44xo+WpN+VyBDYQ7cZaJ5PvWE/gAyRxvY5y3vWAOCWqO/MLMEXi91LEgRMd0DzmiWoljj7d+S84k0uozAE5ShmB7aDkAn6PseIXCA+M7AVR1dUAw1AsM1IgU80IsMIxGQ7/E3OVPq59EDaOMf56pBiIrZL3AouQMYZwY1YkjjOdZ9LwrlT+x8jHkAJjeOj5LF8TJLf53Ssj593KYnhzNBG7JGq9LbTgs7aRfLuNUCN4b5TGrbldZxDA4/I9AdkJTLf+c53cPHFFwPgptOzzjoLEyZMwD333OPanxCCYDCIysrKHp+oBw89DZLogtKVBC2pydjHXDuGqZV/9zdH8d4avsCUhf2YeXwthM46xIKDQQnBix/uRH1rz+cg0XDy0aHCiQzRQlwzg4JXGLb3Ouu4Irz9ORcuh2xeIOcDK1FzhBgNU4YH8OkeTmrEjj0IphXQ0ioIlEBhcs55cZsFcX06ri2h8AsEPskmrJZ4eC4/bYYpgd0uowIDSwgCApCU+fvJQ31cN+MmUCVAeYg4yZScwlmpdxEsakJd2UCIxTUoCzDLGIRySxaVDSEsVePzS2gcRZ8/bxPAFoB0HNeUvAMA+HErLwtzfugznBna6Ih60jA7uBn7pfKsw/5nCS+g+EnyaKQhoowWVg7hxMB2nBb8HPVyWY6eCtIbFhU09sGA1oyE0rQrr77sSM6hcAQhK5EpKSlBSUmJ/v7iiy/GqFGjvKrWHo4I0FQUSjyVhcgwncjsa4pAS0Aqm1w0aVnRq8GnJBmtncleJTEAMLxEwhWnVOPxpc1575OPRUagFAMrQtjfZbWkzDgqpBMZgVoHsq/3qtfIUkvnPyYV6UTGLxAoMsHQ6mIoYidS8OdcuAkYBlaEkHDRrUwe4sMpRTtRvvNta0PcIHoZLUyMwC8A3z8lhL8siQMM+H+n1qCpqQ1kj5b0kCDsA2JpICAAQdE5WNn6ZyAHeCLEITtfQfSo03H9KUOw0FSYmQicyAQ6dhnbVEtUMU1aXWYFGgHEjx91bDszxDU1s4JbHG0a8rWwaATLzZqSDReEuVVsoJCDdB9io4d41Ayk8iQynkWmf6Agse9tt92Gq6++urfm4sHDIYNii2pxh1rQDpnTWxCi1opRjRCrvmjs6alaUFbkB4VNNJoHchGZk0YFQQkBASxh0VVF9h2dIeinjTUEvtxy4hR8nn5sGCUBroohhIFQAkIAwiScFnkdx/rqcEPp65hYZl30jh/kUz1emRPh+bv2Zz+5rGAI+zgBO/sYqoZjM52Jfa94CYYJPKdKCYlBjBxwHUVIGq7Eoh3v6RYhgEcEVez70GUvd20Lbd6alyBXg92GVkXzq7o+QMjP/UkAfD38cd7zKRiHXIdiOp4vRxJXT+zbL+CVE/Xw5UWu7GXMKmbdvr9DFacaONASQ0tnAgBDJJ7Grvr8FhE3/GpeBU6fPDBrn+9feDwAxTUHS215CBNHVzl3gjNqaaTYpC94c8eFcfrYsHo5jMIKxw7w4QezikHSMZwZ3IBa2uFYNBkYLj2pDAHRmkfFOj+G2UeH8KNTi6zCVMaAaCtC8Qb8oOQdDBdbcEX1Wr35pnNCuHhiEL7mrWByOsuCl9s1pQRKHFsn+XcDigKfQPCL03w4sfgAwBgnW6ZjXRhYAQCY6XNqWDLB11mX29KUYZFkbftw+fGFuXE0TPDtwZlZIpy6g5FiU1bLzsFCadqRu1MvIXj2j7J38MS+/QLdSvSyf/9+vP766/jiiy/Q3t6OdDrt2o8Qgscee6xbE4vH43jggQewcOFCNDQ0oLKyEqeffjquu+66bmlwNm7ciIcffhgrVqxAW1sbysrKMGbMGHz961/H+eef3605euibkGQFbV1J1JTnSlOf+yZldiO9uGQnRg+rBDOtUFv3dWDrvg789sIaPPvediRS3c/pIagWkWwQo9ziQ2WnkHbS0VVoaHNfAM0GnBNH+DG/i4e//rj12xheqYbvqnoS7ebN9SUyyjc8gwvDbbgw/Ck68F+WcZlqvDAITCZLF1HT5HOiwCKtrv2sEUAKgi2bEd7xNmRELH0HlQAHNM6YK0U/AZg/DCStJHOw2A7Svga0diYqNv0bQjoKjJngKAEQ9vPnPZqnKwYAiJxCrttrJiIDAMLulRnbsuGqkvexDz2b9uLa0rdzd+pPMH9dcj7MeESmP6BgIvP444/j//7v/yBJkv7UZc6hYN6WKXNnLsTjccyfPx8bN27EsGHDcOaZZ2Lbtm14+umnsWTJEjz33HOoqnJ/8nTDU089hVtuuQWEEEyaNAnTp09HU1MTNmzYgNLSUo/IHGGIJyVEE2nUIDORYbr3IPuNyl5HSVYYdtc7TfKMcYvMwYDmYR+lUgKggJvn4dgRFRmJjPZTvOpEEUOrQ4DJU1BdIoAxhqqSIJicsv6emQQx0WYayelaYvEu/F/pY3gqMhMgU2APWq4pC6GhPQHCGMJ+AUVFfjAmgSW6IH/wYMZzJUwGTUUAANI2aw6ZURUEV9HnEW4GSEUemr0MC5JfiqC6LAglrYqFFQmVNAq50UgQF1Zz4eQbXQUA4bqVmBwYlL3P9nfyHq8QDEVDr4x7xMBvlK/I+ejguZb6BQoiMsuWLcMf/vAHVFdX4yc/+Qkee+wxbNu2DY8++ig6Ojqwdu1avPjii4jH4/jZz36GY445pluTuv/++7Fx40acc845uPvuu/UMwbfccgsef/xx3HrrrbjzzjvznvPNN9+MESNG4IEHHsBRRxlhfslkEjt37uzWHD30beR8kGKAlo4r2yCKbaD3P92HNZudOpj1dS45U7JgaE0R9jU5SxgMry3OviNRJajULdGZ9aY8spJiV6tWCJAjUxAGgVoIUmFgihE94yhI6PJWifC8UScGdoBhiioxMToKZkuPIvNkc7IMucspVva17bLMKdDm7nb4WuxpiEIKUICEi3XKmCCzVFp2a/eLVA9Ekht2gCSsRDUYa8CNpa+iWXG6p7LhOJ+7nsbD4YMwcgpomclildMi4xGZ/oCCNDL/+te/QAjB3XffjUsuuUSPaJoxYwbmzp2LG2+8EYsWLcK4cePw5z//GYMGZX8icUMqlcJTTz0Fn8+Hm266yVLm4MYbb0RlZSUWLlyIxsb8RJW///3vQSnFfffdZyExABAIBHDssccWPEcPRxByEB77wt/S4W7xaOzMniXY77P+1CaP4ZFSR1dbc+QPrgrhZxcMzzKSqkVxm7fppvyVCWF8fVLA0jSAtoMqKcfOvAgk+E2bMTD15k1yJMcD1JB0tb+iE0NmfdJlzOSuUf8pcl5sU3Ol2SEqhpg22L4r6yhEnYN7mwJzyFB6zStgiYij3xCxDaPE3hVye+h9iGNm2awwnmvpSEBBRGbdunWorq7G9OnTM/YpLS3FnXfeiVgshvvuu6/gCa1evRqRSATTpk1DdXW1pc3v9+P000+HoihYsmRJzrFWrVqFHTt24MQTT8SYMWNy9vdw5CD3Emn0kGQFHRHjqZ4xBdG2FqQlWY9a0pDptpeUsh9RsEUZVZfxaImx1dafoCDFs4bFEi1hnctEArFGaGcuUmIJlRYg41flr2BE/buuWWSJyWqiuZZcXW9uhTMVrgsy1+cJqs8fEwcJABSDzDBOYJiSW0uU1ZJSAKhbzQIVvPqydZu8c1Xmvh4OPUjPxaQQQQAIAa111glz7R8q7bFje+g9FORa6uzstFgw/H6eHSsSiaC42DCJDxo0CGPGjMHy5Zlro2TC5s08MmDcuHGu7ccffzyef/55bNmSW0X/ySc8e+UJJ5yAeDyO1157DZs2bYLP58OkSZNwzjnnwOfLVqfEw5EKvUQSY4glJDS2x1FWrFowpBRaGw5AhAKZWcMzU2n3BTiX5YISJ5G58Zvj4W/egte/MLYLyQ4IWUoJVJQEEYnGdeNLaZCgM8H7UyWJoG75MeKramgnfkpfAgCUJA4g4bOWGTDAIDdux4TPFyKAb8Ht9iB07NVfzy/6CO9jjh6JpDACH+FiXr8I3HzxQMjRVmhWmJrSkBpWzHRCkw2hbT0jMhXeuzdjW0VsN1KfvpzXOCW0MPehh54BrT2q57L9EgqAwD/jUiAVzUmSfGNn98xxPfQqCiIylZWViMfjlvcAsHfvXhx3nDUVdiqVQltbGwrFgQPcrzxwoHsYqlaIUuuXDdu3c8Geoii46KKLsGvXLkv70UcfjQcffNBL8HckIq+HZ0MjY+/OFACUOSwyB1rck93lcrUfM6wca7YamhBKgKCPQmHA6Boftjel9Ygfs/FmRKWI3a2G2yogUsSIUZsnYOIkBEzXyTBDzYyJ/j2WPj6TeDeo3wE4AUlv+wQEDNVCJ0ZUFoMwq4A5sH+N/vrEwA7skUeBdcT0EcAIaKwZVErAP3wC4mBgiSj8DZsQHHsCmJRGeudqCLWjoTRn16eJHfuytucDKuUOY1aaPJ1cX4bv+LOR7Ckio1opCRUA0Z/7h9uD1iAPvYeCiMyQIUN0cgBw68jChQvxyiuvWIjMpk2bsGvXrm5Vv47F+E0xFHKPOAmHefKtaNQplLSjo4Mn13rooYdQXV2NBx98EFOnTkVdXR1uu+02LF++HP/1X/+F559/vuAIq6qqHKLMPFFTU5iA0ENu+CNJpAnNem1bomkQMYTyMoZQeTESivFZKCkR7fUB+KmMUCp3ZWYAkFj278/XzjoGMyYMxgMvrAMAVFYWI0BkJDv8uOasckTiEkpCBCCCJXfND86uwS+e4aT9v+dPQnlZDBJTUJzk1gGeGI/vUFocQDDI5xEMiCguEgHEIDHrzVhcYRQh/M0F1fAH/GCKhNLSANoEAgnANScHEBpYAiStvzORpSzvLxHegaSuMTIoSor8KP34RT6H40+EkPJBWfcaipr3onjcOChSDK0b3wb2fga5M//MxB6+vKioqUTspAvQ+cmrxkYq6C7NQlBeXgRCBBCfHyxNQQQfslHdsrIQwt49us+jICJz8skn47PPPsOWLVtwzDHH4IILLsB9992HRx99FI2NjZgyZQqamprw9NNPgzGGc889t7fmnRc0X78kSbj33nsxceJEAMDYsWPx97//HWeffTY2btyIpUuXYtasWQWN3dIScTytF4qamhI0NXU/gZoHd3TFUmhvi6FRSIP4OSFOpmRQCvh46la0t8cgRBNokyXE5Qja26M4UC9CURh8SKOrKwEfkdHVkd8T2epd2Z/8I10JBExZ6TqbmyH6/EA0ARFJgDF0dSkApWCqvWVUlYBEzKTdibSjHRK6uuJgqotraBlFY5eiHiOGRJyfXyKZRjTK+8hZpHBSMolUSgEUCR2CBFkNNy/d+ALaS64FkkmUm/orsoLMjimCaMy4Dp2dUSQjcYhxLp7t7IgCSd4ueSTmS4Ek/AgglbtjFrR3xIGSEfDP/i5SHz56UGN1dCQAQkFEGSydAITsKRM6OmKI9sA92ntg7V0UZDc799xzMWPGDN1FU1tbi9///vcQRRGvv/46brnlFvz9739HR0cHJk+ejOuuu67gCWkWF7MLywzNYlNUVJT3WEcffbROYjQEAgF85StfAQCsXNm95FMe+i6IlIDSYeTT2NsYQaMpx4omz4gm0vr75o4E9jWrFggtEifpjGApFNdePB4EgF80fm6+OE8IR8BQVepHoH4dSvYtA1Els788pxiXTi1GwBTtJKS69Iij0iDBD+cUY96xflx/egg/P6eMe5JMqfE1I6OUkXqAi3AJT87G2vaDqaHUAFBR7EchtWYm+fegvGG1/r44KKIkbC0vLe1Ykfd4/QFysAx04NjDPY0+i070gOWaEOPfwUJ1J9GSamNsIYtO0tN39wsUZJEZM2YMHnnkEcu2r3zlK5g8eTIWLlyIffv2IRgMYtq0aTjrrLNA88nwZYMWsl1fX+/a3tDQYOmXDYMHDwaAjBqYoUOHAgBaW1td2z30X/AAHPNdiEG2vXekSGFGDhVNYiL3QBqJiuIAFJUwGAfj4ciEEPhEiqLtXNgaGD0HiTRQU0TRmQSKgrabLNP+w1BTTMFkBRWpFjAahiSUWMYnoLwWVFa3F58Xk2WkVz9raRHadoEotoUoh0C3osGI+BEIEPQJ0GxKrKMeSouzCnV/hlQ0AIGyAVDq8y9fcKiQGDgRwfp1xgbiXkG8N5ESiwDpYO+vKomxWMCN7zSpGAxaNgjyrtXOXe0jhUrBYu2AP6gTIxIqMWWa9tAf0a0SBXYMHTq0x4pJjh3Ln242bdrk2r5xI68jkk+yPU2309npXhytvb0dQGY9jocjF5rE135fl/UU/bzhYN2HZx0X5gUSZfdM145N2oSYAkAAGPDDU8sQIkk1/NcoAaDlwyjdsAAA0HritUZ5AcbH/r+vhNG6KwDUuc+PgkFhQOiAM+RYWv0SglW231nBCcKME0xveKvAffs+mBhA7pi1w4NUzbEWIiOMmg75EFvEqosIkKP4dTbQquFGfgDTd088+iRIW5YCAIgYhO+40/IiMiDU9KPrm5+bh8LRa5LsxsZG3HrrrQXvN3XqVBQXF2PVqlVoaWmxtKVSKbz33nuglGLOnDk5x5ozZw4EQcDmzZt10mLGihX8R50p1NvD4YMkK0hLCuRMqWjzAs+JwuQ0X/7ltG6lYQB+/+IevLSmE5Ks6NxFt8goABjDq58dxF0YQElQrSidTkBLvMa3MQS3v4fy1Q+DSYaf3iArBmGpKRFQEmA2IkMc+WQIzNeKlwkoC1GUBbLcsBkDUSTQtLsrV0haz1+IF/DkyhRkTHpzhEAJlgGyMxmiVFRzGGZjh03kPWxihn49i6YTrgIAKL4QCv3sA3P+E6SMR6xWnHYZ/DO+abiVRMM66TtmFnxTLgIAMLlQDU7+likSCOfu5OGwo8eJTF1dHW666SacddZZePzxxwve3+/347LLLkM6ncbvfvc7SJJxk7jjjjvQ2tqK8847D7W1tfr2O++8E+eeey6eeOIJy1hVVVW46KKLEIvFcMsttyCVMr7wCxYswPLly1FRUYGzzz67G2fqoTexrymCuuYI6jOEO+cLloxAaeXmCKHrAJimeVHvYx9uS6AjmoJmnTHubwxSN6IiHMdX0/UL0SZU0Bi+d1otrj6F3+DD29/jnUwp9ouDPpSG/aoZ3XSz1dL+M4bSkB8BkUDoakB45wd6F+uSobqyGOCzl742gYKHYDPB3ThLlewZi7OBgUHavQYseuSa7dNVY8AUp2A0yyXvNki4rKD+mrBd31/0QRxzck9OyRWECOg6+lx0HnNB4TtTwfgREgLtfwCBUGyur0dAfEbeJzfC5DvhAtCaUda5UQoaLObHEUS+H+HXSRg+ifcpM1J/CAOOLvwcPBxy5OVa2rNnD9566y1dA3Pcccdh3rx5lmRy+/fvx7333otXX30VsiyDMdbt9P/XXnstli5dikWLFmHevHkYP348tm3bhi1btmDIkCH41a9+Zenf1NSEnTt3uuat+cUvfoG1a9fi1VdfxerVqzF+/HjU1dVh48aNCAQC+OMf/5iXcNjDoQVTXSNyN107OiFRFEMrozBTzQFj3HufX4ejBpfiq7NH69sVMKyvO7gikNohh9WWYF8rQ0VYxKQRJehojIBBMe69pqdDn0jgE0REImn4WveAFR1rlJdWywH4k20IlFYi8dFzjuOVhfigAZGAMhkAQQhxZKIjNeVBMCmFrgwWGZpo7+6pg8kypO2fdHv/vgRlyCTQurXOBkpBXLSAJNLU85PwBVGIn6a8LARLCj9CQUp7tjI2C5SA2KqKgwByyQB0yxJn8rUS3Q2k/rPldCElNaC1o+Eba404FcefDRZphTDwGMh1XKJASmshDBoLgICWqg/BhJqshoA4+DjIe9aClg2A3OGu0fTQN5GTyDzyyCO48847IcvWp9O//vWveOyxxzBw4EA8+OCDuO+++5BOc9P95MmTcc011+C0007r1qRCoRCeeOIJPPDAA1i4cCHeeustVFRU4Fvf+hZ+/OMf64n48kFpaSmeeeYZ3H///Vi0aBHee+89lJSUYO7cubj22mu9Wkt9FLqmtdtPtjaLhr3V1NQeSeHTLc24ePZRlg4fbD34TK5+Lc+Lrn1hGicxHcvpPqOfvYjiWCsQPwAMPxWEMX7f3bYcye3L4J/1bdfjXTCxGAE5huNqCYg6rrR1WeYJMgWpVc/D35mhjtDBWKWkgyeCfQXKiGlQ2uogxuxh4wS96KG3HarA41D77b2HIn8sQ7qMR4y2QvNzARRC7VGQOhtAQyXQCQxxITKCiMCJlwCApeSFb8QJ3N1ECGjtKCiN2+EbdxZoxSAbUSJeKaUjBFmJzKpVq3DHHXeAMYZQKISRI0cikUhg79692LNnD2666SaMHTsWDz74IACeZ+aaa67BiSeeeNATC4fDuOGGG3DDDTfk7Hv77bfj9ttvz9heXFyMG2+8ETfeeONBz8tD96CoC3jeN7YsJIYpiutTsKIwUFNaXKZbc3hlJUVhUGQZkJ01lMyH1V70xD2uKEBUTqUVSlQMrYum17G4b/h2ElPdMe37gGH8JQHAOnhyvNRH/4IdNB2D9O6/MG3UmVDIQIT2LENqT/b8NqnVL4FlIjEHCXnfutyd+gsIRXTs+Shb85hju1m7kQv+GZci9fHT3Z5DYf1tYfc9QGJSRQPgjxppDdznxAkM66ZFRjjqRAgjT4AQLgXiMDQy9t880f+TYQ4U4vATIA4apwuGSbZrKPLklyTYM8lOPRw6ZCUyTz75JBhjOOecc3Drrbfq9ZR27NiBH/3oR1iyZAmWLl2Kqqoq3H777Zg926tL4cEdexq6UBLyo6osmLszgExMhkkpKG37IdSMdB6jsQsjBpSoexPUt8YwuMSgI4wxHKjbj6C/EURxFoNLqknmpLpNoJEmAPnO1R1BERhRqS0mDExOwcfSEClFwE/1dYVJxtMk04mOAYFqZ5TdyuSLNACpGEq2vonoMeci0LgJuaTSSuveHD26D+mLD3J36i8g1MXCwTMrC4OOhdK6FywRyUkK7ToXEiyB7/gzkVr9Uu4pkMLKaBJBgH/615Fa+Zw2WcdCrgh+YOSJCJRWIL3mVZdRrGCCNdO1ebz4mLORIn6LRabghwFKeZSfv4Q/9KgEhliijaC+pkaGX0IQOPVKsGi7Yb3RLELBIrBUDISKTjJkgjhkHDDzUvjGzoE0cgpoSV8QbHvIB1kp/tq1axEIBHDLLbdYikIeddRR+MUvfgHGGGRZxl//+lePxHjICanApCyuN8EstmDFZkXR3VP6rgxMAdJpWc9gax+aMSDx1r2o2vTvgzY7Tx0qIBz0AcQgIT4BqCiiKLMkijO5bxTFpOPhieoGVIRRW547AqSsnJM4oqRR/EXuRenLCmHkVJCi/N3THMT18ocCfhBfEP4pFyJw8vw8Dm613tDKYaDluXNi8SmYbte+IGjtaNCBY3RLgrM/AS0bYOznapEhYEMmQKgdDVI+CEKOyCb7CJpllA0aj9IRYyGVDjYMJRksQFmPYT5H7bVOIM3jWZPkEVX7Igw5ztSXWPsKYuZrpfbzT5gL4g/BN3IqhKrhWfp66EvISmSam5sxfPhwlJY6n15POOEEADyHzOTJk3tlch6OHBRKClgW4wPLNJhpH05iFNWpZO3PGEOWAtO2AQ8GplBqI74bybf/itQKU/I5sw6FyZB2rzFPVh8j1/MtO4gIo74OWj2yx8YSx8xEYPZ3C9wrQxg5IWqVT+reboctOowUV+XvMjIRg+DpV8M/9SL4T/gPBE79f5n7m7UldquG0REgQGDa1+AbmyOthW13ZyQVVbtYrwdRrRvi0TPhO/4MkHC545z4FAVYliWTdSfjZIjpr8aiCKBVutbmxV/ar/XhSw1wxhlnYOzYsdi37+CLo/YErrjiCowdOxaffNL/BPpZf0GpVMqVxABASQl/+qup8cxvHnoL+RMJxtScMaZ9LOHUaqZfhSl4blUHGtpyV0U+WBD9wKa/6oSU5t2myZstMgzp9YtMbQypz16H0lGPnNfDJZ/JkQBaNQK+8Wf13IDdqWicSV+iLZZ56k/MrhgSruAhvzRLCQnHsfQ3AIirVswAdWpL3M49q8XGCrPuxTfta6DVI4zpAGBEdZmaLCVu8/JPvwT+2d91zoeYXlgS19mIpL7JTjD565/cfDtO+/plOO0bl+Nnv79NPY6TyBFCsLkxgnn/2oA555+PWbOm4dNPnckhC8Wnn67CP//59x4Zy0NuHLTcvnBVuocvI7pj23A3vGSPROqIpPRuTAH2t0QhywrEzn0AA1qjMtbtS+K1TzKkunU5UrY8LNlAAMgyM+lQGJgb2bBYZKwuLyYlIe9dh9Snr+Seb+QIKsToD+suBWHQWBAhvyrkAEBCZRDHnZG5nWayTGSGz+8HddnH59NykeRJZkx9hAGj+VwykBFaNRzWxdv2mhKAipnvwbqlwtBpuffT/lJkMlDQEVMgDRiH6AjDYkPLjXwrVCd0FCQV5wRGt5A450V8QVAx4EKs7Jl3rVFGwbP/C+LYUwFowl3N+kIApva3kZtPN2xES0cHCKGWa0WCxSCBMN6PGTnJegpr1qzGI4/8A2vW5JFt2MNBI2f4dVdXV9aiirnap0+f3r2ZefCQEc4bMgOPjIqnZIhqcUbNAMIYw+b9cSTTMoZX8yRabu6pj9YfwJDqIi1ICC1R3mdAuR/7WroTis3zvnDNi2qRMbt/NNJiTm3gKACl9tGVwZmPJm37uBtz7JsQR02HvOczsHiHmnMnf+IhHncahNrRkDa962wUfMjbDWRCMOhHQGRIqO9p9QgozbsRCohgsgRCBQtJpYOOhXLgC+P94HFQ9m+yLNy0apjV/UFFy/eDFFUCZUOBHTx8Xhh0LJTG7Wqjau2gFMRF90HKBurWGHHkCTwE3+fPcN6atUIBGEDC5bwekXk8SpEeeTKYIpo36udDBROJEEUIogDXpNxagigqAEyCMHIq5O0fW8fUvuQ6EQM/z6JyCOGyDDoXYuxn0jMNGzwIe/cfwDsffYzLrv6J9ayLqyBJEt776CMUF5cgEPA7ssl76B/ISWS2bt2Kb3/bPWcFISRne6aaSR489DQUy52Tu5mISgyeW82Tdv3odL/a6sQHn+0HAJxk04HSwtY8HScNU2/uigyiFWc0W1y0StWmbQ6CpbVJKVR8cv+Xpxgvk0GrhkPet95K9DQEioBk1Ln5tP8HEsyW4DKLpQCAOO4MiIOPQ+Ltv9p2sz7l+6d9VR+Lu3dUV0+oDCzeAf/Ec8EmzkVy0Z95/0nzgONOtVgEaPkQXdMRuvBXkOu3WbVTALdMARDHnMKFvWtdJm37zghDx0Mcf44a5UQhjj4J4ojJIEIAxG+NxFNC5WpEEAUvoUEQOO0qpFa/DKVhq/PqWaxCzmSAjFBuASIUhLhHHhJCwagAKBS+Y06B75hZ/Hqn4yofIfr4gGx6rZEVmzZGzzVjvNVw9uxT8Mizz2Pxh0tx2dU/dcxl+fKP0N7ejgsuuBgrVx45DwJfNuQkMhmFlXngYPb1cIShG18F969PhhWIATKzGaOZSzIYIy9dThwj7scWaXDB7tMRQhMGCh0o8h2jzkE2BDtuB7a4k2yPsVKhdWSOECiyIYxVJAAE/lOuQGqpWvYk2wdoz59iadP/A//s7yH14SO29gzWGofbxeRO0iwMsgT/jG9Aad3PCY7Fc++m8yDQvfuEgpbVwjdxHtLr3jD6DD4eEcWPqmEjbS4oLUkcwGRb4kEt7FgjKEzRj0vLB3OS4+eFcttpLUo0Yse4VYZk0uwQOM9JtzAZLh2mEhlGMkQpEs2dZlzrwIxvQUl0qeRLcZJN7TprPIbYrqdWj8N2jWuqqzH5+HFYs2Ejtu/YhqOPthZBXbRoIQDg3HPPy0pk9u7dg6eeehyrVq1AS0sT/P4Axo49Dl/72jcwZ85plr6zZk3TXz/yyD/wyCP/0N9ffPHFrjnP1q1bh/vvvx+ffvopkskkxowZg+9///sZy+dIkoRnn30WL7/8MrZt24Z0Oo2hQ4fi7LPPxpVXXplR29rQ0IB77rkHS5YsQVdXF4YMGYKLLroIV155ZcZz7w/ISmS++OKLbM0ePPQKdtVr1coZFJcFKxpPI9kWg6wwlIT9KAoKkJt3Q1FKIQimsotqxE9bZ8IxRj5E5oelb+PHrd8u2CJzfRlfiFoxBgQKwBRQKNqsHP2VaFthE/sSgCkyxBGTobQfgDCYF3UVKobo7b7xZyP96cuWfUjZQDUaJkc2Q3UhpEUVIMESsISRYj9jwjQqwElCwBdkzV1CABIogVA5FEgn1ZIC5mPa56FZQphOsIQBo2GhJZQiXXssCE3zPChuJ0MFoGoE4iUjEG79XK0ZZFiLeGJIrilhBPCPORlKtJWTjU4FWqI4RgzXjDh2FtKxdrAuXmpBHDoBSJvOG+CmSmadC+dmAqgogKQk/aSFwcdB3g8II6cAgggCBkapbmQhoVIIRRUm15JVd0SIACL4LBl8NcLGtB9oBt50zqmzsWbDRixa9IaFyHR1dWHZso8waNAQTJw42X1nAO+//w5uvvl/kEqlEAqFMGzYCHR2dmD16hVYvXoF5s//Dn7wg//S+0+YMAkNDfVobGxAbe0ADBgwED4fJ4cjR450jP/BBx/gtttuU8cehrq6Oqxfvx4/+tGPcNddd+H888+39E8mk/jBD36ApUuX6mOGw2Fs3boVf/vb3/Daa6/hsccew9ChQy377d69G5dddhmam5vh8/lwzDHHoLOzE3fddRfWrl3brw0Phyi3tgcPhUQgZd8jlpQQTUiIp2TEEmkeZs14BeugesPQtDEKgLQpZ4wW1VTIT9ZN5JkPCACqcGtMbYmPa2VcbhbS5+8Zb/rxzaRQBM/5MXyTTDdpU0ZVWjoAJFiMwMmXgwSKLIsaAAiVQ0FC1qfOwPSvgQhiVteRtqBr0TSOq03tlhd1L0GERZiqzocWVWnDmvYnRiRSwOzmsg1sDhd2iagx2ohLm8mPQgmCUy5ExbgTEZz9PdBQmWG1MOlM9ErOgmCdjt7PYFtCSQ0CJ33DOFywGICtCCUVQMq4UJZWDTOilQhB0C9iQGXYMJgEihA45QrQUCmEmqNgseboh1bJF0x/tctaPUK3IpmvG60ewccjotrkvFannnwSAn4/3n57kcX9/O67byGVSmHu3HkZra47dmzDzTf/LxRFwU9/+nO88cZ7eOyxp/Hiiwtx330PoqqqGk8++RiWL1+q7/PAA//E+edfCAA4//wL8cAD/8TTTz+Np59+Gtdcc43jGLfddht+8IMfYNmyZXjhhRewfPlyfO973wPACyUrNrHRn//8Zz0R7TPPPINFixbhxRdfxDvvvIPJkydj3759+NnPfmbZhzGGn//852hubsb06dPx/vvv44UXXsDbb7+Nf/7zn1i+fDk+++wz12vQH+ARGQ99DjmypfCHOFMos74XM4Kv9Vc2I4hWhLIQvpCLyNTQTvy+/FmUEadmQ6tYzVJxxN+6D0pn9mKCSc110g/ROfFSpI8+Nf8dCIFQO8p4r34oXeO/BmHAaMAckeJYoAggBlzHJMiRij5buHHG/YjF5aJnmjUTANWyYc7XEjh5PgKzv+c+H72/8R4gEI+ewd8z9TxhbrfOyTmW2fRDMv/Tw5GpaZv2ktjGUbdR+zYKoWo4gmdeC3HYRNNhqXt5Ao2oaC4yt2y95utqCuG2RBvp7IgTF3PWX1pWq/fSEA6FMOvEaWhqasTq1UZgyptvvg4AOOecec65qnj44QeRSiVx9dXX4mtf+yZE0bCKTZ48BT//+S8BAP/+95MZx8iFk08+GT/84Q/1IsyUUlx//fWorq5GfX09Nm/erPeNRCJ4+mle4uI3v/mNJYfbgAEDcPfdd0MURaxZs8aSD2bFihVYu3YtfD4f7rzzTlRXV+tts2bNwo9+9COk0/23NppHZDz0PeSwyGjOIwePgcmaw9RMv1rEkArt4SYp5c9kCAFOHpU5/PeU4GaU0gROCOy2bPdF6kGirVDaD0DasQJIRpCyuUMcSMXynldfgf/k+fCdcCGYPwxWVJ17Bw0EVuKg1Z7yl8Jgq1pH89+sA6ovs/TNFiqdSSOjtvlnfxe+yV8xWQxsC6xtISaCH8QXMIiBTSxrFayq/3whfZNhUXEhFnonG6nSrUPEOo6NB9rPzUG2HH1s2hkq8HMMl1lIR+Zrbzt3aljGoJFPTTjtNmfHWHYSRED8YZfzJDjnNB42rmli6ur2Yf36tRg3bjyGDx/heoR0Oo1ly5aCUoqvfOUi1z4zZpwCURSxfv1aSFL38jh94xvfcGzz+/16QeM9e/bo21evXo1YLIba2lrMnTvXsd/gwYNx1lk859KHH36ob1+yZAkA4JxzzsGAAc4K6N/85jd1ItUfkVPs68FDT8DNAtIeSUIUKIpDuX9ASmcTGuICqgISUpKiu45iSQnROEFbawzwl6t+Xq2qrdP6kqFWZFZQJqleewYKBQSAZDKz6/dN29jF23hiO4tc1y7M7OcQR5/ENSEASH0zaLwtY19aPRJK8y5jAxEAMIhjToa061OIY2dDWr8IzBcEQAEpDvgC+mJkgRsZyWRNcExEADKUy6CqDkcYOh5KZxNYZ4OxD6GgpTUg5YO5gNay2KrHo1Rf4PX9mKEVcZkwdK0HtMgh82kSEMLcz1dtZ9r10DdqBMlsHTITKRP5MQZyuW4E4lEnQarbCABgDjJkFexqVhNCuFXBesr2fU3WK+1a8gum/yWZSKXL52utQ2VnMgTTJk1GVVUVPvjgPfzsZ7/E4sVcx3buuVb9iRl79+5BKpWEKIr47//+acZ+hBCkUkl0dnagsrIqY79MGDHCnUhVVfGxYjHj4Wbnzp0AgFGjRkEQ3EXZY8aMwZtvvoldu3Y59hs9erTrPsXFxRgwYECfyTJcKDwi4+GwwY3IaAJde4kCJRFBOiYgLaV4YDVj3OLC+DjqXro4WKurBADJtMki0w0NiqDeg79X/AEm+fnT0e/bL0KzWnhSKx3QrWq/fQi0ajiUlj2WbSRcznOy7HJP7EUHHQvVfILq8hCUhIhMz6XCgNEWIkMEEYzJ8B0zi4cHB4ohDp8Iv6J++LpFwfSErh/YJr7lHW1/DQROuxrJ9x8ErRgEQkU154vRL3D2dfxzFAOAnIJ/8lfAFAmJhX/iIwZLwLREb4C6ALuEhYOoifyYMU9FUMmNbW66wYSAhEpAUjEenm+ytgQDImqDApCKGDsEwro7hZbWQEl0wUgGR/UxtWtHoApi7cTFfL0s1hz1NaXwjTsdvnGng6XisOR4AUAoAQ0WQYm16/sxwY/qMgofhZro0WQVsn82hECoHApJ+06oCeuYWTvjRmAzWd3cjqG+Fv1+nHXWXDzzzFP44IN3sWjRQoiiiDPPdI8KAoBIhIvAJUnC+vVrM/bTkEx2J9cUEAqFXLdTrY6V6Z6lkRqza8gOjQBFo4arW9tPa3NDdXW1R2Q8eCgUGUsmmawppq2mcgNcxKsoqhLGJaxZYcYN4I2Nxg9azklknO38fsl0EgMAtUInOpQwLi1ahqmBXfyYIJhV24GMK3lfh4uOwz/1YkAQQWtGIv35B87swXpIMAEVRR6JkvkALsczWQwIAWGAIFB1QTcWZQdJdLXIqNtsCy4AIBCC/5QrDIGwm9uCUE6eZDivhdnFoy2wWr4Ti7VD7WOL5slkRTAKIwoZzolCoDwfDAhB4LT/x4mSdp46oTMRBjdLC3ER+Jrfu8GsJSIAY8Q2Pe1aW61BAjUntTO2W1xX2vUQ/c5rY3GbOSbl+j3NeTJUxNy55+GZZ57Cgw/ej4aGesyefSrKysoz7hIKcXF0RUUlXn11ceaxDyHCYT6n5ubMWby1pH5FRYbQXNsvW8K/bGP2dXgaGQ+HDnkYLFiOd7oGRlGtL+p7RVawrzECRWHY1xRBPCGhMyGhuTOFtphhrt/dkt21Q12IjCPKFECaCZga2KmTGACYfZQPlyivZx2/b0P9gNQkbABA/AEQQkErhsB3gmGG98/6DoRhk0CDZaZFUXAsMuKxJvGvyyJtzY1iLPiEmCwuukUG8J9yBcQxpwCEwj/1YggjpkAYNR2BWd+xjmE6VnjMdBAxAKF8EIgmEHYTD1NTpIyNEDj0I2ZrEQBH1IuZ1GiRTOp5iGNnwz/jW6ZtZsuI24Jv/KWlNWr0kZk4mc/HTKpMZM1OMO3n70YeiI1cERdCaXSGlmOGWOZitNuHdzt3+5iOm4abFsjR18FQATAcc8yxOOqo0WhoqAeQ3a0EAMOGDYfP50N7exva2lqz9rWjt0r3jBo1CgCwY8cOyG6JIsGT2ALWUG9tv+3bt7vuE4lE0NDQ0IMzPbTwiIyHwwpJVnCgJYpk2vhRuuWx4w1AKiVxr0OsTbfckHQMX9Sn8cKSnVi+sR5/evozPPvRXvxtaQL/fHsfRFMimPe3ZDf/ulWYJgR6hmB93hAcFiV/f7dvaouypUKzYTHRQ40B0JJq+I6do7pXiLFg2xYRcfgk0/jOxTSjDkKzNJgXZxAIVcPhGzsLIAS0uBL+CefAd+wc0PJB4ATIEIySokqAUBRPOJVHHNkWTnN4L99MM7YbnbRtqtDVvlibrEvWbYb1xTd2DmjlUKPNbPmg1LYvjPM3j02pWm4BsERQmSxNRDuuTgyNMYl2fm6kQyVGRDBbewgodbHImK8L0SK7nJ8nsbu2tP3cyJSFQFoJjnauxDZnYvruEWL7VPTvGXDppVdg6tQTMWPGyZg5c5ZjnmYEg0GcdNJMMMbw7LNPZ+1rRyDACXN33U2ZMHXqVITDYTQ1NWHRokWO9gMHDuCdd94BAMyePVvfrr1evHgxGhsbHfs9++yzXtSSBw+54JrQVv2bSMlojyRNG5nF+mI0MESTaQAMyWgH2rsSPBdZvA1tMe7POdDCfcGtXYbEVizgW+5qkXHppzCCy4qXW7b5+vuvyc0tZFkgibO/qc0cBmsawDqWpcnkWrE/TRNiqwptWsTNVotMVgVCEDjzGgS/8t98kRPMxMg4L83VRATRRMScC6rZSgUQ0GCx+8KsH15zGWnXxkRKTAsrCECDxTxXDkzbtX0tFiKoBMMHUAFC+SDj0IIPBvFRyUtxpfu1Ycb5Oz8fUz+VKGnkqLwkgJrysJp0UIPp9yL4IIfKQUtq9GECoolAmf+aCJqFUBaV6+cJACRUAlpuirIJFPHT9AUtcyeltRCquOicVgwy6jERgFYOASnlYdnz5n0Ff/7z/fjTn/4Cvz93IdKrrvoBAoEAnnzyMTz88IMW4S0AdHS047XXXsKjjz5k2T54MBeNb9iwrtvRTG4oLi7GZZddBgD4wx/+gLVr1+ptDQ0N+OlPf4p0Oo0TTjgBJ510kt520kknYcKECUin07jhhhssLqZly5bhvvvu86KWPHg4GDjVMO69GAjPycKAR5Z2oC3Whl9dPhWAEY2USDrNrUIBBMNHnPsTwnBUarNjmx2B9h0gSn8VyMCFVKivtVwnjoVbjcixmPqNPsLQ8QAB/CdfDialAFMGXR2U8g+cmiNY1DE0ImNxb6jz1CJ5zBWPrY/h3NVBARAGQkQws3hJ7eufdB6U9gMgwRJe60c/Bh/Xf9I3QYoq1IrZpmvgCxiDuFgStNpFWr0ji7XK7lLxBY1rzGzXWLcyaPszY5EX/aoI10ZA9PZAZhKqjaW9VkssmK+fHVQQQRmD76L/hbR1qaOduwpFQNQKc6ofqX482/eKGudnXEOrLgdUsGQ01i0xvoAlVQG3BAmm8zZZcYTuL9BHHz0GN998O37721/j4YcfxBNPPIphw0bA7/ejvb0N9fUHwBhziIZPPHEGSkpKsW7dZ/ja176CESOGQxRFzJ49G1dffXW35wMA1113HTZt2oRly5bhG9/4BkaNGoVQKIStW7fqpQr+9Kc/WfYhhOCOO+7A5ZdfjhUrVuC0007DmDFjEIlEsHv3bpxxxhk5C0D3ZXSLyCxbtgwffPAB9uzZg1gsljG1MSEEjz322EFN0MORggwmGe1+monNmP5qXzPCmEn3wtQ23mh2UQFACYlDVIIopzG0K5mLCVbQCNqUIviJk4hQAkxPWG/cx4oHHP18Xc5tfQXimJPBEhHIB77IWL/JPWkboFVHtrbbrBuAzS0CkBDXz9CichB/GEqkFaSoAkxKwXfMbOgkyVxbx/w90PKMWPQb6gLIzDoS/T/mDZzsgBc2JIEwmJQ0nYNqtfCHIAwaq/anVjMgIRBqjzKN7XLOxOW42rWQtX1Mlh6Lq8wFZtJhm4tOFpl9f2IhX8552oiOaT/LLCxjuJyWOiYtqoBQO9po0K4/se3gai0zwx6i7Tigcy6u/XoXp5wyG08++Ryee+7f+Pjjpdi/fx/S6TTKyspx4okzMXv2HMyZc7pln6KiYtx993146KG/YdOmjfjss8+gKAqGDBmS4Sj5IxAI4B//+AeeeeYZvPzyy9i6dStkWcawYcP0WktlZWWO/Y466ii88MILeq2lrVu3YujQobj++utx5ZVX6tmE+yMKIjKJRALXXXednmgnV22G3hI8eeibkBUFkbiEsiLDZCvJChIpd1GaAf49amyLIRz0uRYRSCQlAD7N6WTbk0cwAcDmve2W/W6peI4vKOXA/7Z9DR3MSWaGC824oWwhnonOwLa0M1kUdSniMi+81rGtp0FCpWDxzh4Zyzd2DpjEa07Je9dlOKB5YYJqnidWF5K5O6VgMoFR+JBZ1xYtPFlLnOYLInj2D3k1a52VquTBYVUAfxLXCh5ajm2yyFjmrlkviIkDaEQCtnFcFlkqGCHDBLxopSI7I5Ds+5nf68OrizSlnHjYrTBajSb7ou0gElqOGdOxXe+rpmNrLikzwSAEYMx5T9bdgjYhr+W7oM2JwqhGbc62q+7vCJs2jwfnNbATLOI87+7ivvsehNS4I8O1cseCBa9mbKutHYAf/vDH+OEPf5z3eMceOw5/+tNfAAA1NSWO9nfffTfr/rfffrtrgUkAEEUR8+fPx/z58/OeDwAMHDgw45iPP95/s4oXRGTuvfdeLFmyBOFwGF//+tcxadIkVFZW6vHuHr7cSKYU1DVFUBQsh6j6c5ra4zmJDGO8HlJaUhBNSLr1xUyU2yIJMH9Idy1pUBQGCAEoSi6yBJTQBGpIFy4Mr8afO8/F90vewfrUMFxSxM2po8RG7JGseRaOEhtwTmxNvpegZ5EtzX4h0MJ1QeAbfzbEo05E8oOH3PvxAyNwxtWwZIx16Cmgb6fFFSBUhNLVDKuiSLXkiCLfQXc58ARoQmkNWDLK86DoZMh8DKuriZZUQelqBg2XqYU2VcJDKC8oCALiC3BtiKJAbt8PXSxqsv6R4koQOa2Xi7DobUxaFkIE8ErMJheM2VqiWzBU0hlrNfqYF3kQkGAxSCKiNmeL/oERWSX4IJQPhNLRAJ0suD082sgZsVhHjHmTogoQfwgs3slJB1NQURJEQDQ6624YbRPT3pgJldolXKa6owho+UBO+polENEP4g9BiZvFo87vEA0VG2TFfjkYg69iIEiHm6uW64QEvRyBhy87CiIyb7zxBiileOihhzBlypTempOHfgx7SheNkGTSvQBET36nKNoTo9MmYx3XaJFkBYyKeVdunV+0FJVCFOU0hrG+eoz11ZtmQyDYrC+XFS1DjeKi7TgUOIgn0sRRpyG4430AgH/GpdCEtUQQgbDT7AzAokkh/mLVGsLdM3aLjFFTCCD+MF+8EhFLH2HY8db9iElwy8ALEfrDXDvjdq5m9wghIIFioKsZtLgKSqzN2k+LShJEgwiAW4FA4tYFmQq8QGVXszE/StVsv8SYpyACimT9IhpfT3083XpjnbyFBBo6D7tLzPaa8MrTSqQZ4tjZ1kKJPj+QSrqQSps1w+5OIqbzDhRxK59K1oJ+wTaOi/jaIuI2aU9Ev5pYECC+IAhjIES1IAbCQKIzw0mq7wLFxnFdfr40WATSleG3R4gqkPbgocCopaamJgwfPtwjMR4ygjHmIBW5OQaz7Guul2TtouphzERG4sTDXnpAq8dkP4om5nULs2YgEIiVyBQLhzEk8SAsMtKAcRBHTQPALRmwhL8SiMec4tzJvBhrlgltIXbTK1gS2vFmWj4QAOCbehGIoLkY+WJIKIWeY8SyvmmEwOYSocY2Yt5u7Gj6x+CYn3WyLmTJzdJk7mf/jhBYb5lux3MJ2842Jxd3DqEifGPngIimqBpirUvkHMc8nnYOLq4kx7Htn6nLMXKeTrZj2MY2W7dcj5vXAT14sKAgi0xNTY0eH+/BgwZFYWpVaV42ICUpEEUKqpqiM5UF0AmL+l5WGEQlxckK5SZuhTEkU6qmQpZhpTFAOpWEHFCwdJeVgNxT+Tg2pIbajkjgU8W8/1P+kut8vl30oWVbCImc599ryIPIkOIqsIhbtk4CccQUiCOnqouEWScC+MacAlo9Eqllpqq9guFa4hYKm0vF/tTv0M0Q0FApgmddC/jCgJSEmeQYESk2Ua3ZFWJZw9y0LbB21OaguXqcl8E+qLODRYxLrblQLBYY+1j2SVvbsuah0UiLnTho5NGek8fUn0uK7J+F/Tj2abpcA2KMZ1y/3GQpu3bF2kZLqqAcABAssozvOkYv8BdPp/nlQEGPfHPnzsX27dv7dQZADz2Ppo446poj/A1jaO6Io73LSASVMcGd1q7+VRiDEG2EL1IPmEoSHGiNAmAgqajWUd+XRBqN7TaM91vrhgwU2uFDZi3NzOA2VAh9qPq0mE/YqPuNurTYlMEWBNQfNi2QMBZ/80iWMFdTAjnXhdBMAPSd+Hvq0xdkPbcM9UEvumjW1OkLtDP0mxZXwlg4+Vg0VKL303OzOMZwuUT6P9NCrZ0XISCiHzRcCqrmlaHFFYA5uzCgthfr49BwKa/NZDkQh1BaY9pkbCeiz3CJEIAUlXMXm30Manz2vLo0QIoqQYoqQMOlICG7eDQLsQHhVjnLJvWCCCKEslrQ0lr9WgAwXWfT8ILPOC/T3Bxjm44hHjMH/pnzIVSPhKXAZMh67eykrxCy5MFDQUTmRz/6EUaOHInrr7/eIzMedCgKc9HFGG9YFiaj6WOY/pqBMUV9z4yxGMArA1tdRpLEdBsNgYIRQlPGeX67+CMILvlfDjWEYRPz6kfE7ls//Zr2QbWEkKJyaO4hLScJrRgMYegEHiYNWPO2GLMw6WGcFgLHmkIpT/VvyzbLE8gBJFDEF02LRcJp2QERQMPlDusCLTEVyxNE7QRh1XeYxrG8ps5txNiXFlcZielCZRaiAwC0uIone9PmUlxlyShrvjZWcqLZEdXtPkPDQ8PlNoGtel1MlY25ToYLmWm4jM+j2EQe7O4x/VyN9xpBc8wzXAYSKNKPoZ+b+TprZM8XcJwXEf362K52KUGAUDMS9mtEiytdCW3PwiM8XxYU5Fp65JFHcMopp+DJJ5/E3LlzMXv2bAwfPjxj9U5CCH74wx/2yEQ99A/YaYJGRDLdp1xkvfp+muFF68O5DB9Qs/hLskGS5obWYV5oHe7qmNcDZ9J7IC6LijjmFEeSMWHQsTxiRU38RWtGgcU6wKKmui85H1wz5eoACBXhn3Ihkh/9CyzeAYfQUycrmTL2EuciqllpbJE7lr+ursbMrowMszf+6mTI7bnMTpLszWark/G++y4JFwuVS5PDUuMgI6Z2S5h2tmMWMGftB5Szk/Yq0+eT3+bsB+3G/D14MKEgInPfffdBCwGUJAlvvfWWaz+tj0dkjmxIsqFLYfo/ZqMlhgVFlhkkWYEoUItuxl6KgFnaGIia7ZQpTH/Q1IJM0grD2r1cxzJE4JEsZTTeQ2fYS1AtH6S0FkjFwRJdFjcSrRgK3/Sv8qfd6pFQmncive5NAID/pG8g+e7fch5CrxituXf0BpP1gqmfX4Cn3zcKKhr9CIipMjOs48C+wGlkQluUTaQGNjZrWe9N0mu7y8tMpjKcqUWYbJmiNketzXYOgHptMiT6y4jM7fYQZet4bpYT+7jG+VvJVKZjGiRH66/ZJ7MTFQoQe34k7RjG8ZmuP+oGydB4i+4aJO4cFsjy+bp0zXMu7uTQw5GIgojMj370o96ah4d+hnhSQkObSU+S6QalWlYIA1KSjH1NEYwcWIr9TVF+fzS5oQxvlBG2pFtjVP+SJCkAY6CEp+aSFYZ3N8fth+zTIKIfvglzIQwYg+TypwAAtGKY3u6bcA6oLwjGFC48NYcTW+oP8W05jsYXEsFnXVAJ0RcZ33GnQa4YClox2DSmraigY80lxkKnbQoWA+YQbPvfDPMm4TKwqJThYZ3YuwMAzxYs+KDEO8DSSRBfCEyyFugjoRLuMpGSIGIAxB8AELTVEOLnoYcCC4Lx2jnVbsL9/B06FADUH+TuLdGnJucDIPpAfBncjKJf1wqRQBFYIsrPyed3CQlXjxwqBVJxx7yI6AOCJTrBJYHwwSdk1Dks/w5Sm2tKR4a5ug7pC9i0SRn6FVV0j4B56HfwiIyHbkE26WIs9wpb1LNiEu0qpjI6kqJAAQNV73R2HQ1jxr5alWsoCiDFQKQkREKRBoUkM3TEnZl3DzdozVFQ2vfzhGG2+ksMBOKgsXzB0i6IqUK3fdEijuKJpuNUDoPc5aILIlyroleCdhAgrR8BEYMQBx8HptWusVs4cgkvtW7+EF+QVFExYYLVQpPBwkCLKqDEOsDrNmltzNrN9sROiyoAAEKwmGdw1YommvtoOhKJqEUWB8MJlcyo+VoIFSFUDLa253SrGASRBMJg6YR1O4Fh1TBdA6sORe1aWgNCRct3wDknWNtqRurv5XQCUCQIFeZU+DbCoudfsW4XKq1RfqSoAoh3IlcCP8s+jnfq5yj4IFRmTs9PS6rVhIrOURx9TcUys/bLlC/JwxEHLyWvh27DWZ3aXSMjK5ruhTkbTa4opm7TE+gxYx+NH9V89giqNzyF+aElAADZNKZbbpjDBRIoQnDO9xziSN6oumCyCYfMET/ZjuMLghRVZj6GPeu2YziTtSWTDibbwuJw52g1mYiaUTjrwZ2HcnSltveZdu5+cw4B10Ei0/XLd1tPHL+Q7W4o7EJYQ8o9i4iH3odHZDzoyJYd173NREKYOcza2lcnIypB0d4rimk/ZrQTdYMCgCky/E1foGTza6hvN5LTTfDtAQC8u/kw5nnJhQxEhOi1i6jh5jDf8LX6QoBq3dCsKS6fgaZfcRyEQCsRYNoIi2XEHDHkWHPsrMJ2jIzkB4blQTDrZUw7ZRWvEsOCYUEv3apyaDPy0VnoWpxMhEjXmXRnggXC5dK6zatg/UjG75h1TGe3wk/ay/3ioVBkdC299NJLAIDi4mKcddZZlm2F4KKLLurOvDwcYkQTaTS1xzFyII+o2dcUQdAnoLqcm9x3N3ShqjSIkjDPNppRA+hCeBQ1jFoBQBnD/uaovt3cR+yqB6hhfRGSXRCi7Shb9Q8QRcL7rVGcYFt3WqNGFewJat6YPmOXIQS+ifOgNO8CAsUgog9K+wEIQ47XL55/2tcg71gBYg6nZYy7aOQ0nC4ZA8JR0yGMmgJh8HGQ9q4DYQqkHSu0g6uh0G4hyeqYTKufQ7n1hGkEx5y4TgH1B6Gk7ALqTE/d6kqqWmYYkUADIRB/CERWiajoB6FZiIz5nS/Iw5MzVOwGACIIqiumQOuD6j4jQadWBeBWNZbluEZHAiIG+PdO8NmabAUssw5DchKr/GAchwSL1XpI7sezZA/ONFqwRL0ObhbVDPscROoAEizuoevg4cuCjETmF7/4BQghGDVqlE5ktG2FwCMy/QNpSXG8tyxPzNlHE+va78+KuZvuHmIA4zf1eFKGqBaqU1QrjCwzvtBRvhPfRbYMJma5t433Gcnv+sQtkAAgFLSoHLTsRAAUkFIQBowBoSKYohbbKyoDGX2i9cZNCHgytjRACaiq7RCGTrAQE9+xp/LuxSH4xs4CoaKJyPBJuLqn7AJatUIzURObiSOnGH0YQMoGAk07+Q5UgDDoWD52sAgs2u5y7qqFIlgEFusALeNlCwTVzUZEv5ogzX7BnK8zaUPMEKqGG28yRfdmFBwznnDOBTRcBiaIkDvsObNs2pLqEfxvzUi1AKbWjYAUlauZlzWCl/n+ada6dB/WH6SmJer2sQjXGinRtryeELQHIYNYFvJYweftph3y4CEbMhKZiy66CIQQ1NTUOLZ5OPJgt+YzPWwyG/hN0/5gprjkhdFe874aiVGrVzNAVhS13ZQynQGMKfrEfIRlvC+aNx9qrUzkqDNRvOMdlxbNWqG5kgBdCAvB2seyjmuWEr4vCRUjdOGvuYjUJhw2Im/sWhjteC60jhL1gmkuEQoGBcRfhPAlv+eRL9oYMLshGEIX/sY0hwwLsznjb7duF6Sw9e9gkJeLpcCTsGhEMu3fz+6jBOjeh0L63an2JFas+BjXX8+DZG699VZ87WtfK3iMM844A3V1ddi8eXPe+1xxxRVYsWIF3nnnHQwdaoi4x44d6+jr8/lQW1uLmTNn4pprrsGwYcMcffo6MhKZ22+/Pa9tvYV4PI4HHngACxcuRENDAyorK3H66afjuuuuQ2Wli7gxT3z66ae47LLLwBjD/Pnz8b//+789OOsjCSzLO/M2BmYmM0zN92Luo/IPxo0yKtEgRuZecEEw1Y6iMCTTMpZtbMOZR5ktMgz2CgMECi4Kr8YeyXDNkF7O3kuOOwvs87f1965H0/1upru4niGXgjva1DZK4cj1okHLHJshn4pezDGT5YVS2wQ1YmXW4VCV87hpYQwyo+WGYto4jgXbZf4FrWL8+Nqxeh+qRSYbYXeZR+EPc0TXBfH8PjmOeTDImTyvuzDlHSoAWXPHGJ30vkcaFi1aqL9+5ZVXukVkegMXX3yx/rqrqwsbN27EggUL8Oabb+Lpp5/GMccccxhnVzgKCr8+VIjH45g/fz42btyIYcOG4cwzz8S2bdvw9NNPY8mSJXjuuedQVZWhvkcWpNNpj7hkgcV2woC0JCMST6M4ZPj9D7REkUzL8AnUQXUa22I6KemKp+D3UUedJcYAsXMfUFINX0c9EC6HP94OxSQUVsDwyecNWLs7gmq/D8PVFntlagAYJrTitODnlm29YZERx86GtJkXlCS+oNUCZLoBy4ESCMkungOECvyvTmjUxZ9QQBSNPCzqP9/EeWCpqIm0EDUKyEhu53Cc6TleKCxXWhfyaqRJG49vZ+ZFiQpqF2OORAyCySnozFHw8SKQ5nMWMtSCIsSkgclvcdKcENq1pAH3bOHdwsEskILPXSxLs906rdYoQgiINo4ggjDWC99Q9Vj+EIiSuZ5YN0bk/xV8eeVuMYN/hgQs2ZWzr360TN+pfohEIoElS96H3+9HOBzGihUr0NDQgAEDBhzuqTmMEul0Gr/5zW/w0ksv4a677sLf/pY76WZfQlY5wWmnnYaf//zneOaZZ7B9+/ZDNSfcf//92LhxI8455xy8+eabuOeee/Daa6/hiiuuQF1dHW699dZujfvQQw9h27ZtuOSSS3p4xkcmFAZeeRrQ18hESuYkR1bUUGmjTVJzy0iSomphuAvCHJkEMBA5xYWsDKCpKBhjUBRFfwqPpxg+3cJzSqzYk9afiu3UCQASzHnjc+t3sBBGTgOpUE20DncNdbxmiswXO3PuEO01FSHUjOZ5OrTtIPAdeyrEoeON8VQrCy9pYBAeUlxp2g8wzPd2S4iTTNCSahB/UB9P14eYCBUA0Mohqj6Fv7fkACEEQs1IVZRpv1LcIsA1MIUTCKFmJISakbqupjB0g7Dk2IX4Ag49iVAzUs87k3tw41oJNSO5eNkf6iE9jBO0tDbvPCuFQKgaZtXb5EEOadlA0LIByP25qN+xvK9r/8CSJe8jHo9hxoxTMG/ePCiKgldfffVwT8sVPp9PzxO3cuXKwzybwpGVyNTX1+O1117Db3/7W3zlK1/BySefjOuuuw7/+te/8Pnnn2fbtdtIpVJ46qmn4PP5cNNNN0EUjSefG2+8EZWVlVi4cCEaGxsLGnfXrl144IEH8NWvfhVTpkzp6WkfGbBoZBiYwkz3KyOwWvciMduummcI1gazo8lsoVGgqBFNTNXKMBCmYNlOI1KkK2kcZ5Ky0TLd2YHP8evylx2ncUn4k3zOtjBQw+JhTy5n9aSoPymzJcYU3kxU0sGf8rUrYXYN2YiP6T0BT2wXmHMlgufdaOyrWT/s64VjsXHRbujkxTyGqdYQcds/17ZuoBfdCrr7zaWllw7IQUmvnpeHvo3Fi7lb6ZxzzsUFF1wAABmJjCRJ+Pvf/45zzjkHEyZMwJlnnol77rkHqVT2qLlnnnkGF1xwASZMmIDZs2fj97//Pbq68reAmaF5ORTFafkeO3YszjjjDMiyjAcffBBz587F+PHjceqpp+KPf/xjznn2NrK6lv74xz9i1apVWL16NbZv347W1lYsXrxYr7FUUlKCKVOmYNq0aZg+fTrGjx8PQciQQTRPrF69GpFIBDNnzkR1tVW97vf7cfrpp+P555/HkiVLCrKs3HTTTQiFQvj5z3+O995776Dm+GWARkwcZnXT2mveaLiFYCMvpjwy/I2mEtAtOgQMCmNZ0m3w/ScrGyxbLylyf3II07Rj24Lo9Iz98wEVRINR2YgMM1toqI3IEEMXoYt+zeDiFMPlBJiIjEsNICJwtw0VVNcP+Liq5iYw+7tg6aQ2sHU8x4HNbzMt9tmQgUA5LEMFjtdNZM7j0nvHzA6b9qlfwiNi3UFbWytWrvwExcXFOPnk2Rg8uBJDhgzBF198ga1bt2LMmDGW/tdffz0WLVqEoqIizJkzB7Is49FHH8Xnn3+eUS/2f//3f3j44Yfh9/sxc+ZMBINBvPrqq/j000/h9+cOq7djwwZ+fx01alTGPjfccAM++OADnHTSSRg1ahRWrVqFhx56CA0NDfjTn/5U8DF7ClmJzAUXXKAzyfb2dp3UrFq1Cp9//jk6Ozvx/vvv44MPPgAABINBTJ48WSc2kydPLviCasrscePGubYff/zxeP7557Fly5a8x3zxxRfx8ccf4w9/+AMqKipy7/ClhVWky8DQEU0ikZLAGNAZM+eSIJAVBlFd03WBr8LABAKmMMSSEiRFQbBrHwRSDjlYoSfEY6oricoSiMIgywyCuhD5BevNM2PKkQKwOnXUQREZHg5tEJlU5Wj4W1V3q2kBjY6cjdLNr0Mcc7LaBlgtK3bCYGrT8qyYq1Br7iVNJyNQQGY6QdKEwloKeVo2kOswCOFpYbJwCkKo6v6yNeoGGxO5AngtHlmyjWMdlAg+g/Cp8zikKKRmDyFgrGfnR3Qxt/o6U2mIfo8evG5HIFd6++3FkGUZc+acDr+f18O64IIL8Le//Q0vv/wyfvazn+l9X3vtNSxatAgjRozAE088gdraWgDA3r17cfnll6O+vt4x/qeffoqHH34YFRUVePLJJzF69GgAQFtbG77zne/gs88+y3uuXV1dWLduHW6++WYAwPe+9z3XfnV1dQgGg1i8eLEezbx371589atfxauvvorrrrsOw4cPd923t5H3r768vBxnnXWWnlMmHo/js88+w8qVK7Fq1SqsW7cO8Xgcy5cvx8cffwyAW1DWrl1b0IQOHDgAABg40N1HrgmltH650Nraittvvx1TpkzpM4rxfgE9Gy8QV3Uyhs6Fg9db4poISeFERHM9STIveKhl7CXJCBCsgCRz0qLlpFGYAoFXVQJj/KZPeyHqKO6ipcmFdiWMcmrUH6JlgyG3HwACYUTHnAv/J38FADAxyPsMnQCldBBC5/2c7+IPmqwjyGwZ0QiHlNLdULRyCFgqDqWjngtFKwZBbtwJw/qijkcF0KphkBt3WMYnReVAKmocQ/1rlpnSquE8y3CnOqbq+tJIEREDlnwvtGIwkE5Cbq/LeM1Iaa1xtFAZSLA0Y9+eBq0anp/1SetfPhBy2/6enYQ/rOtfaNWwzDWuviRYuv4APlxdDxAKItrz8XCwVBwQ2x0Cap9fQDrVk8LlzJg1cRBOmdBz2iLDrTRP33bhhRfib3/7G1577TXccMMNOsl/+umnAQA/+clPdBIDAMOGDcO1117rGqDy73//GwBw5ZVX6iQGACoqKnDjjTfiyiuvzDo/tzDsmpoa3HfffTj77LMz7veb3/zGkpJl2LBhuPDCC/HEE09g1apVfZ/I2BEKhTBz5kzMnDkTAPfxbdiwAa+++ioWLFiAZDLZLb9ZLBbTx3dDOBwGAESjUdd2O26//XZEIhH87ne/69Gnw6qqDFVcC0RNjXtW0UMNMeCDTKk+n/qOJAgB/D5BD5MmlECRFRBCIAoUVEwiFBRBCUE8KYFSAkWgCPoEFIV88IkUaUlBZ6cfRUE/WFkIaYkhIPshFwcgyQFQSkAUASTNIAoExQGKQCAFwD0baXewI10DBoo/tP8HvjumCQNLKYTtH+Xcb11qGOYEuYWwoqII6ZPOxf6aY1A1eAgSrUam23BlDarmXQPJVwQ5JqO8gn9HaSAM2ZcGEX1gap4cIvhAA2EEakogRSRIvgSUJAX1B0CoAEksBqEUodoyKAkfUrQL/uoSUF8AiVQYxOcHkyVOEqU0iOhHcEA54qkQCKVgsgwiiPCVlyLJ2kHVxGTBmhLEEyH4q4qhJAjkBBAcwIW+iXSYC68DIYAQyHEFoQzfSyUZRwrFCKrtCSmMQE3pQf+2kkoRmCzp4/YkEnX1qK4uBvUHLduVtB8ptPfKMY8EMCmNpBJ2vT7xeBj+6hIIwSKXPQ2UlrZBFAVu/fO7kzqFURBRAHGRJfgy7NPTKC0N9ti9eOfOnfj8802ora3F3Lmng6ru5tGjR2PcuHHYtGkTVq5ciRNPPBHpdBpr166FIAg455xzHGOdf/75rkRm9erVAIB58+Y52mbNmoXy8nK0t7dnnKM5/DqZTGLPnj3YsGEDbrvtNgwcOBATJkxw7OPz+XDSSSc5to8cORIA0NTkUrz2EOGgw6937dqFlStXYvXq1Vi5ciX279+v+/R8vsMbSrd8+XK8/PLLuPLKK3s8Lr6lJcIFqgeBmpoSNDV1T5jV0+iIJNHWkUBTmH9mbe1REBD4fYIq/iKglEcmURAIAkFXLI1EXAAlBImUBEIJIrE0EiJBKiFCFChkhSHWmYCcViALcUiyglBXEpKcBIskAEIgMglRiUCgBEgBUjo/EiMzAiEP683DkdMAABGhFIHB5YiIAsqQm8gIYPDPvAxK8y60tcfAUimguAodHXFEIiloTspILIWishqkoil0RdNob48DYCA+BiWRACFpIweHIIH4AYF2QYlFoETigJQARBmEClBivHZUpCkClopC6YhDQBfgS0NujwGizBPSUQrIMiBIEP1dkNrj3AcnK4BAQeUIlI447w+i96GIAMkYlGQMoo9/96Q2Pl/q59dSScQQCbh/L1kqDrk9BtFv7CuIXQdNZOS2KJgs63PqSZQAaG6JgIhW7RSTUpDb4r1yzCMBTE5Dbou5Xh+pPQaBdoEEsueWmTCiAseFarl1L0P1a6lhG2hJNWi43LL9UN8fe+pY//73AgDA6aefhZYW/sCtkaQLLrgAmzZtwiuvvIITTzwR7e3tSKfTGDRokCWwRUNxcTFKS0vR2dlp2d7Y2AhKacZQ7kGDBmUlMm454ZYvX46rrroK//mf/4m33noL5eXllvbq6mpXDWxRESezh1PwWzCR+eKLL3R30urVq9HS0gKAuw9KSkowa9YsTJ06FVOnTsWkSZMKnpBmcYnH7bVdODSLjXbxMiGZTOKmm27CoEGD9LAyD7nREUki6BfVfG7cUaRAzVKiZbUj0MW6WritwgBqcjUxAIqchthxAIBiFQAzxhdvMChMl/7qa32+a6IMCsGeIc+G3VIVuphh3SOa3oRQkBwJvigU0PJBoMVVJmmLpmkxaUcoBQEBVRPLmVXL1kR3NrGvliNGz4RLYM4HQ8wRTKb5OxPS6a02vYvqIrILTim1EA8iiKa8N0r+HwCyCGwLBRVyfh69gSMxCdshQU9etiPsI1i8+A0AwPLlS7FpE4+09Pk4AdA8CYsWLeqRnGY9+f2dOXMmzjrrLLz55pt46aWX8N3vftfSTh112/oOshIZSZKwfv163eLy6aefIhKJ6BaXAQMGYN68eTpxGTt27EFf2EGDuJ/STeAEAA0NDZZ+mbBjxw7s3r0bgwYNwve//31Lm2YCe+utt7B161YMHz4cf/jDHw5q3v0e6ufWFkkiHFR0rQtjqpCXQi8fwAh0VsLfq0/yely2uo6nIgAU+EQCBWreGZXRUCkBSQ9RYigN+5FWGAApb42MzChAshOZmGIk8SLg1Xkry4Jom/afiKdk1K59JOO+gpb91izWpYIjwKeylLtkRL+AqrIQb2OakFQjPcwhRCXBErB4J8BM50CpSibA87R0mtIMEAJaWsN1Oup7a6STm5gYoJVDTX0AUlwFEjZE77RisGrl8YF1FpbWgFYM7pGbKc8b00tp4jIFM4l+fu4esiDTZ9vDTOaQZHLufaxfvxb793MN2Z49uwHsdu3X2dmJ9957D2eccQZ8Ph+ampogSZLDKhOJRBzWGIDrWerq6lBfX28pQaAhXw2pHUOGcKvZ7t3u8+6ryEpkpk2bhmTSyOY5evRonbhMmzZNP+mehCZC2rRpk2v7xo2c4ebrKjpw4EDGD7WxsRGNjY2uX5QvK3SrC8yJ7DQrizUPjE50AMuNyMgzoyayU9OUG7lm+BvCFCiEgjAGQgE/FcDSEoQ875FyHuUhY8weNUchCiLAkharSttJP0DFJw9YelaFNSGuQRCMHHTGJAVRgEZ0fKbKloQATHuK0StLA7rFRc34aq6wzCtGu93U1f1zZT61WXwA1eJi6UJ59JPeTdCja5jpUPkgn+rJeY1zmESxPTV/DweDI8cks2gRt8Z873v/D1deaTxAm/U3L7zwAn75y1/i1Vdfxdy5czFx4kSsXr0ab7/9Ns4991zLeG+88YbrcaZOnYq6ujosWrTIIexdtmxZVrdSNuzbx4vvZtKo9lVkXQkSCe6vHzZsGG666SY88sgjuPnmm/Ef//EfvUJiAP4BFRcXY9WqVbrbSkMqlcJ7770HSinmzJmTdZzjjjsOmzdvdv132223AQDmz5+PzZs34+WXX+6Vc+lvsJQTML1g4HEyWki2RkaYbmFR+5jJjIUEQc8ZY+Ix3JekdozEZcRTaki27b4mMfevaS4iE0UIr8dPwOwR6gYCriOhxHHvlMucTzVDy1QrjCk5HLGHKgMGYbC5dDhb02olZZqrOSme+Xj2sUyvif21HeYoqe4sErn26W8LT3+bb99B5tw8PWxFOQIsMpIk4b33eI61M890Cnc1nH322fD7/fjggw/Q2dmJb37zmwCAu+++2yKYraurw1//+lfXMbR9HnroIezYsUPf3t7ejjvuuKNb81+2bBneeYcXv821vvY1ZLXITJ8+HevXr8eePXvwu9/9Dr/73e8wdOhQTJs2TbfKaIrlnoLf78dll12GBx98EL/73e9w11136ea2O+64A62trfjKV75iCVO788478dZbb+Hyyy/H5Zdf3qPzORLRGUuhNJzhSZQBsaRkIRyae8m4d6lVqmElLJrCgbuVoiBSghMZpoBppEORuGUHDIRp1IjhnvcioAT47zNCsNdK6lBCqBKcUWpKjhwgr4nnokUpQXGA+8LG1Wpkg6qpWFxIggnpsmF6ODJRCQmBoGpb7MnqtPe2G7Kl1pHbcUyVq1VXkbnopWWOxLyPW0UpToKIrtGxEy7HDk7k0ae/6UpItwndlx09dc2yj9P/kwZyfPzxUnR0dGD06KMxcuSojP1KSkowe/ZsvPPOO3jjjTfwjW98A2+99RbeeustnHvuuZg5cyZkWcbHH3+M6dOngxCC/futaQKmTZuG73znO3jsscdw0UUXYebMmQgEAvj4448xePBgTJ48OWsumV/84hf662Qyib1792L9+vUAOEmaMWPGwV2MQ4ysRObxxx/Xw6pXrVqFlStXYs2aNXjxxRfx0ksvAQAqKyt1jcy0adNw3HHHHbQo6Nprr8XSpUuxaNEizJs3D+PHj8e2bduwZcsWDBkyBL/61a8s/ZuamrBz5060tbUd1HG/DGCMoaUjkYHIqFYTRnQri+Em4n8lSYFgdkPAZMkxRXEJ8RbLjrwGkwxfvFUvWMjAAIVBSiUACLy2U1pW6zsRnBbYhEn+3VAyWF4ybTfOht9A/QJw/elFKCIJ3eLBGEDMUXXmxTlcAXnKN5FM+wyrhugH0glUloeQTEioKAkZthRKVV0MYL5p07KBPBdMV6Npu028W1LFi/wpMiD4IDftBC0bqJMFWjYA8AWNfdX5kEAYLNHl8iRLQMtqcXDIsvD4gmr9nP4DX+UgkM5Dk4/kSAIRxMzfJWKI9A8WtHwgIPR/F5/mVjrjjMx5WDScf/75eOedd/DKK6/gm9/8Ju6++27885//xIIFC/D++++jtrYWl19+Of7rv/7L4W7S8Mtf/hKjRo3Ck08+iaVLl6K8vBzz5s3DDTfcgB/+8IdZj//iiy/qrymlKCsrw6xZs3DJJZe4hnT3deSMWhJFEZMnT8bkyZNx1VVXgTGGLVu2YNWqVXrk0uLFi7F48WIQQhAOhzF58mSd3LjFnedCKBTCE088gQceeAALFy7EW2+9hYqKCnzrW9/Cj3/8Y1RWVnbrZD1wcEsKc32yVhgDMVliNOEuCIPC7HWUjNIEjPF2jdRwrqJw4S5jYETlOWptJaYoXNQqAPetMAjJnz9KQVb4vC4uWgUAaJDdk6pVCZGs59lJS6EleisKCKCSoWVhhOqZhAFua4kefQ6KtvHvMQJFfH6alcMXBEt0IRgQkUzKCAYEaHF1XN+hWmWYug/AE9mFfFC6mpBJyEqoCKiJwDQtEgkaOYosRfTM5nxfEEh0wY10EH8YLBVzWmR6QFRJCAH6WWE/GggD8EKsuwPiD2dq6TF3EPEFc3fqB/j9750hzZlw/vnn4/zzz9ff+3w+XHPNNbjmmmscfd99913XMQghuPTSS3HppZc62h5//HHXfbTM+YUi235f/epX8dWvfrVb4/YUCg6/JoRg7NixGDt2LObPnw+ApynWiM2qVauwdOlSLFu2DISQjKLdXAiHw7jhhhtwww035Ox7++23u8bFZ0JfuPCHC4bg1r2NJ78juuiX6foXoxOzchndpaSYxgFUokKtBySMcQuEWl8JDEjJxoIr92AErqFZoQAlYJSAEqKnkbeGRgNyuNp1HIslxK3MgDms2vXaEkB3F2V+hs3psiGa2cfFukNsL5jWL7v7LMOB8uznwUNu9DdXpIf+hx5xTg4YMADDhg3DsGHDMHToUAQCAXVB7P8CriMOmsYlQ7OiaASD8cIBproEhhiYGSlkTG2QJUAxko6pI2gHBUlHeZivef88Qm4LMWD/uPUK441Z8woTESEUjBC9vqMcKOVtdpWxoop1LbyBOtd5LSrILAy2wBggf7LiBpr1KVjLkWM+buGLSHf28fDlQw+LfT14OAh0K7NvNBrFmjVr9Pwy69ev17P6aeTF7/dj4sSJPTdTDz0GUx47BxQGUJNrSVH/EcBinZFkmbuSYPQNxBoRDhC0sYFQwEAURXW38GP54i1WgbAeD2XFWcH1aFaMcMXC7pfGSflEAYAMnouYJ60rL+F5ZXhBPwrfad9HW0cKpUUB+DTdkNnCQnhfEigCupp1wS4tNVlvdEGvOcLJmIdQWgO5k+c/omW14AUoc07fAVpcAYh+0JIqYz5m0kMISLhMPfkQaLAIJFhY2nVSVMHH9uAhC/h3MJPbyda3tEZ3n3rw0BvI69vV1tamlyBYtWoVNm/eDFnWCgnyG2koFMIJJ5ygV76eNGlSt0qJe+hdGLlgnEzGcCFpcUWmNs0ao37emqvJXEhSYQoCok8fjPdXeDg146SFEUHVkRgB3WYIkHFBeI1tXt2zEAyp8GPN3hSKg1S3lgT8Ik86p8UvldaARVtRVhyEUlEL6ajpoMMmQtJyxmikRs9zwsehwRKEzvs50ts/UbcbFasJobqgGYCa2I4TmcyaA713RoOMlsKdhAzNkGE9Ueel9aEUpLRw0S8R/VzY7MFDFtCQu27NDR4x9tDbyEpk/vd//xerV6+2xKlrxKWsrAxTpkzRicvxxx/vWofBQ19EZhOHotg1MsZfPSUMTEE6KsMxvETqe8bDq/l6zkkRlRmYaBL76jsauCS8wjEnnmE3f3QqQRSHfDh/YgnKhQRGVYvcbWTWtxACormJtE2Uwjf+bDApBZI2LDI2H5Uxr9qjVIJj1644+5quWA7k2880L8frfPp78ODBw5GBrETm2Wef1V9XV1frpGXatGmuZcA99H1o4lzGgHhSQsAnQGEMaUm2al70/iYNjD6IVdir2PgIY4op2kmrs8QASIDCfev/XB7HoBLgvOOMPf1I4+TgVsecQ6SwYmT/0/51/GE6RZFAcXSNAFkV94IIfC2nKnOhgKEJ0SwvmvVGs7BQwCYKtvIHsyWGgJktJA7kSTZ6SXvgaV88ePBwJCIrkbnoooswdepUTJ8+vccT33k4jGBAWlJQ1xRFeYkfkXjalNTOsMIA0EOuqSlqKZmS4PMJljBuWVIQUtPzB2K8Xg+3vBjuI5qMoaJpFQQ5gQkYjGXNx+ACOYkwSWFO8Au0yO56jnyITNfos7ke5EPAbEFhQoD/DZSApJN6YjtukTEJW3VSIwJI6UPQQBGYbD++NVqIhkrAEhH9mABAi7uXIoAGwnm7diw6nTxAAkXWcG4PHjx4OAKQlcgUEtLsoT+BqUJeBlmxuo0sKX2hWmRMXhPNAiMqGtFhoIwhHBRQ7vMDsgSSjoOyBCTq56JfcFlMWctalLRvAQB8s2gPNqaGgMoCvh5egSmBXRln68vDtaQEyyFUDQPQzOepEgqmCmuVQAnEaIvuXtL0L7RsEFDfqhMQ4guoOVj4T0NL/sYUWRf+2l06tKQaKOGkQmrczgW5qlalUBSSbI4WKOSlpTWFTseDBw8e+jyOjNzQHgqCUWaA6YoMpupgzBJcTlq02CLDWqO9B3iqGAbASNamYMi2BRi68VGAqZFL+oGthOTmiudRuX8ZAiSNgwa1uYBgEA6iZfPl76Bu5Bl5zbHV5irXcIukJlbNjCsytPW6W8cLh/XgwcOXEx6ROYKhMGb5yxhTxbzQs/DyBk0LwyzCXs3dpAt5TeukLgIGIOsqYP43kOBWkaKOHajZ9jInNGBwK5wYju7D8f66gz5XRnyu2hUtb4xGZgxxLmDWtziEs1rZAQu0fn2PMHj6Fw8ePHxZ4RGZIxQd0RT2NPC07HsaupBMydjbGMH+ligAhuaOuCnAmumWGPsarajKYLOVRsv4y0kRZ0ViskMbSkf1vvfgT7SiOFYHQSTdDqPOhGTpMOMNFWD+OodDRh0lQqmRFE9NGhcKiAgHtbwxsP419jS9zCbiPfwgoRIjh4wHDx48fIngEZkjFGnJKJLHGCArhh5GsVlkzDWV7C4lfQyYrDPgBMfvo3obUTP2Ws023JVUtn0RyotEhIKmIo09gPiACVC0RFtUNBVXBIpCGkkhkMqGoTjo1zP3CjUjUVESRDgcNIl8AYBySkfNlhptGGrfVBB622JCi6u6rcvx4MGDh/4Mj8gcwbDkfSE2mqE16gusKczaljPGDQrUxdniorISGfPSXb70LwjsX9Ptc1mRPMq5kfpBNLcZFawWH71cgC0HjNmwonc3vWFwdYFp/Yi9zTVfTCb0TWuOBw8ePPRneETmywSTroXZLDLWQpBGsUg9aolZw7KhmITCpkMQlon6HOzUnSSA+UxhytSejFGzoBCDz1gijsxlCMzFIE1aHtshSR7uJU+r4sGDBw+HFh6ROQKRTMs8N4y2gQENbTH+kmmuJgWMMcQSaZipiBZerbgQEvMmhTH4fRSBrr0gUsK096EjMoSKaB5zAdKDJwPUB38whHkzhuOSE6v0KCbmL4LVKqNGJIkBEH9Yr7nEmwnfJmguMOsxtdoyNFuNGdHvmquF+ItAg16qdg8evixobW3B3/52H77znW9hypQpmDx5MubOnYubb74Zu3fv7taYn3zyCcaOHYtf/OIXBz2/F154AWPHjsW999570GN1B2eccUaPJdYtiMiceeaZ+OlPf5pX3+uvvx5nnXVWtybl4eCQlhRbJC7TQ64BXoZAkk2WGb2XIeaF+tfcrsDUEUA46OMGDD1hHOstHuMKJoYhFdVCGnUKQAnC5RW4aNYoHDeiEoSKoKESKEVVOh9RgoYYVqgcAhouBxF8kCtGghACoWYkaFGFmo/GSZxoaS0Cg0Zbc73YugkVg0GLKlz2rQHtRu0jDx489D8sW/YRvvWtr+KJJx5Fe3sbZs6ciTlz5kCWZTz55JM4//zz8dxzzx3uaR4xKKgkaV1dHQYOHJhX36amJtTVHXxYrYfuwRyBxBx/GRS1npLJc2RxPenjMF57iQBGGWzbWIc6Grl5wuVgRIAoiCAyVI+QYW1hBNYij45XHjx48NA7WL9+LX75yxvAGMMPf/gTfPObl2HAAOMh6tVXX8Wvf/1r/OY3v0FxcTHmzZuX99gTJ07EwoULUVJSWDJMN5x99tmYNGkSKiqcD179Db1WWz2VSnlFJPsUDDGvzBiIHrbEdNZjaHZNriaT6FdhDFRPLGcb2c6Wemn+AEAEH5iqcSGEwUhUp/fg9ZSY/s7aZkMmWYund/HgwUMhUBQFt976O8iyjO9//0e49NLLHX0uuOACUEpx/fXX47e//S1mz56N4uLivMYPhUIYPXp0j8y1pKSkRwhRX0CvaGRaW1uxbds2VFcXVgvGQw/CZJIxJ7gD1PpH5rpKNtONosgIRurUYQwCoajWl2BkHwKRfUY7YxAEYjlmTyPsM1lWtGR1lIARqkdlUUErkCQCEABB1BP1+kT+3k5OMpEV6g+4CIjtUEmUBw8ePIC7lPbu3YPa2gGuJEbD+eefj8mTJ6O9vR0vvfSSvn3s2LE444wzkEqlcO+992Lu3LkYP368ronJppFpbm7Gr3/9a5x88smYNGkSLr74Yrz66qvYt28fxo4diyuuuMLSP5NG5oorrsDYsWOxb98+vP322/jGN76ByZMn48QTT8T111+P+vp6x7EbGhrw4IMPYv78+Zg1axbGjx+PWbNm4YYbbsD27dsLuYTdQlaLzIsvvogXX3zRsm3Lli349re/nXGfZDKJbdu2IRaLYe7cuT0zSw/dgJVQMBuxUVRLDLPpYMAAosj6+8rSALpiEsCAsmI/ojEJABAO8K9ORbEfLRIQCohAovfEvqOrBaBVnaLgA5EkAJRnDAbBwMoiiD6Ba398IQjVNSBUADo7IVCKsiI/SsID4bQnAcMHOJ9KimsGIVydnxvVgwcPHgDgk0+WAwBOP/1MiGJ2h8d5552Hzz77DB9++CEuv9wgPYqi4Nprr8Xq1atx0kknYezYsTndPy0tLfjWt76FvXv3YvDgwTjppJPQ3NyMG2+8EfPnz+/WuTz11FN49NFHMXXqVMyZMwfr1q3D66+/jo0bN+Lll19GMGjk7Xr33Xdx5513YvTo0Rg3bhyCwSB27NiB1157De+++y6eeuopHHfccd2aRz7IeqXr6uqwYsUK/T0hBF1dXZZtmTBjxgz87Gc/O/gZeugWGDO7TKxRSVpkkrnFnAxPD7NmhsWCAbpbiSkMlspFioySlY8iPnzm/2/vvuOjqtIGjv/uvTOTDiEhFGmGktCl2bEAKwgurmUVhUUsqy6iIupa0BXbi4gi8O4KsqtiQRQb7yJkQZGmiCIsG1YQAgoktFACqZNMuff9487czGQmyQQCJOT57geZ3HpmbpY8Oec5z0H1OE/J+1EqFKczVP9yAx7ftsD9mEGMr33WAteBdWUCqGF6ZRRFQYtoaEl6ZIQQpp07zUVx09Or/6HdrVs3ALZt2xa0/cCBA0RFRbF06VKaN49sEdlXX32VnJwcrr76al555RUcDrM0xbp167j77rtr8hYs8+fP54MPPqB3794AOJ1O7rjjDjZt2sTixYv5/e9/bx3bp08flixZQseOHYOu8X//9388/vjjTJ48mffff/+E2hGJKgOZ66+/ngsuuAAwf3MfM2YMaWlpPP3002GPVxSFqKgoWrduTVJSUu23VtRIYH6LfxDE6jPxJe6G1pMxfMcGBz/+g6w1mQL220uPYivYR+wvX6O4T00gE1TrTlXBW77cQPCaSUKIusKdtRb39jUndO5+uw2321PLLQrPnn459rRLT/o6BQXmUi2NGydWe6y/l+XYsWMh+yZMmBBxEFNUVMTixYux2+1MnDjRCmIALr74Yq655pqg4atIjRkzxgpiwMzP8QcyGzZsCApkKptGfd1117FgwQJ+/PFHCgsLT1lOTpWBTKtWrWjVqpX19fnnn096eroV3Ij6wd+Lglo+5UhHQSUwMCmnA1rgViOg9yWAWnwEndPDnJikobbu6Vs7yf/HlwRTMdnX/0oBmyblkoQQdUtl+XmKojBw4MCIr7N161ZcLhcXXHBB2ODn6quvPqFApn///iHbzj33XMCclVxRWVkZq1at4r///S/Hjx/H4/FYxxqGQXZ2ttULVdtqNGvpVHYNidoVNLRkgEc3sAf8H0cnTKZ3FTOPjDB5rYqrJCgG0pyhv1mcrA+KLmFU/HeAQW6fe2jdrDGJXp28Yy4axUdx9FjVv7G1aRYfNCwlhDh97GmXnnBPR0pKAocPF9Zyi06tRo3Madb5+cerPTYvz0z6q5j/kpycHNSrUh1/UNGyZcuw+yvbXp1wpVbi4syini6XK2j7tm3bGDt2LPv376/0esXFxSfUjkjU2q+qhYWFLFq0iDfffJN169bV1mXFCTDjlXBJt+XTrf2TjMqXIvAdETTUFDgNO3RKdsUhplMheP0kQFWw2TQUVUOzRwWtqWT9hhNwiqaqqOqpDGQkSBJCmDp2TANg+/afqz1269atAHTu3Dloe1RUVO037AREWn7CMAweeugh9u/fz6hRo/jnP//Jv//9b7Zt28b27dv57W9/ax13qtQokFm8eDHDhw/n448/Dtq+c+dOrrnmGh5//HGmTZvGnXfeyeOPP16rDRU1Y+XrBtWKCU7qpcLf5Ym/OkEBikGFTprA+dwGRi0HMzvczdlQlkpJ2+Df5BT/XGrVlx+jaqGLQwohxBly4YUXA7Bq1Qq8Xm+Vx2ZkZABw2WWXndQ9U1JSADNJOJxw06Vr06+//squXbvo0aMHzzzzDJ07dyYuLs4KhHJyck7p/aGGgcyyZcvYuXNnSI7M5MmTOXToEK1bt2bQoEHExMSwaNEiFi9eXKuNFZFxlnms0OJgXklw9V7M4EYhoLKvb6f1v8C4xNCxuQrQXPm+cwnphLG5a7fL8O2iK3m/+DI8Sans85pJ467G7XxrVCsoimbOWLISfc9UDowS8W8tQoiz3yWX9Kd167bk5h5kwYIPKj0uIyODTZs2kZiYyHXXXXdS9+zatSsOh4NNmzZx6NChkP3Lli07qetXJz/f/NkQbigqJyfH6nk6lWr0E2Dbtm0kJiZaCT9gFsJZt24dLVq0YNGiRfztb39j1qxZGIYRUoNGnB6FJW7fKwNnmQe3x0zJ9XjLg5RGcXZcHrNejH/dJQyIi7HTKN6Bzb+QoteFVpZPU0dZmDvpaJpCQkztFoguMcyu1ZgoG/u9TXgs71bKktOsFaodjZNp2jgGUNDjQtcvOl2hheKIQU2UWjNCCJOqqkyc+AyapvHGG3/j44/no+vBUyKWLFnCk08+CcCzzz4bcVXfysTHx/Pb3/4Wt9vNSy+9hNvttvZ9//33fPHFFyd1/eq0a9cORVH4/vvvyc7OtrYXFRXx1FNPBbXnVKnRT6C8vDzatWsXtO2HH37AMAyuueYaYmLMVX8vuugiWrZseVoiMVEJX3KuRzfQDVAxMBRzArZhgN2m4rBr6L4qv/7KMFE2lRjNRmFANGAYBnZNxZ/epQQMRCmGju0UdYiYS1zolOFbjVpRzJGl2EQchUcoU1SwR4PnzPWKKPbo6g8SQjQYPXv2YvLkV3nuuaf53/99jQ8+eI8+fXqjaRpbt24lOzsbu93OCy+8UKN1lqry6KOPsn79ejIyMsjMzKRXr14cOXKEH3/8kZEjRzJv3jzsdnut3Kui5ORkbrjhBj777DOuvfZaLrroIux2O+vXr6dRo0YMGjSIr7/++pTc269GP4LKysqsKVV+GzduRFGUkOGmlJQUCgvrV8b52cII+K/uNdAN3aoFUzGbxdCNSvYZoXkxITcKSZ6pXYFF7vxfWgm9AXVkrCOEEOLMu/TSy/jww88YNWoMjRs3Zu3ataxatQpVVRk5ciRLlizh5ptvrrX7JScns2DBAm688UacTifLly8nPz+fl156iWHDhgGQmJhYa/er6Pnnn+fRRx+lZcuWrF27lszMTIYMGcKCBQto1KjRKbuvX416ZJo1a0ZOTg5Op9Pqffnmm29QVZW+ffsGHet0Ok/LGxDldN3A5TETzAwDDMVcH0nXDTQ1IAG4wvpJircUXYvCCgZCYoLA4niByb0GGFUntFXlmDeWJlqJ2Q7VjqqXd0E6tDDNUJSAKrxqUBBTsbKvEEKcScnJTRk79gHGjn2AlJTICsFt3769yv0XXnhhpcc0bdqUyZMnh2z/+9//DoTOjrrhhhu44YYbQo6vqsxK69atw97fZrNx9913h60iPGXKFKZMmRKyfcWKFZXep6Zq1CPTr18/SktLeeGFF8jKyuKvf/0r+/fvp0+fPkHjfC6Xi927d9OsWWj+gjh1jheVcTCvpHxSEb51lfTgjpNGceU1CnTDwF5yBMVdElQKODAWMHxDvFF2jcR4BwlxDuw2Dd2r4/V48XhrVhbvjcJBPH7slgpby+9474Uq95xPQMVe/xEKSrRZx0Br3IyoxCQUBfSE5igx5j8UNk0hqZEM9wghGpYtW7aEbPvxxx+ZM2cONput1oax6qIa9cjce++9fPXVV0GLSaqqytixY4OO+/bbb3G73UHljcXpUR6LGPgXhay4L9pevqqzGeQYGIaBrocfoFF8kZGiKMRG29FKVCYtyqOtdoRCI5ooxcOTjSNv48/uViHbjIBulIQoG4bhCe5a8Q0jqdFmwKxExWGtpmSPRo2P8x2m0Cg28mJSQghxNrjpppto2bIlHTp0ICYmhuzsbCtP9ZFHHqFNmzZnuIWnTo0CmQ4dOvDee+/x+uuvs2fPHlq2bMntt9/OJZdcEnTcF198QUJCwknPjxc1V14LpkLNGF+pXwOCohXdd6C1ArZVkqXCEFIYjzQ26yBMyR8eUdsyXW1Jt5dXfgwaAgr8QlVQvMELRVqjXjJuJIQQIe69917WrFlDZmYmRUVFxMfH079/f0aNGlWjJQ/qoxrPm+3RowdvvPFGlcdMnz79hBskTpJh/ScomCnfEFyw36ohE9h7UyFusQKa4CW1Ld3seyNq2ttFV1a6zxOdjFa0z/xCVUFXQgY+wwYxEtcIIQTjx49n/PjxZ7oZZ4SspnfWCQhiAgIQf2xS6vIElezXdf8SBWYw4w9zYqNtNI5zYNdUom3mtG2lrCDwFparYn46oZbaVfNC84ouJS91cPk7UOy+dqjcf0UC91zmSxqXoEUIIUQFJ1zJ7N///jfr168nNzeXsrKyoGzpvXv34na7SU1NPeGGOZ1OZs+eTUZGBrm5uSQlJTFgwAAefPBBkpKSIrrGkSNHWLFiBStXruSnn34iLy+PuLg4unbtyq233sqQIUNOuH11lb/TxKrka/gnY5uBilc3yns2jPKgx8qn8e2yaxpqjB08XhJi7WY1YN1rBT2BopUTK3jk0AAv7PC0YKCtfH0RRVWs+dZNE2x4DQVD9xJlD/12ldhGCCEathoHMjk5OTz66KNs3rwZ8JW7V5SgQObNN99kwYIFvP/++/Tr16/GjXI6nYwaNYotW7bQpk0bBg0axM6dO/nwww9Zs2YNn3zyCcnJydVeZ8qUKXzxxRfYbDa6d+9Ov379yM3N5YcffmDdunWMHDmSSZMm1bh9dVXwwo8Bw0C+QMU/FTsc3XdyaGCgmIm+eK0eHm8EtWO8tlh+dp9Dd2Vntcfe0rt86rczvrU5W0lRyxeD5FQvTSmEEKK+qtHQUl5eHqNHjyYzM5Nu3bpx//33h1T6Bbj++usxDIOvvvrqhBo1a9YstmzZwuDBg1m6dCkzZsxg8eLFjB49mn379oWdKx9OYmIiEyZM4JtvvmHBggVMnz6d+fPns2DBAuLj45k/fz6rV68+oTbWdRWTfQ1Cp2FDeUrv+6v2snX3MQD2Hy7kfzLyKCouBQwy97mZ+lUButcDho4ewWxrzVNCiRHZ7KHmCWbQciDtZo60GeBbT8kX2vj/U0nXi+T+CiFEw1ajQObvf/87Bw8eZMSIEXzyySeMGzcubM/IeeedR2xsLN9//32NG+RyuZg/fz52u51JkyZhs5V3Gj322GMkJSWRkZERdnGsip5++mn+9Kc/hQxF9ezZk3vuuQeApUuX1riNdZlhYObA+PJizCEl87UOxEaXT72Oi7ET7dAAg6JSL9/+9wA2w83uTT+QwlH2794DwOKfnBSVGbidTrT8vdiPZnFFVPXLT2SUnMduT0ql+/0xiKGoGKodbHZQNV/soqFrZkltxQpmQqOW+Bg7CTLdWgghGqwaBTIrVqwgJiaGiRMnVjsNtm3btuzdG9lslkAbN26kqKiIfv360bRp06B9DoeDAQMGoOs6a9asqfG1A/mrHEYSENUvBrFRZvBnrqPk32x20TSOL89FiYmy0TjegREw3KQpBsM9S3ms8RI++rGQmZ9vp8y3KsVLGUeY9H+HaLJzKTfEbai2JcdcdqYXDGW/J5ElJb1C9h9N6Yuh2tFtsRiqzazu6+uKUVQNA63aHpemjWOIjzk1a4gIIYSo+2oUyBw8eJB27doRFRVV7bFRUVGUlYVbMblq/vLHXbt2Dbu/W7duAGRlZdX42oFycnIAIk4crg8CK7+YG4zypN9KKChB+40KkUN+yYkl8m4sO9d6/XLBtXxZ2jPkmOJG7cm/+D7QbGbBO1Uzp14Diqr61r1UUFRfnowkygghhKigRoFMdHQ0BQUFER178ODBE1qk6sCBAwC0aNEi7P7mzZsHHXci3G438+fPB2DAgAEnfJ26yD+c5H8dtKMSekCEUOYOXjspzbafEbHratwObwTfWtY0cP+okaqhKgqq4g9kAtZSIvBrIYQQwlSjQCYtLY2DBw+ye/fuKo/bvHkzubm5dO/evcYNKikxFxH0L0pZUWxsLADFxcU1vrbfzJkz+eWXX+jWrRtXX331CV+nLrNqwxCmpyaAVnwoaDr11j3Bgeq4Rsu5JHpHhStXT48okIGj+WXkF3tAtaGggqqZi0OqKnabhuHwLUlgrRV5whUDhBBCnIVq9FPhd7/7HRs2bOAvf/kLs2fPDloo0i8vL4+nnnoKRVHCrqx5pi1ZsoQ333yThIQEXn31VVT1xGoCJieHvvcTEemqqJEwNA1dNRd2dHrB0M3aMbExOqoKXh2aJMYFnaO7FYpjowAzgPzi+338porRtkj7RLxG8OfaopHGwYLg3p64+GhcigMUldgmjVC0UnNkyR6FpkDLFo3QjeboR3PQ3aVEt06nWYszv6J6bT4zcXrIM6uf5LmJSFQZyAwaNIiePXtaSw7ceOONfPHFF6xfv57hw4czZMgQjhw5AsBHH33Ezp07+eKLL8jPz+fKK69k8ODBVV0+LH+Pi9PpDLvf32MTFxcXdn9V1q1bx+OPP47D4WDWrFm0b9++xtfwO3q0qNKaLJFKSUng8OHCk7pGoKP5To4XlOItc5OfX4LuNQvceby+dZZ0g5gKT1wtLKG4KPJcJiXCHhlvhZAn3GdVUuLChbkYpNdeis3pRsVAtyloChw77uSYXoJS4ARPGSV5xcRqZ3Z4qbafmTj15JnVT2fTc5OA7NSqMpDZt29fUK6KqqrMnj2bSZMmsXjxYt555x1r33PPPWcNUQwfPpwXXnjhhBrUsmVLwMyxCSc3NzfouEht3ryZ++67D13XmT59OhdccMEJta9OMwISYg3QDbBWI/Dt2J59DFBIb5sIQGGpzrrdrogun6A4GREXWb5MSI5M2PhDCXqlYObGmEV9rdUrzUrFUhVPCCFEGDVOOIiLi+PVV1/l3nvv5csvvyQrK4vCwkJiY2Pp2LEjgwcPrnTGUSTS09MBrOXHK9qyZQtg5utEaufOndx9992UlJTw4osvctVVV51w++oH8ye+Dqi+Ar9mMTyDBSt+AeDZ37fDsEfz+b8L2XXEE9E1b437jm6OfRG1oOLQUqBhXaPYcchl5sPooCgqKKAqii+QMXtpFEUlOkoDm4ZuqNgcWqXXFEII0TCdcOZkp06d6NSpU222BYC+ffsSHx/Phg0bOHr0aFDBPZfLxcqVK1FVlcsvvzyi6+3du5c777yT48eP8+ijj3LTTTfVepvrGn8l38DVqiuuj+RyFrN+23HKPJF1cygY2BRv9Qf6VDVrqWMzO52bGqD4i98FFvBVMBwxeONTUJs2prmi4DViMdwqtsTwCeBCCCEarjq3+rXD4WDkyJG43W6ee+45PJ7y3oKpU6eSl5fHsGHDaNasmbV92rRpXH311cybNy/oWkePHuWuu+4iNzeXu+++m7vvvvu0vY8zJagmTIV6eIGW/VTE8p/y2X88kt4YM9DQq+hlqSiwR6ZRFAzpGkPXc5twUWpUeTFF/zoEAV8r/j+qUm3RRSGEEKJOzmW97777WLt2LcuWLWPo0KF0796dnTt3kpWVRatWrZg4cWLQ8YcPH2bXrl0cO3YsaPszzzzD7t27iYmJ4ciRIzzxxBMh92rfvr21XMHZwShfY8kwyCtwUVDi4pzkODPp12dTdmmNrupQPOhVzFny2GKwecoTtAN7ZP48IBrd7iC1SwdcB3bidAVHVUrAC8VfU0aCGCGEEBGoNpD597//TZcuXU7o4oqiVJrrUpWYmBjmzZvH7NmzycjI4KuvvqJJkybccsstjB8/PuJqvP7ifU6nk4ULF4Y95oILLjhrApmiUjcGUOJbU8Aw4MOvzRowf/xtFzJ/OWIdG8mEKwflVX1/F7OxykDGHdMUW2GO9XVgIOM1FAqKXcQnY/a64A1I5jUCAhcVVMVaNFIIIYSoTrWBTMXcitMlNjaWRx55hEceeaTaY6dMmcKUKVNCtr///vunoml1lj84MQzYc6CApo3Lc0qaNooiK/t4ja4XONU6US3GXcW3izeqMQQEMoe95nTDLi0cuA0NtydgyWxfEGOoKniN8pWuUaxcGbtW50Y9hRBC1EHVBjJpaWk8/fTTp6Mt4iQYvkUhDQOKnG6++G4PrZqW19pZlXmAQmfN1k1SlfJApp3tCHFq+Gnar+UPZWSPZhgtuvD6DxDlLWS3x8xhMmMWFQU9+KTAQEVRwdcLo2kaHkCTQEYIIUQEqg1kEhISzs6aK2eZwMUh9x8xl284FlDobtueY2HOCtVSKz+uve2w9bqyIAZgjzcFVbNh2BoxdpCdZzICZhcp/oUoy9dNMnteNCsPxj+ypKBIrRghhBA1Ir/2niXcbt0aBvx8za8h+48XRVb07onGX1iv70lYUe3xLsOs7aKoqjVkFEhRFFACFnz0RS2G6gtoFAVV8a147Ts97GwlyZkRQggRRp2ctSRq7nhxGV698sUhT4VPii9gbZlZwBBNxdB8CbsB1PLuFgDccc1x2cGGC0W1Yy/ajxoVbR6iSrQihBCiZqRH5izi1Q2Mk1z/qSb+Y6RZPS2aL88FRSE1OTD/xddj4y/MpzpQNQ3DHoths5vVfDUb5YsUQPjuFwlyhBBChJIembOEWbDu9KaYTLgiBpeuYWg2NA1fJWG47fxoNuyDJf8tCZhGHRCI+OvgoaCqKqqqmGspETo0JYQQQlSlykBm27Ztp6sdohboXj1oeKakNLKqvScqSndij2qEoWkYGCi6jqGoaKrKOYlmr0zb5CjK1yAwqUp51V5NBRQFFf/kJUViGSGEEBGTHpmzRHSUDa9hoLsjXw+popAp0tWeoPr++GYbBfSqtG5i58Hr0mmcEE1ZmRu3txDADHgUpXwQSVGwaSpuRyN0I5rmTRPYm1cW7mYn/L6EEEKcvSRH5ixiAL/uLzzh8201DWQCZypZM5bKXycmJqBodgzNgR7VyDqtYkgSH2MHRcUeE48jJhZFlVWuhRBCREYCmbOI7tXZd6So2uOaJzq4rIMjZLum1CyQMRQbhj/JN2h6NVWulRQ4/CVLEQghhDgZEsjUU2UVhpC8Hp1Z/7eFzb/kVXtu68YqHZJDH71W4x6Z8qJ2KP7r+SIZNfj6/njF7dF9tWUUa7VrCA5oJLgRQggRKQlk6qkDR4spdZUn87q8kQch17XcR1LBzwB0sOUyM+k9WmrHahzImL0x5vCS4YizKvgaiooe2zTsOWUuHdUXx6hWTk3g35DcKJom8VE1aosQQoiGSQKZesq3tFL51zWoH9MoawntDn0DGDzYaBkA4xK+IlEtqWEryoMPPS4gcFEUDHtM8FEVellURUFVA1a5DtifEOsgylExT0a6aYQQQoSSQKaeqhi26BEGMrGO8oDgQscv1msVnUcaZ9SwFRWDC3+ib/C3VWDL/D0xigKaL1fGV35GCCGEqDEJZOo5j29IyWtEFsikNy9P8h0Z/531uqpFISsVrjslaDt4PLrVc6TrhlVDRlXApqkR58OEXX9JCCFEgyeBTH3lCw72Hi7Cq+t4vJEFMsN6xFd7jKfROTVri80e3DRHrPX60HEnx32rcB/MKynvtEFBC1ibKTE+isbxoTOpANTYRJTYxjVrkxBCiAZBApl6ywxc/LkykQwtDegch8NW/XGeRq0ia4Kvk8Sb0LJ8g+ZAj0uxDlGtWU0Kim+1a3O2EkEJvU0SooiLDg6IrNtEx6PGNYmsTUIIIRoUCWTqOf+wjVevfsaRYRDRYkx6VCN0R0IEd1dC50pX+DpwSMi/+kDFb7rTu2a3EEKIs4kEMvWIYRjW3+UpMeYLj6f6QMajGyiuqiv/uhPb4klKpfC8WylrcZ61vbTdJaHtCQlisFbDtjYF1MkzZyqZX1WWXiOEEELUhAQy9cie3EKKS93syS0PRgwD9mfnUHwop9LzYuxmtODVDaIO/lTlPbyxTc3idvYoylr1Kd8eFa6HJsyspZBDgle99g8r+bc7omKIjqs+b0cIIYQIRwKZesQwzMq4gROUdMNA97iYvbLyir4Du8RZ56NXs6ikagNFNYvdqRruxHa+HQplLXpU08LQlasD6/2iKNh8tWP8h7Vo34H4xonVXFcIIYQITwKZesQfv/gTfM0/BsdLqg5OereLoV9bO1ekxaAYVR9rqKo5ZOT7o0ebs4UMewxl7S4h/5L7yw+uJj8G/NV7fa/LD7QCGyGEEOJk2M50A8SJKM/a1XUDTQ0fj3a276OJWoxda8a13R0YNiLokdEoT8lVKGtzAZ4m5+JNOAfQfcsSBKpmeKniGkpG+fRrIYQQ4mRJIFOfGHC8qCwo0Vc3oLJllsYmfA3Acf0SFEM3k4V1T/iDfTyNWpk5Mqrq65Wx4WnSztf9Y66tVJx2NVrxUfMEW0DtF9WGYSufUl1Q7AqatWTXVHQMf2ePEEIIcdJkaKleKZ+tFDjM5K5uxpLhAcNA0b3VDi2hRZmzj2zRGIqCoVDeC6OayS3ulHTK2l4IgDe+mXWqt3ErjOjywnVOl8es5OubpeSwq6gotE6R5F4hhBC1Q3pk6hHDwAwyDMMaXDIMo9IeGT9F18HQidv6T2z5lc9uMg82V7A2qYBu5cuUr3btv2H13Sr+CUoKmItE+pN9Q/OChRBCiBqTHpn6xoCDx0rIyjkO4BtaqiaS0d04Dvy3+iAGAMVXH8Ywh5h8+TKBCcBmMBNZcxV/tq+Ctc6SmeorYYwQQoiTJz0y9Y7B20u2AdA9NQlHwV4MlwcNL160sGdE7/meqIOZVV61tFVfbIUHyntkDHyvNfNvQwHFlwhcg2p2qqJg08x8G1VVUai+cJ8QQggRKemRqUf8U679DhwtJudYGef9/Ffuil9lbY+tsGRRdUEMQGnqZTg7DDK/8AUqhqJiqCreRudgdqloeBu3wpvQnIh6VHwzlBx2zUyvUcrjJOmQEUIIURukR6Ze8SXJ+Pzji58BuCgJujn2AdDP8Qupjjw+cfer4bWDh4uUwHtpdl/wofqmZ9fwyqoCemAVGYljhBBC1A4JZOqJvIJS9h4uxlahZoxaYahmdPxaAD6jb81u4O8q8efAYJjRhhGY+BvunMr5O49Uc6Gl8nWXAhdgEkIIIU6CBDL1xJN//z7sNGs75XVhzrUdCrs9EoYBRaVe4mJt5gwlVILq1imqL/nXd3wEeTKGXh7rWEsVKOV/q1JMRgghxEmSHJl6orJaMQ6lvC5MnOKyXk9N+qhm149OxOVVrGDFcMT69vgWnGzcCm9cSsAZEUy9tjpeFNSA1wCtmsYRFy1xtBBCiJMjgUw9Z1fKe16MKo4LpyRtSPm5tijfFGsVNH+AEZDNomph8mOqDmbMqdb+IsGKb5uv3TYtqOqvEEIIcSIkkKmHktVCHLgBsFPeIxOrlFV6jiu5U9DXuj3WXI7AoqKgYFhrLUHwVOtKVLHfv6aSoijVXkYIIYQ4ERLI1EGGYaA7C4K+DvRM4kLuTVgBgD1gaMmf6BuOf0kBP3dS+6CcF6+OLw9Gw+1VKHK6cHt0PF6d0jIPxaVudN2gpNQMoJxlXkrKPBQUuzhy3ImzzEOx043bYx6fX+iyAhg1YHElCWiEEELUpjqbpOB0Opk9ezYZGRnk5uaSlJTEgAEDePDBB0lKSqrRtfbu3cvMmTP57rvvKCwspHXr1lx33XXceeed2Gx18CPwutELj6LGNALA4y0PZK6P/RGAjvZcZia9R4EeHdk1A4IW3RFPWbuLMdTyBR/zS9zEqWBodvKJB1cZrmI3RmJLdKcHt0dHjzUoKvEQmxRFSakHb0IyLqcHRYHjhWZ+jqZ58HoNYqI1tICgxbq7RDJCCCFqUZ3skXE6nYwaNYo5c+YAMGjQIBISEvjwww/5/e9/z9GjRyO+1i+//MINN9zAokWLOOeccxg4cCAFBQVMmzaN+++/H7268v5nTPiMlyujfw76upFaGtnVVBu6albKK0kbiqGas5OK04eSk36L7ygFPSoBXbVbxesM1YGq+NZJQkHTzB2KqmLYYlBVBVU1tyu+daBUVUFVVDTN/PZSAtZvUmXOtRBCiFpUJwOZWbNmsWXLFgYPHszSpUuZMWMGixcvZvTo0ezbt4/JkydHfK2nnnqK/Px8nnzyST755BNmzJjBsmXL6N27NytXruSTTz45he/kJBjlAZbLXc2K1dVwRTUB1WatYq1HxfmWGzBXsvbENCWwhowZxBi+oSElKFXGP2VaURVURfEFLYq1jhKGGdBoqoKmqcHlaTATf4UQQojaUud+rLhcLubPn4/dbmfSpElBQz+PPfYYSUlJZGRkcOjQoSquYvrPf/7Dpk2b6Ny5M7fffru1PS4ujr/85S8AvPPOO7X9Fk6K26NT5vKCYVBSVETh8eM8MPMbILT4XXWKm5/H7vQ/cKDTjRS7DErjWpr3QMNjmHkxxaU6Cgoe3UBBpcwLbo/ZG6QoUObyovgCleJSN6oKBUVlKKigqWiqGZyYM5PAoPxcmwrlE67xbZceGSGEELWnzgUyGzdupKioiH79+tG0adOgfQ6HgwEDBqDrOmvWrKn2WqtXrwZgyJAhIfu6detGmzZt+PXXX8nJiWRV6NPj4NFijv6wCFf2Zg7s2sXB7N201Y5wS+x3JChVDyOVRSdzpPWV7O9wPUdaXMSx+I4YWhSO6GhKPZDXdhD7z72W46Uqx4s8HC92U+rW0WMTUVBQNAVnmRdFVTDscXijk2iSEIWmKKgqaKpKbJQNRdVQ45NQMbcpikJslB3NP8ykKjhsGqpqLnuQkhhjrrlk02jaOMKcHiGEECICdS7Tdfv27QB07do17P5u3brx2WefkZWVVe21tm3bZp0TTteuXcnJyWH79u20adPmBFtcOwxd58CPy5m0UmNm0r/wAsuLLuGgtzH3JqwgXi0jRSuo9Pyi5C4cT+qBrtmx21SKbR2xKTpodjRVRdU0UGx445uhoKKoKhgGRlQCelQjNLeOpmqoqoJNBVWz41Hi0TQFVVdRDANNw5cPo0JsY1SPWbpXBTRNwW7TwOPFrqnmEJIOoBAfY+fQMTPASYh1VPoehBBCiJqqc4HMgQMHAGjRokXY/c2bNw86rioHDx4MOqci/z0iuVZt83q8lDmduErNXhbvthUkZH5MX0d/65iR8d8BcMxrVtntaA8/nPZW4RUM7t0NpbQQQ9ex2Ry4PG4Um4KqaSiaimazoQJeDPAq5liQoWO3aXj8uS6aOT5kU1VsmoKum0NKNhW8Vr6MguYrcKfr5vLWum7mzjhsCrqhYLOZ1wmYdS3rKgkhhDgl6lwgU1JSAkBMTEzY/bGx5g/14uLik76Wf3sk16ptv859ghbG4ZDtt8V/G7KtiVbCYW8CKVphyL6D3sbstbXFbUCM4cbQQbPZUFQdVTFQNBWbpuLRbGYOi6KYvSsqeHUVu13FC9g0BVVVfUNMqjlMpChmUq+mongM8xgA1UziVdXyxF9FNXtkPF4Dh03FXPC6PHqROEYIIcSpUOcCmfoiOTn+pM7f3Xs4Wb9uC9qWVLqXbKMlzW0FxJYewmVo5BtxFDqaUZTQjoSyQ7Qt+ZmE9t1wNGuPvn8LBfZO3NeyNcnxKh5vojljCIU4rxnIoGho0XHEGwa4nWCAV7WjahqGakeLSbCmZaslHpLjmuBFs3pcEhOiKCxxWb0vKY2jKSwxC995A6aux8c6cJZ68Oo6mqqS0iSGvIJS4qLtpCTFojpsxETZiI22n9TnVhekpCSc6SaIGpJnVj/JcxORqHOBjL/Hxel0ht3v72WJi4s76Wv5t0dyrYqOHi0yf7ifoHP7Xc75Q6/h8OHgXpbUmlykW3/6nnALwvDNagqi6yQGLO7odXmItSlg04DAtZcMHNHl21xOF/F2Fbxe6z0WuzwUF0ZW96auSklJCHlmom6TZ1Y/nU3PTQKyU6vOzVpq2dL8YerPb6koNzc36Liq+HNg/OdU5L9HJNcSQgghRN1T5wKZ9PR0ALZu3Rp2/5YtWwBIS0ur9lqdO3cOOqci/z389xRCCCFE/VLnApm+ffsSHx/Phg0bQpYicLlcrFy5ElVVufzyy6u91hVXXAHAsmXLQvZt3bqVnJwc2rdvf8anXgshhBDixNS5QMbhcDBy5EjcbjfPPfccHo/H2jd16lTy8vIYNmwYzZo1s7ZPmzaNq6++mnnz5gVdq1evXvTq1Ytt27YFVfAtKSnh+eefBwiq+CuEEEKI+qXOJfsC3Hfffaxdu5Zly5YxdOhQunfvzs6dO8nKyqJVq1ZMnDgx6PjDhw+za9cujh07FnKtyZMnc+utt/LSSy+RkZHBOeecw4YNGzh8+DBXXnklN9100+l6W0IIIYSoZXWuRwbM+i7z5s3jnnvuQdd1vvrqK44fP84tt9zCp59+SnJycsTX6tChA59//jnDhw9n7969fP311yQkJPDII4/w+uuvm2X0hRBCCFEvKYZhnPgc4gbsZKdfw9k1vbChkGdW/8gzq5/Opucm069PLemOEEIIIUS9VSdzZOoDVa2dovu1dR1x+sgzq3/kmdVP8txEJGRoSQghhBD1lgwtCSGEEKLekkBGCCGEEPWWBDJCCCGEqLckkBFCCCFEvSWBjBBCCCHqLQlkhBBCCFFvSSAjhBBCiHpLAhkhhBBC1FsSyAghhBCi3pIlCk4Rp9PJ7NmzycjIIDc3l6SkJAYMGMCDDz5IUlJS0LHp6em0atWKFStWnKHWNgw//fQT3333HZs3byYzM5NDhw4RGxvLpk2bKj3H4/Ewd+5cFi5cyN69e0lISOCSSy5h/PjxtG7dOuT4gQMHsm/fPrZv334q30qD4XQ6+fbbb1mxYgUbN25k//792O12UlNTueaaaxg9ejQOhyPkPHluQjQcEsicAk6nk1GjRrFlyxbatGnDoEGD2LlzJx9++CFr1qzhk08+ITk5+Uw3s8GZNWsWX3/9dcTH67rO/fffz8qVK0lJSbF+2C1atIjVq1fz0Ucf0b59+1PYYrF48WKefvppADp06MCgQYMoKipi06ZNTJ06lWXLljF37lzi4uKsc+S5CdGwSCBzCsyaNYstW7YwePBgpk+fjs1mfswvvvgi77//PpMnT2batGlnuJUNT69evejcuTM9evSgR48eXHrppVUe//HHH7Ny5Up69+7NW2+9Zf2wnDt3LlOmTGHixIl89NFHp6PpDZbNZmPEiBGMGTOGDh06WNsPHTrEvffeS2ZmJrNmzeLPf/6ztU+emxANiywaWctcLhcXX3wxZWVlrFq1iqZNmwbtu+KKKzh+/DirV6+mWbNmgAwtnSnp6elVDi1dffXV7Nq1i4ULF9K1a9egfddeey3bt2/n448/5rzzzrO2yxDF6bNp0yZuueWWkP/vyHMTomGRZN9atnHjRoqKiujXr19QEAPgcDgYMGAAuq6zZs2aaq/13//+lwsvvJBevXrx7bffnqomizBycnLYtWsXbdu2DflhCDBkyBAAVq1aFfH1Bg0aRNeuXVm4cGFtNrXB6ty5M2D2zvjJcxOi4ZFAppb5f6ML948oQLdu3QDIysqq8jo//PADY8aMwev18tZbb9G/f//abaio0rZt24CTf47+Y2699VZyc3OZMWMG119/fe01tAHLyckBCMo3k+cmRMMjOTK17MCBAwC0aNEi7P7mzZsHHRfOihUreOihh4iPj+ett96iS5cutd9QUaXqnqN/e1XPESAzM5N77rkHl8vFnDlzqs3LEZF77733ABgwYIC1TZ6bEA2P9MjUspKSEgBiYmLC7o+NjQWguLg47P5FixbxwAMPkJSUxLx58ySIOUP8zzE6Ojrsfv/zrew5Aqxbt47bb78dXdd566235IdhLVq9ejWffvop8fHx3HvvvdZ2eW5CNDwSyNQh8+fP57HHHqN169bMnz9fpojWY8uXL+eee+4hJiaG9957jz59+pzpJp01fvnlF/785z9jGAbPP/88LVu2rLVry3MTov6RoaVa5u9xcTqdYff7f2MMrHsBcPDgQZ577jnrH1D/EJQ4M/zPsbS0NOx+//Ot+Bz9xo8fj9fr5R//+If0qtWi3Nxc/vjHP5Kfn8+jjz7KNddcE7RfnpsQDY/0yNQy/2+HBw8eDLs/Nzc36Di/pKQkLrroIpxOJ1OnTkXX9VPbUFGl6p6jf3tlvQHDhg3DMAymTJlS6Q9VUTPHjx/nzjvvZP/+/dxxxx3cfffdIcfIcxOi4ZFAppalp6cDsHXr1rD7t2zZAkBaWlrQdofDwRtvvMH555/P4sWLmThxIlLi58zxT+2t6XP0mzJlCsOGDWP9+vXcd999uFyuU9PQBqK4uJi7776bnTt38rvf/Y7HH3887HHy3IRoeCSQqWV9+/YlPj6eDRs2cPTo0aB9LpeLlStXoqoql19+eci5MTExzJkzh969e7Nw4UKeeeYZCWbOkDZt2pCamkp2djY///xzyP5ly5YBcOWVV4Y9X9M0XnnlFQYPHszatWt54IEH5IfiCXK5XNx3331s3ryZAQMGMHnyZBRFCXusPDchGh4JZGqZw+Fg5MiRuN1unnvuOTwej7Vv6tSp5OXlMWzYMKuqb0VxcXG8+eab9OzZk48//pgXX3zxdDVdVHD77bcD8Nxzz1m5TWCWut++fTu9e/cOqg5bkc1m47XXXmPgwIGsWrWKCRMmBH0/iOp5vV4efvhhvv/+ey644AJmzpxpLflRGXluQjQsskTBKRC4aGTbtm3p3r07O3fuJCsri1atWoUsGhluiYKCggJuv/12tmzZwh133METTzxxJt7KWWXVqlXMmjXL+jozMxNVVenRo4e1bdKkSVbRNF3XGTt2LKtWrSIlJYV+/fqxf/9+MjMzady4MR9++GHQ+j8QvtS9y+Vi3LhxrFmzhqFDhzJt2jQ0TTvF7/bs8O677zJ58mQABg8eXGmS7pQpU6zX8tyEaFhk1tIpEBMTw7x585g9ezYZGRl89dVXNGnShFtuuYXx48eTlJRU7TUaNWrE3LlzGTNmDHPnzsXhcPDwww+fhtafvfLy8sjMzAzaput60LaioiLrtaqqvP7667z99tssXLiQr7/+moSEBIYPH85DDz1E69atI7qvw+Hg9ddfZ+zYsfzrX//CZrMxdepUVFU6RKtTUFBgvf7yyy8rPS4wkJHnJkTDIj0yQgghhKi35FcLIYQQQtRbEsgIIYQQot6SQEYIIYQQ9ZYEMkIIIYSotySQEUIIIUS9JYGMEEIIIeotCWSEEEIIUW9JICNELfrhhx9IT09n9OjRtXbNgQMHkp6ezt69e2vtmmeDJ554gvT0dD7//PMz3ZSwRo8eTXp6Oj/88MOZbooQZzWp7CvqPf+K4zVRcUkIcfIifQ4vvfQSN9xwwylujRCioZBARtR7ffr0CdlWVFREVlZWpftTUlJOSVtiYmJITU2lZcuWtXbNNm3a4HA4sNvttXbNUyktLY34+PhK9weuM3YyUlJSSE1NJSEhoVauJ4Son2SJAnFW+uGHH7jtttsAghYCFKeOv0fmvffe48ILLzzDrTnzRo8ezfr16+XzEOIUkxwZIYQQQtRbEsiIBufzzz8nPT2dJ554AqfTyfTp07n66qvp2bMnv/vd76zjMjMzeeWVV7jxxhu59NJL6d69O5dddhnjx49n8+bNYa9dWbLv3r17SU9PZ+DAgQAsWbKEm266id69e9OvXz/uvfdetm3bFvaalSX7BiaT7t69m4cffpiLL76YHj16MHz4cBYsWFDpZ+DxeHj77be55ppr6NmzJ/379+fPf/4zOTk5QZ/P6ZCenm715mRkZDBixAjrc/njH/8YsmK5X1XJvosWLWL06NFccMEFdOvWjYsvvpjhw4fz/PPPs2PHjpDjPR4P8+fPZ8SIEfTt25eePXsybNgwpk+fHrQCd0W5ubk8+eSTXHrppfTs2ZOhQ4cyZ84cPB5Pte973bp1jBs3zvre6t+/PxMmTKj0++D48eO88sorDBs2jJ49e9KzZ0+uvPJKRo8ezZw5c3C5XNXeU4izkeTIiAartLSUUaNGsWXLFlJTU+nYsWNQHsqjjz5KdnY2iYmJpKSk0KxZM/bv38/SpUtZvnw5r732GkOGDKnxfadPn84bb7xBixYtOPfcc9m1axerVq1iw4YNfPrpp6Smptboelu3bmXs2LEYhkFqaiqHDh0iKyuLZ555hvz8fO65556g471eLw888ICV7NyuXTvi4+NZunQpa9asYeTIkTV+T7Xh7bff5uWXXyY5OZn27duTnZ3NN998w7p165g5cya/+c1vIrrO1KlTeeuttwAzj6ZNmzYUFRWRnZ1NVlYWrVq1olOnTtbxZWVljB07lrVr1wJw7rnnEhsby44dO3jjjTdYvHgx7777Lq1btw66z549exg5ciRHjhzBbreTlpZGQUEBr732GpmZmVQ1av/yyy/z9ttvA9CkSRM6derEvn37yMjIYPny5UyfPj3o/RYVFXHzzTezZ88eVFWlXbt2xMXFcejQITZs2MD69eu56aabSEpKiuzDFuJsYghxFvr++++NtLQ0Iy0tLWTfZ599ZqSlpRldunQxfvOb3xjbt2+39jmdTuv1woULjd27dwed6/V6jS+//NLo1auX0a9fP6OoqCjsff/whz8Ebc/JyTHS0tKMrl27Gr169TKWL19u7SsoKDD+8Ic/GGlpacbDDz8c0t4BAwYYaWlpRk5OTtB2/zndunUznnnmGaOkpMTa98477xhpaWlGz549jYKCgqDz/Pv69u1rrFu3ztp+9OhR47bbbjO6detmpKWlGY8//nhIW6ri/7y///77EzqvW7duxptvvml4vV7DMAyjrKzMeOGFF6y2Hjp0KOi8xx9/3EhLSzM+++yzoPfQpUsXo2vXrsZXX30VdLzb7TZWrlwZ0r6XX37ZSEtLMy6++GJj06ZN1vaDBw8aN998s5GWlmaMGDEi6Bxd142bbrrJSEtLM0aNGmUcPnzY2vfNN98YvXr1sj7Hivf7+OOPjbS0NOPyyy83Vq9eHbTvww8/NLp06WL06dMn6P2+/fbbRlpamnHttdcaBw4cCDrn6NGjxrvvvhvyvShEQyFDS6LB8nq9vPbaa6SlpVnboqOjrdfXXXcd7dq1CzpHVVWuuuoqxowZQ0FBAatWrarRPT0eD+PGjWPQoEHWtoSEBJ566ikAVq9eXeP3kZqayqRJk4iJibG2jRkzhi5dulBaWhpUx0TXdebOnQuYQzMXXXSRtS8pKYmZM2cGfQYn4rbbbrOGi8L9qcxll13GXXfdhaqa/yw5HA6eeuop0tLSKCws5KOPPqr23tnZ2Xi9XtLS0kJ6cGw2G1deeWVQ4m1RUREffvghAE8//TS9evWy9jVv3pzp06djs9nYtGlT0Oe4fv16MjMzsdvtTJs2jaZNm1r7+vfvz/3334/b7Q5pn9vtZubMmSiKwv/+7/9y+eWXB+2/5ZZbGD16NEVFRXzyySfW9l27dgFw44030qJFi6BzkpKSuO2224iLi6v28xHibCSBjGiwOnXqRI8ePao8Zs+ePbz++us8+OCDjB49mltvvZVbb72Vf/3rXwD8/PPPNb7viBEjQrZ17tyZqKgoCgsLOXbsWI2u9/vf/9764R+oZ8+egPnD3e/XX3/lwIEDREdHM3z48JBzEhMTIx7CqUxaWhp9+vSp9E9lRo0aFbJNURRrqOvbb7+t9t7+ae+7d++uNNck0MaNGykpKaFZs2ZhhwnPOecc6/P45ptvrO1r1qwBYPDgwTRv3jzkvBEjRoSdLv+f//yHw4cP07lzZ84777ywbfLfb/369SHva/Xq1TidzmrflxANieTIiAarffv2Ve5/++23mTZtWpWJm8ePH6/RPZs0aVJp3ZOkpCQOHDhASUkJTZo0ifiaFXuN/Pz1WkpKSqxtu3fvBsw8kKioqLDnde7cOeJ7h/P000+f0HTjDh06hN3esWNHoLxXoirNmzdn2LBhZGRkcP3119OnTx8uvPBC+vbtS9++fUN6m/zXTE1NRdO0sNfs1KkTS5cutT67wPMqa3N8fDzNmzcPSdD21zY6ePAgt956a9hzy8rKADOR2O/GG29k7ty5fPvtt1x22WVcdtll9OvXj/PPPz+oR1GIhkgCGdFgxcbGVrpv48aNvPzyy2iaxkMPPcSgQYNo1aoVsbGxKIrCp59+ylNPPRXR7JRI7+nvVTFqWNopcEipuusVFxcDVDkMcaaGKCorlOff7m97dV5++WU6duzIp59+yoYNG9iwYQNgvq9bbrmF8ePHW0GcP8gLHBqK5P7+86oq7te0adOQQMY/A+rYsWPV9rz5AxqAZs2asWDBAmbOnMnKlSvJyMggIyMDMIOpRx55JGi4UoiGRAIZIcL45z//CcAdd9zB2LFjQ/bn5+ef7ibVCn+QUlVQEGnAUNvy8vJC8j8Ajh49CkQeYDkcDsaNG8e4cePYs2cPGzZs4JtvvmH58uW89dZbFBUV8fzzzwPlgeWRI0cqvV64+/vP8+8LJ9w1/ecNHTqUGTNmRPR+/FJTU5kxYwYul4vNmzezYcMGli1bxtatW7n//vv54IMPqhy6E+JsJTkyQoSxb98+APr27Rt2f2W1Teq6c889FzCHmCqrO3KmKiH/8ssvVW73t70m2rVrx4033siMGTN4/fXXAbOOkL8nzT/V/ddff8Xr9Ya9hr/uTOD9/edV1uaioqKgoSE//7TvnTt31vi9+DkcDvr168ef/vQnFi5cyNChQ9F1nU8//fSErylEfSaBjBBh+Icewv1WnZ2dzcqVK093k2pFhw4dOOeccygtLWXx4sUh+/Pz81m+fPkZaBnMnz8/7PYPPvgAMGc1nQx/b4Xb7bZym/r27UtsbCyHDx9m2bJlIeccOHCAr7/+OuT+/tdffvklhw4dCjnv448/DjtrqW/fviQnJ7Njxw6rbs3J6t27N0DYdgjREEggI0QY/fr1A2DOnDlBs3527tzJn/70JxRFOVNNOymKonDHHXcA5irUgTNj8vLymDBhwhmbFbN69WreeecddF0HwOVyMXnyZLKysoiPjw8726uidevWMWXKlJBepbKyMmbNmgWYM4D8uS3x8fHWrKj/+Z//Ceppy83NZcKECbjdbnr37h2UwHzhhRfSo0cP3G43jzzySNAQ03fffcff/va3sLOWoqKimDBhAgATJkxgyZIl1vv1y87OZtasWXz55ZfWttdee40FCxaEJJfv3bvXmqbdrVu3aj8fIc5GkiMjRBg333wzH330EXv27GHYsGGkpqai6zq//PILKSkpjB07tsY5DnXFqFGjWLduHStWrGD06NGce+65xMXFsWPHDmJiYvjjH//I7Nmzw07pjsSLL75Y5erXQ4cOtRb0DPTwww/z0ksv8Y9//IOWLVuSnZ1Nfn4+mqYxefJkmjVrVu29i4uLmTt3LnPnziUxMZFWrVqh6zo5OTkUFRVht9t59tlngwLRBx98kK1bt/Ldd99x8803k5qaSkxMDDt27MDtdtO6dWteffXVoPsoisLUqVP5wx/+wPr167nyyivp1KkTRUVF7Nmzh4EDB1JYWMiPP/4Y0sabbrqJgwcP8vrrr/Pwww/z7LPP0rZtWwzD4ODBg1ZQ9Oyzz1rn7Ny5kzlz5jBp0iRat25NUlISBQUF7NmzB13X6dSpE3fddVe1n48QZyMJZIQIIz4+nvnz5/Paa6+xatUqdu3aRUpKCiNGjOCBBx6w6ojUR5qm8de//pV3332Xzz77jJycHBISEhg8eDAPPfSQ9d6qCkaq4p9iXJnu3buH3X7nnXfSokUL3n33XbKyslAUhf79+zNu3LiIk1j79u3LX/7yF9auXcuOHTvYtWsXbrebZs2aMXjwYO68886g5QnA7CX5xz/+wYIFC/jnP//Jjh078Hq9tGnThquuuoq77rqLxo0bh9yrffv2fP7558yYMYM1a9awY8cOWrduzcMPP8xdd91l9XyF88ADD3DFFVfwwQcf8OOPP1qfWYsWLbj44osZPHhwULG8sWPH0rFjR9avX8/evXvZunUrDoeDLl26MGTIEEaPHl3ljDghzmaKUdO5nkKIs9oLL7zAvHnzmDhxImPGjDnl9/NX+z1TScZCiPpNcmSEEJbi4mIr6bWyGVtCCFGXSCAjRAM0e/bskKnDBw4c4IEHHuDw4cP06NGj0iEgIYSoS2RoSYgG6KqrriI7O5sWLVrQvHlzioqK2LVrF7quk5yczHvvvWctDXCqydCSEOJkSLKvEA3Q2LFjWbp0Kdu3b7cCiHbt2nHFFVdw1113RTRDSAgh6gLpkRFCCCFEvSU5MkIIIYSotySQEUIIIUS9JYGMEEIIIeotCWSEEEIIUW9JICOEEEKIeksCGSGEEELUW/8PM7FJM53U6I0AAAAASUVORK5CYII=\",\n      \"text/plain\": [\n       \"<Figure size 515.323x360 with 1 Axes>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    },\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"read finish!\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"%matplotlib inline\\n\",\n    \"import pandas as pd\\n\",\n    \"pd_sample = pd.DataFrame(samples)\\n\",\n    \"\\n\",\n    \"import seaborn as sns\\n\",\n    \"import matplotlib\\n\",\n    \"# matplotlib.use('Agg')\\n\",\n    \"# import matplotlib.pyplot as plt;  plt.rc('font',family='Times New Roman');\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"\\n\",\n    \"# plt.ioff()\\n\",\n    \"#设置风格、尺度\\n\",\n    \"\\n\",\n    \"sns.set_style('darkgrid')\\n\",\n    \"sns.set_context('paper', font_scale=2.5)\\n\",\n    \"# plt.rc({\\n\",\n    \"#     'text.latex.unicode': True,\\n\",\n    \"#     })\\n\",\n    \"sns.set(font=\\\"Times New Roman\\\", font_scale=1.88)\\n\",\n    \"# sns.set(font=\\\"Verdana\\\")\\n\",\n    \"res = sns.relplot(data=pd_sample, x='Training Episodes', y=main_key_name_on_graph, hue=\\\"Method\\\", kind=\\\"line\\\")\\n\",\n    \"# sns.lineplot(data=pd_sample, x='Training Episodes', y='Mean Episode Rewards', hue=\\\"Method\\\")\\n\",\n    \"for ax in res.fig.axes:\\n\",\n    \"    xlabels = ['{:,.0f}'.format(x) + 'k' for x in ax.get_xticks()/1000]\\n\",\n    \"    ax.set_xticklabels(xlabels)\\n\",\n    \"\\n\",\n    \"sns.move_legend(\\n\",\n    \"    res, \\\"center left\\\",\\n\",\n    \"    bbox_to_anchor=(0.79, 0.55)\\n\",\n    \")\\n\",\n    \"# sns.move_legend(ax, \\\"upper right\\\", bbox_to_anchor=(1, 1))\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"plt.tight_layout(); \\n\",\n    \"# plt.savefig(\\n\",\n    \"#     './保存图像/IMG-1-1-new-seaborn_relplot.pdf',\\n\",\n    \"#     # bbox_inches='tight'\\n\",\n    \"#     )\\n\",\n    \"# plt.tight_layout(); \\n\",\n    \"plt.show()\\n\",\n    \"print('read finish!')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  }\n ],\n \"metadata\": {\n  \"interpreter\": {\n   \"hash\": \"916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1\"\n  },\n  \"kernelspec\": {\n   \"display_name\": \"Python 3.8.10 64-bit\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.8.10\"\n  },\n  \"orig_nbformat\": 4\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/seaborn_defaults.py",
    "content": "# %matplotlib inline\nimport os\nimport copy\nimport subprocess\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\ndef setTimesNewRomanFont_MustExecuteAtLast():\n    plt.rcParams['font.family'] = 'serif'\n    plt.rcParams['font.family'] = 'serif'\n    plt.rcParams['font.serif'] = ['Times New Roman'] # + plt.rcParams['font.serif']\n    \ndef init(font_scale):\n    sns.set_theme(\n        context='paper',    # notebook, paper, talk, poster\n        style='whitegrid',  # darkgrid, whitegrid, dark, white, ticks\n        palette='deep',     # https://seaborn.pydata.org/generated/seaborn.color_palette.html#seaborn.color_palette\n        font='sans-serif', \n        font_scale=font_scale, \n        color_codes=True, \n        rc=None)\n    setTimesNewRomanFont_MustExecuteAtLast()\n\ndef roll_color_palette(cp, offset):\n    pre = cp[:offset]\n    post = cp[offset:]\n    return post + pre\n\ndef lift_color(cp, n):\n    return [ cp[n] ] + [ c for i, c in enumerate(cp) if i!=n ]\n\ndef find_in_dict_list(dict_list, **kwargs):\n    res = None\n    for d in dict_list:\n        if all([d[k] == v for k, v in kwargs.items()]): \n            res = d\n            break\n    return res\n\ndef filter_in_dict_list(dict_list, **kwargs):\n    res = []\n    for d in dict_list:\n        if all([d[k] == v for k, v in kwargs.items()]): \n            res.append(d)\n    return res\n\ndef filter_out_dict_list(dict_list, **kwargs):\n    res = []\n    for d in dict_list:\n        if all([d[k] == v for k, v in kwargs.items()]): \n            pass\n        else:\n            res.append(d)\n    return res\n\ndef rename_key_in_dict_list(dict_list, from_what, to_what):\n    res = []\n    for d in dict_list:\n        for k, v in d.items():\n            if v == from_what: d[k] = to_what\n        res.append(d)\n    return res\n\ndef rename_query_in_dict_list(dict_list, from_what, to_what):\n    res = []\n    for d in dict_list:\n        new = copy.deepcopy(d)\n        for k, v in d.items():\n            if k == from_what: new[to_what] = new.pop(k)\n        res.append(new)\n    return res\n\n# def map_in_dict_list(dict_list, from_what, to_what):\n#     res = []\n#     for d in dict_list:\n#         for k, v in d.items():\n#             if v == from_what: d[k] = to_what\n#         res.append(d)\n#     return res\n\ndef lift_key_in_dict_list(dict_list, key):\n    res = []\n    for d in dict_list:\n        if any([v == key for k, v in d.items()]):\n            res.append(d)\n\n    for d in dict_list:\n        if not any([v == key for k, v in d.items()]):\n            res.append(d)\n    return res\n\n# 左下角为0点\ndef legend(handle, 水平位置百分比, 垂直位置百分比, 边框, 字体大小):\n    # https://stackoverflow.com/questions/39803385/what-does-a-4-element-tuple-argument-for-bbox-to-anchor-mean-in-matplotlib/39806180#39806180\n    \n    sns.move_legend(\n        handle, \"center\", frameon = 边框,\n        bbox_to_anchor=(水平位置百分比, 垂直位置百分比),\n        fontsize = 字体大小,\n        title_fontsize = 字体大小*1.2,\n    )\n\n\ndef save_and_push(handle, img_path, check_exist=True):\n    if check_exist and os.path.exists(img_path): \n        assert False, \"image already exists!\"\n    handle.savefig(img_path, bbox_inches='tight')\n    image_basename = os.path.basename(img_path)\n    subprocess.Popen([\n        'curl', '-T', img_path, '-u', 'fuqingxu:clara', \n        'http://cloud.fuqingxu.top:4080/remote.php/dav/files/fuqingxu/research2/heteGrouping/img/%s'%image_basename\n    ])\n''' ratio problem 1\n\ng = sns.pointplot(x=\"N Agents\", y=\"Average Test Reward\", hue=\"Method\", data=data, \n    aspect=2.7 , height=5, \n    capsize=.35, kind=\"bar\", palette =sns.color_palette(\"pastel\") ,legend_out=True)\n\n'''\n\n# sns.move_legend(\n#     res, \"lower left\",\n#     bbox_to_anchor=(0.68, 0.55)\n# )\n# changedNameOfImage = True\n\n\n# nameOfImage = \"ADCA-Two-Phase\"\n# path = \"./imgsave/\"\n# assert changedNameOfImage\n# plt.savefig('%s/%s.pdf'%(path,nameOfImage),bbox_inches='tight')\n\n# curl -T ./imgsave/ADCA-Two-Phase.pdf -u fuqingxu:clara http://cloud.fuqingxu.top:4080/remote.php/dav/files/fuqingxu/research/paper03_phase3/DoR-LMAS/img/ADCA-Two-Phase.pdf\n\n\n'''\n\n# %load_ext autoreload\n# %autoreload 3\nimport os, shutil, subprocess, glob, re\nimport commentjson as json\nimport seaborn as sns\nimport pandas as pd\nimport matplotlib\n\n\nnote_list = [\n    \"NoHLT-cos-run3\",\n    \"NoHLT-cos-run4\",\n    \"NoHLT-cos-run5\",\n    \"NoHLT-cos-run6\",\n    \"NoHLT-cos-run7\",\n\n    \"prob0d2-cos-run1\",\n    \"prob0d2-cos-run2\",\n    \"prob0d2-cos-run3\",\n]\n\ndata = []\nfor note_name in note_list:\n    target_json = 'ZHECKPOINT/%s/experiment_test.jsonc'%note_name\n    target_dir = 'ZHECKPOINT/%s/matrix'%note_name\n    method = note_name.split('-')[0] + '-'+ note_name.split('-')[-2]\n    print(method)\n    search_res = glob.glob(target_dir+'/*')\n    for p in (search_res):\n        base_name = os.path.basename(p)\n        res = base_name.split('_')\n        which_ckp = int(res[1].split('c')[1])\n        alive_frontier = int(res[2].split('a')[1])\n        update_cnt = int(res[3].split('m')[1])\n        # print(p)\n        with open(p,'r') as f:\n            in_line = [line for line in f.readlines() if 'agents of interest: ' in line][0]\n\n        res = re.findall(\n                re.compile(r\"recent reward (.*?), best reward (.*?), win rate (.*?)$\"), in_line\n            )[0]\n        reward = float(res[0])\n        win_rate = float(res[2])\n        data.append(\n        {\n            'note_name':note_name,\n            'method':method,\n            'which_ckp':which_ckp,\n            'alive_frontier':alive_frontier,\n            'update_cnt':update_cnt,\n            'reward':reward,\n            'win_rate':win_rate,\n            'target_dir':target_dir,\n        }\n        )\n\n    def find_in_dict(dict_list, **kwargs):\n        res = None\n        detail_debug = []\n        for d in dict_list:\n            detail_debug.append([d[k] == v for k, v in kwargs.items()])\n            if all([d[k] == v for k, v in kwargs.items()]): \n                res = d\n                break\n        return res\n    # print(data)\n    frontier_win_rate = res = find_in_dict(data, which_ckp=1, alive_frontier=3, method=method, target_dir=target_dir)['win_rate']\n    # print('frontier win rate', res['win_rate'])\n\n    for test_which_cpk in range(1,5):\n        # print('test_which_cpk', test_which_cpk, end='\\t')\n        for alive in range(3):\n            res = find_in_dict(data, which_ckp=test_which_cpk, alive_frontier=alive)\n            # print(res['win_rate'], end='\\t')\n        # print('')\n\n\n\n    for test_which_cpk in range(1,5):\n        # print('test_which_cpk', test_which_cpk, end='\\t')\n        for alive in range(3):\n            base_line = find_in_dict(data, which_ckp=test_which_cpk, alive_frontier=0, method=method, target_dir=target_dir)\n            res = find_in_dict(data, which_ckp=test_which_cpk, alive_frontier=alive, method=method, target_dir=target_dir)\n            res['baseline'] = float(base_line['win_rate'])\n            res['inc'] = (float(res['win_rate'])-float(base_line['win_rate']))# /frontier_win_rate\n            # print(res['inc'], end='\\t')\n        # print('')\n\n# %matplotlib inline\n# ! rm /home/hmp/.cache/matplotlib -rf\n\nfrom VISUALIZE.seaborn_defaults import *\ninit(font_scale=1.7)\ndata_p = filter_in_dict_list(data, alive_frontier=2)\ndata_p = rename_key_in_dict_list(data_p, from_what='NoHLT-cos', to_what='without HLT')\ndata_p = rename_key_in_dict_list(data_p, from_what='prob0d2-cos', to_what='HLT')\ndata_p = lift_key_in_dict_list(data_p, key='HLT')\n\ndata_p = pd.DataFrame(data_p)\n\n\ncp = sns.color_palette(\"husl\")\ncp = roll_color_palette(cp, offset=4)\ncp = lift_color(cp, n=1)\n\nsns.set_palette(cp)\n\ng = sns.lmplot(\n    data=data_p,\n    x=\"baseline\", y=\"inc\", hue=\"method\",\n    aspect=1.27\n)\ng.set_axis_labels(\"Past Policy Win Rate\", \"Improvement Score\")\nlegend(g, 水平位置百分比=0.323, 垂直位置百分比=0.29, 边框=True)\nplt.savefig('temp.jpg', bbox_inches='tight')\n\n\n'''\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/VISUALIZE/threejs_replay.py",
    "content": "import os, sys\nimport argparse\nfrom VISUALIZE.mcom import *\nfrom VISUALIZE.mcom_replay import RecallProcessThreejs\nfrom UTIL.network import find_free_port\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(description='HMP')\n    parser.add_argument('-f', '--file', help='Directory of chosen file', default='TEMP/v2d_logger/backup.dp.gz')\n    parser.add_argument('-p', '--port', help='The port for web server')\n    args, unknown = parser.parse_known_args()\n    if hasattr(args, 'file'):\n        path = args.file\n    else:\n        assert False, (r\"parser.add_argument('-f', '--file', help='The node name is?')\")\n\n    if hasattr(args, 'port') and args.port is not None:\n        port = int(args.port)\n    else:\n        port = find_free_port()\n        print('no --port arg, auto find:', port)\n\n    load_via_json = (hasattr(args, 'cfg') and args.cfg is not None)\n    \n    rp = RecallProcessThreejs(path, port)\n    rp.start()\n    rp.join()\n\n\n'''\n\nnote=RVE-drone1-fixaa-run2\ncp -r ./ZHECKPOINT/$note ./ZHECKPOINT/$note-bk\ncp -r ./ZHECKPOINT/$note/experiment.jsonc ./ZHECKPOINT/$note/experiment-bk.jsonc\ncp -r ./ZHECKPOINT/$note/experiment.jsonc ./ZHECKPOINT/$note/train.jsonc\ncp -r ./ZHECKPOINT/$note/experiment.jsonc ./ZHECKPOINT/$note/test.jsonc\n\npython << __EOF__\nimport commentjson as json\nfile = \"./ZHECKPOINT/$note/test.jsonc\"\nprint(file)\nwith open(file, encoding='utf8') as f:\n    json_data = json.load(f)\njson_data[\"config.py->GlobalConfig\"][\"num_threads\"] = 1\njson_data[\"config.py->GlobalConfig\"][\"fold\"] = 1\njson_data[\"config.py->GlobalConfig\"][\"test_only\"] = True\njson_data[\"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\"][\"TimeDilation\"] = 1\njson_data[\"ALGORITHM.conc_4hist_hete.foundation.py->AlgorithmConfig\"][\"load_checkpoint\"] = True\nwith open(file, 'w') as f:\n    json.dump(json_data, f, indent=4)\n__EOF__\n\npython main.py -c ./ZHECKPOINT/$note/test.jsonc\n\n\n\n\n'''"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/Dockerfile",
    "content": "# how to build: docker build --network=host -progress=plain -t hmp-from-scrach . \n\n# 此文件用于构建 HMAP + Starcraft(难易两个版本) + 虚幻引擎组件\n# 可能需要翻墙，请自行搭梯子，然后解开下面相关的注释，并进行适当修改（socks5地址和端口）\n\nFROM nvidia/cuda:11.5.1-runtime-ubuntu20.04\nRUN apt-get update\n\nRUN apt-get install -y curl proxychains\nRUN $useProxyNetwork curl cip.cc\n\n\nENV TZ=Asia/Shanghai\nENV LC_ALL zh_CN.UTF-8\nRUN apt-get install -y language-pack-zh-hans \\\n    libmysqlclient-dev \\\n    dialog \\\n    nano \\\n    vim \\\n    joe \\\n    wget \\\n    curl \\\n    jq \\\n    gawk \\\n    psmisc \\\n    python \\\n    python3 \\\n    python-yaml \\\n    python-jinja2 \\\n    python3-urllib3 \\\n    python-tz \\\n    python-nose \\\n    python3-prettytable \\\n    python-netifaces \\\n    python-dev \\\n    python3-pip \\\n    python3-mysqldb \\\n    openjdk-8-jre \\\n    openjdk-8-jdk \\\n    openssh-server \\\n    openssh-client \\\n    git \\\n    sudo \\\n    inotify-tools \\\n    rsync \\\n    net-tools \\\n    cron \\\n    swig \\\n    cmake \\\n    redis-tools \\\n    redis-server\\\n    iproute2 \\\n    pkg-config build-essential libssl-dev libffi-dev --fix-missing\n\nRUN locale-gen zh_CN.UTF-8 && localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 \nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\n# create user and add to sudoers\nRUN useradd -m hmp && echo \"hmp:hmp\" | chpasswd && adduser hmp sudo\nUSER hmp\nCMD /bin/bash\n# RUN echo hmp|sudo -S apt-get install -y nano\nWORKDIR /home/hmp\n\n# use python3 as the system default python\nUSER root\nRUN rm /usr/bin/python\nRUN ln /usr/bin/python3 /usr/bin/python\nARG useProxyNetwork=''\nRUN $useProxyNetwork curl cip.cc\n# # comment out below if you do not need proxy network | 翻墙 - 从此行向下删除\n# RUN sed -i '$ d' /etc/proxychains.conf\n# RUN sed -i '$ d' /etc/proxychains.conf\n# RUN echo \"socks5 127.0.0.1 10880\" >> /etc/proxychains.conf\n# ARG useProxyNetwork=proxychains\n# RUN $useProxyNetwork curl cip.cc\n# # comment out above if you do not need proxy network | 翻墙 - 从此行向上删除\n\n# pip install everything we need\nUSER hmp\nSHELL [\"/bin/bash\", \"-c\"]\nRUN $useProxyNetwork pip install numpy scipy scikit-learn\nRUN $useProxyNetwork pip install lz4 gym flask cython waitress colorama func_timeout setproctitle filelock\nRUN $useProxyNetwork pip install commentjson matplotlib psutil paramiko ipykernel onedrivedownloader flock\nRUN $useProxyNetwork pip install torch --extra-index-url https://download.pytorch.org/whl/cu115\nRUN $useProxyNetwork pip install pygame cmake redis numba\nRUN $useProxyNetwork pip install git+https://github.com/oxwhirl/smac.git\n\n# # download and extract UHMAP component\n# WORKDIR /home/hmp\n# RUN $useProxyNetwork git clone https://github.com/binary-husky/uhmap-visual-tool.git\n# WORKDIR /home/hmp/uhmap-visual-tool/\n# RUN $useProxyNetwork python linux_deploy.py\n# RUN $useProxyNetwork python linux_deploy_starcraft_all_versions.py\n# # add execute mod to starcraft II (two versions)\n# RUN chmod +x /home/hmp/StarCraftIII/Version2410/StarCraftII/Versions/Base75689/ \n# RUN chmod +x /home/hmp/StarCraftIII/Versions/Base69232/SC2_x64\n\n# RUN mv /home/hmp/uhmap-visual-tool/UnrealEngine/home/hmp/*  /home/hmp\n\n# download UHMAP main framwork\nWORKDIR /home/hmp\nRUN $useProxyNetwork git clone https://github.com/binary-husky/hmp2g.git\nWORKDIR /home/hmp/hmp2g\n# RUN python main.py -c example.jsonc\n\n# # Installing Times New Roman font\n# USER root\n# # RUN apt-get --reinstall install ttf-mscorefonts-installer\n# # RUN apt-get install msttcorefonts -qq\n# # RUN rm /home/hmp/.cache/matplotlib -rf\n\nUSER root\n\n# RUN sed -i 's/22/2233/g' /etc/ssh/sshd_config\nRUN echo \"Port 2233\" >> /etc/ssh/sshd_config\nRUN echo \"service ssh start\" >> /entrypoint.sh\nRUN echo \"redis-server --daemonize yes\" >> /entrypoint.sh\n\n\n# exit\nUSER hmp\nWORKDIR /home/hmp\nRUN touch /home/hmp/.sudo_as_admin_successful\nCOPY ./bashrc_suffix /home/hmp/bashrc_suffix\nRUN cat /home/hmp/bashrc_suffix >> /home/hmp/.bashrc\n\n# docker build --network=host --progress=plain -t py38 .\n# docker run -itd  --name  hmp-fqx --net host --gpus all --shm-size=16G py38:latest && docker exec -it -u 0 hmp-fqx  service ssh start\n# docker exec -it hmp-fqx bash\n# docker stop hmp-fqx && docker rm hmp-fqx"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/DockerfilePython311",
    "content": "# how to build: docker build --network=host --progress=plain -t py311 . \n\n# 此文件用于构建 HMAP + Starcraft(难易两个版本) + 虚幻引擎组件\n# 可能需要翻墙，请自行搭梯子，然后解开下面相关的注释，并进行适当修改（socks5地址和端口）\n\nFROM nvidia/cuda:11.7.1-runtime-ubuntu20.04\nRUN apt-get update\nRUN apt-get install -y curl proxychains software-properties-common\nRUN add-apt-repository ppa:deadsnakes/ppa -y\nENV TZ=Asia/Shanghai\nENV LC_ALL zh_CN.UTF-8\nRUN apt-get install -y language-pack-zh-hans \\\n    libmysqlclient-dev \\\n    dialog \\\n    nano \\\n    vim \\\n    joe \\\n    wget \\\n    curl \\\n    jq \\\n    gawk \\\n    psmisc \\\n    python-dev \\\n    python3.11-dbg \\\n    python3.11-dev \\\n    python3.11-distutils \\\n    python3.11-examples \\\n    python3.11-full \\\n    python3.11-gdbm-dbg \\\n    python3.11-gdbm \\\n    python3.11-lib2to3 \\\n    python3.11-minimal \\\n    python3.11-tk-dbg \\\n    python3.11-tk \\\n    python3.11-venv \\\n    python3.11 \\\n    openjdk-8-jre \\\n    openjdk-8-jdk \\\n    openssh-server \\\n    openssh-client \\\n    git \\\n    sudo \\\n    htop \\\n    inotify-tools \\\n    rsync \\\n    net-tools \\\n    cron \\\n    swig \\\n    cmake \\\n    redis-tools \\\n    redis-server\\\n    iproute2 \\\n    pkg-config build-essential libssl-dev libffi-dev --fix-missing\n\nRUN locale-gen zh_CN.UTF-8 && localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 \nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\n# create user and add to sudoers\nRUN useradd -m hmp && echo \"hmp:hmp\" | chpasswd && adduser hmp sudo\nUSER hmp\nCMD /bin/bash\n# RUN echo hmp|sudo -S apt-get install -y nano\nWORKDIR /home/hmp\n\n# use python3 as the system default python\nUSER root\nRUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11\nRUN rm /usr/bin/python /usr/bin/python3\nRUN ln /usr/bin/python3.11 /usr/bin/python\nRUN ln /usr/bin/python3.11 /usr/bin/python3\n\n\nARG useProxyNetwork=''\nRUN $useProxyNetwork curl cip.cc\n# # comment out below if you do not need proxy network | 翻墙 - 从此行向下删除\nRUN sed -i '$ d' /etc/proxychains.conf\nRUN sed -i '$ d' /etc/proxychains.conf\nRUN echo \"socks5 172.18.116.161 10880\" >> /etc/proxychains.conf\nARG useProxyNetwork=proxychains\nRUN $useProxyNetwork curl cip.cc\n# # comment out above if you do not need proxy network | 翻墙 - 从此行向上删除\n\n# pip install everything we need\nUSER hmp\nSHELL [\"/bin/bash\", \"-c\"]\nRUN $useProxyNetwork pip install numpy scipy scikit-learn\nRUN $useProxyNetwork pip install lz4 gym flask cython waitress colorama func_timeout setproctitle filelock\nRUN $useProxyNetwork pip install commentjson matplotlib psutil paramiko ipykernel onedrivedownloader flock\nRUN $useProxyNetwork pip install cmake redis sacred\nRUN $useProxyNetwork pip install pygame --pre\nRUN $useProxyNetwork pip install git+https://github.com/oxwhirl/smac.git\n\nRUN $useProxyNetwork pip install torch --extra-index-url https://download.pytorch.org/whl/cu117\n\n# # download and extract UHMAP component\n# WORKDIR /home/hmp\n# RUN $useProxyNetwork git clone https://github.com/binary-husky/uhmap-visual-tool.git\n# WORKDIR /home/hmp/uhmap-visual-tool/\n# RUN python linux_deploy.py\n# RUN python linux_deploy_starcraft_all_versions.py\n\n# RUN chmod +x /home/hmp/StarCraftIII/Version2410/StarCraftII/Versions/Base75689/ \n# RUN chmod +x /home/hmp/StarCraftIII/Versions/Base69232/SC2_x64\n\n# RUN mv /home/hmp/uhmap-visual-tool/UnrealEngine/home/hmp/*  /home/hmp\n\n# download UHMAP main framwork\nWORKDIR /home/hmp\nRUN $useProxyNetwork git clone https://github.com/binary-husky/hmp2g.git\nWORKDIR /home/hmp/hmp2g\n# RUN python main.py -c example.jsonc\n\n# # Installing Times New Roman font\n# USER root\n# # RUN apt-get --reinstall install ttf-mscorefonts-installer\n# # RUN apt-get install msttcorefonts -qq\n# # RUN rm /home/hmp/.cache/matplotlib -rf\n\nUSER root\n\n# RUN sed -i 's/22/2233/g' /etc/ssh/sshd_config\nRUN echo \"Port 2233\" >> /etc/ssh/sshd_config\nRUN echo \"service ssh start\" >> /entrypoint.sh\nRUN echo \"redis-server --daemonize yes\" >> /entrypoint.sh\n\n# CMD [\"/bin/bash -c echo clara | sudo -S /entrypoint.sh\"]\n\n\n\n\n# # install numba, never should have used this \n# WORKDIR /home/hmp\n# RUN wget https://github.com/numba/llvmlite/archive/refs/tags/v0.39.1.tar.gz\n# RUN tar -xf v0.39.1.tar.gz\n# WORKDIR /home/hmp/llvmlite-0.39.1/\n# RUN sed -i 's/3.11/3.12/g' setup.py\n# RUN pip install .\n\n\n# WORKDIR /home/hmp\n# RUN wget https://github.com/numba/numba/archive/refs/tags/0.56.3.tar.gz\n# RUN tar -xf 0.56.3.tar.gz\n# WORKDIR /home/hmp/numba-0.56.3/\n# RUN sed -i 's/3.11/3.12/g' setup.py\n# RUN sed -i 's/0.40/0.60/g' setup.py\n# RUN pip install .\n\n\n\n\n\n# exit\nUSER hmp\nWORKDIR /home/hmp\nRUN touch /home/hmp/.sudo_as_admin_successful\nCOPY ./bashrc_suffix /home/hmp/bashrc_suffix\nRUN cat /home/hmp/bashrc_suffix >> /home/hmp/.bashrc\n\n# docker build --network=host --progress=plain -t py311 .\n# docker run -itd  --name  hmp-fqx --net host --gpus all --shm-size=16G py311:latest && docker exec -it -u 0 hmp-fqx  service ssh start\n# docker exec -it hmp-fqx bash\n# docker stop hmp-fqx && docker rm hmp-fqx"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/bashrc_suffix",
    "content": "ulimit -n 4096\nalias nv='watch -n 0.35 nvidia-smi'\nalias xrdp_reset='rm -f /var/run/xrdp/xrdp-sesman.pid'\nalias fq='proxychains'\nalias killhmp='kill -9 $(ps -ef | grep Hmap | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias killpy='kill -9 $(ps -ef | grep python | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias killsc='kill -9 $(ps -ef | grep StarCraft | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias killvs='kill -9 $(ps -ef | grep vscode | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias killnb='kill -9 $(ps -ef | grep ipykernel | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias hmp='python main.py --cfg'\nalias vscode='code --no-sandbox'\nalias killue='kill -9 $(ps -ef | grep UHMP | grep hmp | grep -v grep | awk '\"'\"'{print $ 2}'\"'\"')'\nalias ftop='top -E g -s'"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/AirAttack.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"xxxx\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 3562,\n\n        \"device\": \"cuda:0\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ],\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [105,],\n        \"MaxEpisodeStep\": 5000,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": true,\n        \"UElink2editor\": true,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapJustAnIsland\",\n        \"SubTaskSelection\": \"UhmapJustAnIsland\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 5,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.script_ai.uhmap_island->DummyAlgorithmIdle\"\n        ],\n        \"DemoType\": \"AirAttack\"\n    },\n\n    \"MISSION.uhmap.SubTasks.UhmapJustAnIslandConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n\n            { \"team\":0,  \"tid\":0,  \"uid\":0,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":1,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":2,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":3,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":4,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":5,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":6,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":7,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":8,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":9,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":10,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":11,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":12,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":13,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":14,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":15,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":16,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":17,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":18,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":19,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":20,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":21,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":22,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":23,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":24,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":25,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":26,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":27,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":28,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":29,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":30,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":31,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":32,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":33,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":34,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":35,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":36,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":37,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":38,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":39,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":40,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":41,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":42,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":43,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":44,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":45,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":46,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":47,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":48,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":49,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            \n\n\n            { \"team\":0,  \"tid\":0,  \"uid\":50,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":51,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":52,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":53,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":54,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":55,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":56,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":57,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":58,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":59,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":60,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":61,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":62,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":63,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":64,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":65,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":66,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":67,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":68,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":69,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":70,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":71,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":72,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":73,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":74,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":75,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":76,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":77,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":78,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":79,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":80,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":81,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":82,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":83,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":84,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":85,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":86,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":87,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":88,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":89,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":0,  \"uid\":90,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":91,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":92,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":93,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":94,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":95,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":96,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":97,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":98,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":99,  \"n_team_agent\":105, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n\n            // { \"team\":0,  \"tid\":5,  \"uid\":100,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":101,  \"uid\":101,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":102,  \"uid\":102,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":103,  \"uid\":103,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":104,  \"uid\":104,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n           \n           \n            { \"team\":0,  \"tid\":5,  \"uid\":100,  \"n_team_agent\":105, \"type\":\"commander\",  \"init_fn_name\":\"init_target\",  },\n            { \"team\":0,  \"tid\":101,  \"uid\":101,  \"n_team_agent\":105, \"type\":\"commander\",  \"init_fn_name\":\"init_target\",  },\n            { \"team\":0,  \"tid\":102,  \"uid\":102,  \"n_team_agent\":105, \"type\":\"commander\",  \"init_fn_name\":\"init_target\",  },\n            { \"team\":0,  \"tid\":103,  \"uid\":103,  \"n_team_agent\":105, \"type\":\"commander\",  \"init_fn_name\":\"init_target\",  },\n            { \"team\":0,  \"tid\":104,  \"uid\":104,  \"n_team_agent\":105, \"type\":\"commander\",  \"init_fn_name\":\"init_target\",  },\n\n            // { \"team\":0,  \"tid\":5,  \"uid\":105,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":101,  \"uid\":106,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":102,  \"uid\":107,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":103,  \"uid\":108,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n            // { \"team\":0,  \"tid\":104,  \"uid\":109,  \"n_team_agent\":105, \"type\":\"Air Defense\",  \"init_fn_name\":\"init_target\",  },\n\n\n        ]\n    },\n\n    \"ALGORITHM.script_ai.uhmap_island.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/AirShow.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"xxxx\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 3562,\n\n        \"device\": \"cuda:0\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ],\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10,],\n        \"MaxEpisodeStep\": 5000,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": true,\n        \"UElink2editor\": true,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapJustAnIsland\",\n        \"SubTaskSelection\": \"UhmapJustAnIsland\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 5,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.script_ai.uhmap_island->DummyAlgorithmIdle\"\n        ],\n        \"DemoType\": \"AirShow\"\n    },\n\n    \"MISSION.uhmap.SubTasks.UhmapJustAnIslandConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,  \"uid\":0,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":1,  \"uid\":1,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":2,  \"uid\":2,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":3,  \"uid\":3,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":4,  \"uid\":4,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":5,  \"uid\":5,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":6,  \"uid\":6,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":7,  \"uid\":7,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":8,  \"uid\":8,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            { \"team\":0,  \"tid\":9,  \"uid\":9,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":10,  \"n_team_agent\":10, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":11,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":12,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":13,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":14,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":15,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":16,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":17,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":18,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":19,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":20,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":21,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":22,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":23,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":24,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":25,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":26,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":27,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":28,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":29,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":30,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":31,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":32,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":33,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":34,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":35,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":36,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":37,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":38,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":39,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":40,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":41,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":42,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":43,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":44,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":45,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":46,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":47,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":48,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":49,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            \n\n\n            // { \"team\":0,  \"tid\":0,  \"uid\":50,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":51,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":52,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":53,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":54,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":55,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":56,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":57,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":58,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":59,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":60,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":61,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":62,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":63,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":64,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":65,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":66,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":67,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":68,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":69,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":70,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":71,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":72,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":73,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":74,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":75,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":76,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":77,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":78,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":79,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":80,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":81,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":82,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":83,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":84,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":85,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":86,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":87,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":88,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":89,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":0,  \"uid\":90,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":1,  \"uid\":91,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":2,  \"uid\":92,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":3,  \"uid\":93,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":4,  \"uid\":94,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":5,  \"uid\":95,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":6,  \"uid\":96,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":7,  \"uid\":97,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":8,  \"uid\":98,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n            // { \"team\":0,  \"tid\":9,  \"uid\":99,  \"n_team_agent\":100, \"type\":\"PlaneAgent\",  \"init_fn_name\":\"init_air\",  },\n\n\n        ]\n    },\n\n    \"ALGORITHM.script_ai.uhmap_island.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/hlt+50vs50.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"z-hete-50\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 16,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/hete_league_onenet_fix\",\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [50, 50], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 150,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false, // note: random seed has different impact on renderer and server\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapHuge\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.hete_league_onenet_fix.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapHugeConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n        ]\n    },\n\n\n\n\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.hete_league_onenet_fix.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.hete_league_onenet_fix.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"hete_n_alive_frontend\": 2,\n        \"hete_n_net_placeholder\":5,\n        \"hete_same_prob\": 1.0,\n        \"load_checkpoint\": false,\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"hete_lasted_n\":3,\n        \"policy_resonance\": true,\n        \"hete_exclude_zero_wr\": true,\n        \"debug\": false,\n        \"n_entity_placeholder\": 11\n    },\n    \"ALGORITHM.hete_league_onenet_fix.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-sin\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01,\n    },\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/ppoma+50vs50.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-uhmap50vs50\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 32,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/ppo_ma\",\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [50, 50], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 150,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false, // note: random seed has different impact on renderer and server\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapHuge\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapHugeConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n        ]\n    },\n\n\n\n\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    },\n\n    \"ALGORITHM.ppo_ma.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-sin\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01,\n    },\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/ppoma+intercept.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-intercept\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [5, 12],\n        \"MaxEpisodeStep\": 100,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false, // note: random seed has different impact on renderer and server\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapIntercept\",\n        \"SubTaskSelection\": \"UhmapIntercept\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 2, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n            \"TEMP.TEAM2.ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n        ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    },\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"TEMP.TEAM2.ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    },\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/ppoma+predatorprey.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-predatorprey\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 3],\n        \"MaxEpisodeStep\": 100,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false, // note: random seed has different impact on renderer and server\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapPreyPredator\",\n        \"UhmapVersion\":\"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 2, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n            \"TEMP.TEAM2.ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n        ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    },\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"TEMP.TEAM2.ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 64,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    },\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/ppoma+uhmap10vs10hete.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-uhmap10vs10\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 16,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 8834,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/ppo_ma\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.ppo_ma.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 256,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/ppoma_waterdrop.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-waterdorp\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 10, 2 ], // 10 ships, 2 waterdrops\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": false, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": true,\n      \"HeteAgents\": true,\n      \"UnrealLevel\": \"UhmapWaterdrop\",\n      \"SubTaskSelection\": \"UhmapWaterdrop\",\n      \"UhmapVersion\":\"3.5\",\n      \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n      \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.my_ai.foundation->ReinforceAlgorithmFoundation\",\n        \"TEMP.TEAM2.ALGORITHM.my_ai.foundation->ReinforceAlgorithmFoundation\",\n      ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.my_ai.foundation.py->AlgorithmConfig\": {\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.my_ai.foundation.py->AlgorithmConfig\": {\n    },\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qmix+uhmap10vs10hete.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"RVE-drone2-qmix-fixstate-run1\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 8529,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n\n\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"state_compat\": \"pad\",\n        \"pymarl_config_injection\": {\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\": {\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\"\n            },\n            \"config.py->GlobalConfig\": {\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qmix+uhmap20vs20.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"qmix-uhmap50vs50\",//\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [20, 20], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 150,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":11,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":13,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":15,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":17,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":19,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":11,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":13,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":15,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":17,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":19,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n\n\n\n\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"state_compat\": \"obs_mean\",\n        \"pymarl_config_injection\":{\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\":{\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\",\n            },\n            \"config.py->GlobalConfig\":{\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\",\n            }\n        }\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qmix+uhmap50vs50+debug.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"qmix-uhmap50vs50\",//\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [50, 50], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 150,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": true,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapHuge\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n            \"TEMP.TEAM2.ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapHugeConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,    \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,    \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,    \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":10,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":11,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":12,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":13,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":14,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":15,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":16,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":17,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":18,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":19,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":20,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":21,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":22,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":23,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":24,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":25,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":26,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":27,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":28,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":29,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":30,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":31,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":32,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":33,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":34,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":35,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":36,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":37,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":38,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":39,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":40,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":41,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":42,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":43,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":44,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":45,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":46,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":47,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":48,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":49,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n        ]\n    },\n\n\n\n\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"TEMP.TEAM2.ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qmix+uhmap50vs50.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"qmix-uhmap50vs50\",//\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n    // --- Part2: config MISSION --- \n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [100, 100], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 150,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapHuge\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"../WindowsNoEditor/UHMP.exe\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapHugeConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n\n            { \"team\":1,  \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n\n        ]\n    },\n\n\n\n\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"state_compat\": \"obs_mean\",\n        \"pymarl_config_injection\":{\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\":{\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\",\n            },\n            \"config.py->GlobalConfig\":{\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\",\n            }\n        }\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qplex+uhmap10vs10hete.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"qplex-uhmap\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundationOld\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n\n\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"use_shell_normalization\": true,\n        \"state_compat\": \"pad\",\n        \"load_checkpoint\": \"False\",\n        \"pymarl2_alg_select\": \"qplex\",\n        \"pymarl2_runner_select\": \"parallel\",\n        \"pymarl_config_injection\":{\n            \"config.py->GlobalConfig\": {\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\",\n                \"runner\": \"parallel\",\n            },\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\":{\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\"\n            },\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/qtran+uhmap10vs10hete.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"qtran-cat\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundationOld\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n\n\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n\n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"use_shell_normalization\": true,\n        \"state_compat\": \"obs_cat\",\n        \"load_checkpoint\": \"False\",\n        \"pymarl2_alg_select\": \"qtran\",\n        \"pymarl2_runner_select\": \"parallel\",\n        \"pymarl_config_injection\":{\n            \"config.py->GlobalConfig\": {\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\",\n                \"runner\": \"parallel\",\n            },\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\":{\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\"\n            },\n        }\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/examples/uhmap/random_waterdrop.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-waterdorp\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 10, 2 ], // 10 ships, 2 waterdrops\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": false, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": true,\n      \"HeteAgents\": true,\n      \"UnrealLevel\": \"UhmapWaterdrop\",\n      \"SubTaskSelection\": \"UhmapWaterdrop\",\n      \"UhmapVersion\":\"3.5\",\n      \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n      \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.my_ai.foundation->DiscreteRLFoundation\",\n        \"TEMP.TEAM2.ALGORITHM.my_ai.foundation->DiscreteRLFoundation\",\n      ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.my_ai.foundation.py->AlgorithmConfig\": {\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.my_ai.foundation.py->AlgorithmConfig\": {\n    },\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/pip_requirement.md",
    "content": "# all pip requirements\n\nFirst of all, python version must >= 3.8, then, use pip to install following packages:\n\n## Group 1: Common\n```\nnumpy scipy torch gym scikit-learn pygame \n```\n\n\n## Group 2: Unreal-HMAP and Starcraft\n```\nlz4 smac\n```\n\n## Group 3: Visual\n```\nflask waitress colorama matplotlib ipykernel\n``` \n\n## Group 4: Performance\n```\nnumba cython \n```\n\n## Group 5: Functional\n```\nfunc_timeout commentjson PyYAML onedrivedownloader redis filelock\n```\n\n## Group 6: Remote and management\n```\nparamiko psutil setproctitle sacred\n```\n\n## install\n``` \npip install torch\npip install numpy scipy gym scikit-learn pygame  lz4 smac flask waitress colorama matplotlib ipykernel numba cython  func_timeout commentjson PyYAML onedrivedownloader redis filelock paramiko psutil setproctitle sacred\n```\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/sc2checkversion",
    "content": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom smac.env.multiagentenv import MultiAgentEnv\nfrom smac.env.starcraft2.maps import get_map_params\n\nimport atexit\nimport os\nfrom operator import attrgetter\nfrom copy import deepcopy\nimport numpy as np\nimport enum\nimport math\nfrom absl import logging\n\nfrom pysc2 import maps\nfrom pysc2 import run_configs\nfrom pysc2.lib import protocol\n\nfrom s2clientprotocol import common_pb2 as sc_common\nfrom s2clientprotocol import sc2api_pb2 as sc_pb\nfrom s2clientprotocol import raw_pb2 as r_pb\nfrom s2clientprotocol import debug_pb2 as d_pb\n\nraces = {\n    \"R\": sc_common.Random,\n    \"P\": sc_common.Protoss,\n    \"T\": sc_common.Terran,\n    \"Z\": sc_common.Zerg,\n}\n\ndifficulties = {\n    \"1\": sc_pb.VeryEasy,\n    \"2\": sc_pb.Easy,\n    \"3\": sc_pb.Medium,\n    \"4\": sc_pb.MediumHard,\n    \"5\": sc_pb.Hard,\n    \"6\": sc_pb.Harder,\n    \"7\": sc_pb.VeryHard,\n    \"8\": sc_pb.CheatVision,\n    \"9\": sc_pb.CheatMoney,\n    \"A\": sc_pb.CheatInsane,\n}\n\nactions = {\n    \"move\": 16,  # target: PointOrUnit\n    \"attack\": 23,  # target: PointOrUnit\n    \"stop\": 4,  # target: None\n    \"heal\": 386,  # Unit\n}\n\n\nclass Direction(enum.IntEnum):\n    NORTH = 0\n    SOUTH = 1\n    EAST = 2\n    WEST = 3\n\n\nclass StarCraft2Env(MultiAgentEnv):\n    \"\"\"The StarCraft II environment for decentralised multi-agent\n    micromanagement scenarios.\n    \"\"\"\n    def __init__(\n        self,\n        sc_version,\n        map_name=\"8m\",\n        step_mul=8,\n        move_amount=2,\n        difficulty=\"7\",\n        game_version=None,\n        seed=None,\n        continuing_episode=False,\n        obs_all_health=True,\n        obs_own_health=True,\n        obs_last_action=False,\n        obs_pathing_grid=False,\n        obs_terrain_height=False,\n        obs_instead_of_state=False,\n        obs_timestep_number=False,\n        state_last_action=True,\n        state_timestep_number=False,\n        reward_sparse=False,\n        reward_only_positive=True,\n        reward_death_value=10,\n        reward_win=200,\n        reward_defeat=0,\n        reward_negative_scale=0.5,\n        reward_scale=True,\n        reward_scale_rate=20,\n        reward_vec=False,\n        return_mat=False,\n        replay_dir=\"\",\n        replay_prefix=\"\",\n        window_size_x=1920,\n        window_size_y=1200,\n        heuristic_ai=False,\n        heuristic_rest=False,\n        debug=False,\n        render=False,\n    ):\n        \"\"\"\n        Create a StarCraftC2Env environment.\n\n        Parameters\n        ----------\n        map_name : str, optional\n            The name of the SC2 map to play (default is \"8m\"). The full list\n            can be found by running bin/map_list.\n        step_mul : int, optional\n            How many game steps per agent step (default is 8). None\n            indicates to use the default map step_mul.\n        move_amount : float, optional\n            How far away units are ordered to move per step (default is 2).\n        difficulty : str, optional\n            The difficulty of built-in computer AI bot (default is \"7\").\n        game_version : str, optional\n            StarCraft II game version (default is None). None indicates the\n            latest version.\n        seed : int, optional\n            Random seed used during game initialisation. This allows to\n        continuing_episode : bool, optional\n            Whether to consider episodes continuing or finished after time\n            limit is reached (default is False).\n        obs_all_health : bool, optional\n            Agents receive the health of all units (in the sight range) as part\n            of observations (default is True).\n        obs_own_health : bool, optional\n            Agents receive their own health as a part of observations (default\n            is False). This flag is ignored when obs_all_health == True.\n        obs_last_action : bool, optional\n            Agents receive the last actions of all units (in the sight range)\n            as part of observations (default is False).\n        obs_pathing_grid : bool, optional\n            Whether observations include pathing values surrounding the agent\n            (default is False).\n        obs_terrain_height : bool, optional\n            Whether observations include terrain height values surrounding the\n            agent (default is False).\n        obs_instead_of_state : bool, optional\n            Use combination of all agents' observations as the global state\n            (default is False).\n        obs_timestep_number : bool, optional\n            Whether observations include the current timestep of the episode\n            (default is False).\n        state_last_action : bool, optional\n            Include the last actions of all agents as part of the global state\n            (default is True).\n        state_timestep_number : bool, optional\n            Whether the state include the current timestep of the episode\n            (default is False).\n        reward_sparse : bool, optional\n            Receive 1/-1 reward for winning/loosing an episode (default is\n            False). Whe rest of reward parameters are ignored if True.\n        reward_only_positive : bool, optional\n            Reward is always positive (default is True).\n        reward_death_value : float, optional\n            The amount of reward received for killing an enemy unit (default\n            is 10). This is also the negative penalty for having an allied unit\n            killed if reward_only_positive == False.\n        reward_win : float, optional\n            The reward for winning in an episode (default is 200).\n        reward_defeat : float, optional\n            The reward for loosing in an episode (default is 0). This value\n            should be nonpositive.\n        reward_negative_scale : float, optional\n            Scaling factor for negative rewards (default is 0.5). This\n            parameter is ignored when reward_only_positive == True.\n        reward_scale : bool, optional\n            Whether or not to scale the reward (default is True).\n        reward_scale_rate : float, optional\n            Reward scale rate (default is 20). When reward_scale == True, the\n            reward received by the agents is divided by (max_reward /\n            reward_scale_rate), where max_reward is the maximum possible\n            reward per episode without considering the shield regeneration\n            of Protoss units.\n        replay_dir : str, optional\n            The directory to save replays (default is None). If None, the\n            replay will be saved in Replays directory where StarCraft II is\n            installed.\n        replay_prefix : str, optional\n            The prefix of the replay to be saved (default is None). If None,\n            the name of the map will be used.\n        window_size_x : int, optional\n            The length of StarCraft II window size (default is 1920).\n        window_size_y: int, optional\n            The height of StarCraft II window size (default is 1200).\n        heuristic_ai: bool, optional\n            Whether or not to use a non-learning heuristic AI (default False).\n        heuristic_rest: bool, optional\n            At any moment, restrict the actions of the heuristic AI to be\n            chosen from actions available to RL agents (default is False).\n            Ignored if heuristic_ai == False.\n        debug: bool, optional\n            Log messages about observations, state, actions and rewards for\n            debugging purposes (default is False).\n        \"\"\"\n        if sc_version=='old' or sc_version=='2.4.6':\n            os.environ.setdefault(\"SC2PATH\", \"~/StarCraftIII\")\n        if sc_version=='new' or sc_version=='2.4.10':\n            os.environ.setdefault(\"SC2PATH\", \"~/StarCraftIII/Version2410/StarCraftII\")\n\n        # Map arguments\n        self.map_name = map_name\n        map_params = get_map_params(self.map_name)\n        self.n_agents = map_params[\"n_agents\"]\n        self.n_enemies = map_params[\"n_enemies\"]\n        self.episode_limit = map_params[\"limit\"]\n        self._move_amount = move_amount\n        self._step_mul = step_mul\n        self.difficulty = difficulty\n\n        # Observations and state\n        self.obs_own_health = obs_own_health\n        self.obs_all_health = obs_all_health\n        self.obs_instead_of_state = obs_instead_of_state\n        self.obs_last_action = obs_last_action\n        self.obs_pathing_grid = obs_pathing_grid\n        self.obs_terrain_height = obs_terrain_height\n        self.obs_timestep_number = obs_timestep_number\n        self.state_last_action = state_last_action\n        self.state_timestep_number = state_timestep_number\n        if self.obs_all_health:\n            self.obs_own_health = True\n        self.n_obs_pathing = 8\n        self.n_obs_height = 9\n\n        # Rewards args\n        self.reward_sparse = reward_sparse\n        self.reward_only_positive = reward_only_positive\n        self.reward_negative_scale = reward_negative_scale\n        self.reward_death_value = reward_death_value\n        self.reward_win = reward_win\n        self.reward_defeat = reward_defeat\n        self.reward_scale = reward_scale\n        self.reward_scale_rate = reward_scale_rate\n\n        # Other\n        self.game_version = game_version\n        self.continuing_episode = continuing_episode\n        self._seed = seed\n        self.heuristic_ai = heuristic_ai\n        self.heuristic_rest = heuristic_rest\n        self.debug = debug\n        self.window_size = (window_size_x, window_size_y)\n        self.replay_dir = replay_dir\n        self.replay_prefix = replay_prefix\n\n        # Actions\n        self.n_actions_no_attack = 6\n        self.n_actions_move = 4\n        self.n_actions = self.n_actions_no_attack + self.n_enemies\n\n        # Map info\n        self._agent_race = map_params[\"a_race\"]\n        self._bot_race = map_params[\"b_race\"]\n        self.shield_bits_ally = 1 if self._agent_race == \"P\" else 0\n        self.shield_bits_enemy = 1 if self._bot_race == \"P\" else 0\n        self.unit_type_bits = map_params[\"unit_type_bits\"]\n        self.map_type = map_params[\"map_type\"]\n        self._unit_types = None\n\n        self.max_reward = (\n            self.n_enemies * self.reward_death_value + self.reward_win\n        )\n\n        # create lists containing the names of attributes returned in states\n        self.ally_state_attr_names = [\n            \"health\",\n            \"energy/cooldown\",\n            \"rel_x\",\n            \"rel_y\",\n        ]\n        self.enemy_state_attr_names = [\"health\", \"rel_x\", \"rel_y\"]\n\n        if self.shield_bits_ally > 0:\n            self.ally_state_attr_names += [\"shield\"]\n        if self.shield_bits_enemy > 0:\n            self.enemy_state_attr_names += [\"shield\"]\n\n        if self.unit_type_bits > 0:\n            bit_attr_names = [\n                \"type_{}\".format(bit) for bit in range(self.unit_type_bits)\n            ]\n            self.ally_state_attr_names += bit_attr_names\n            self.enemy_state_attr_names += bit_attr_names\n\n        self.agents = {}\n        self.enemies = {}\n        self._episode_count = 0\n        self._episode_steps = 0\n        self._total_steps = 0\n        self._obs = None\n        self.battles_won = 0\n        self.battles_game = 0\n        self.timeouts = 0\n        self.force_restarts = 0\n        self.last_stats = None\n        self.death_tracker_ally = np.zeros(self.n_agents)\n        self.death_tracker_enemy = np.zeros(self.n_enemies)\n        self.previous_ally_units = None\n        self.previous_enemy_units = None\n        self.last_action = np.zeros((self.n_agents, self.n_actions))\n        self._min_unit_type = 0\n        self.marine_id = self.marauder_id = self.medivac_id = 0\n        self.hydralisk_id = self.zergling_id = self.baneling_id = 0\n        self.stalker_id = self.colossus_id = self.zealot_id = 0\n        self.max_distance_x = 0\n        self.max_distance_y = 0\n        self.map_x = 0\n        self.map_y = 0\n        self.reward = 0\n        self.renderer = None\n        self.terrain_height = None\n        self.pathing_grid = None\n        self._run_config = None\n        self._sc2_proc = None\n        self._controller = None\n        self.return_mat = return_mat\n        self.reward_vec = reward_vec\n        self.enable_threejs_render = render\n        # Try to avoid leaking SC2 processes on shutdown\n        atexit.register(lambda: self.close())\n\n    def _launch(self):\n        \"\"\"Launch the StarCraft II game.\"\"\"\n        self._run_config = run_configs.get(version=self.game_version)\n        _map = maps.get(self.map_name)\n\n        # Setting up the interface\n        interface_options = sc_pb.InterfaceOptions(raw=True, score=False)\n        self._sc2_proc = self._run_config.start(\n            window_size=self.window_size, want_rgb=False\n        )\n        self._controller = self._sc2_proc.controller\n\n        # Request to create the game\n        create = sc_pb.RequestCreateGame(\n            local_map=sc_pb.LocalMap(\n                map_path=_map.path,\n                map_data=self._run_config.map_data(_map.path),\n            ),\n            realtime=False,\n            random_seed=self._seed,\n        )\n        create.player_setup.add(type=sc_pb.Participant)\n        create.player_setup.add(\n            type=sc_pb.Computer,\n            race=races[self._bot_race],\n            difficulty=difficulties[self.difficulty],\n        )\n        self._controller.create_game(create)\n\n        join = sc_pb.RequestJoinGame(\n            race=races[self._agent_race], options=interface_options\n        )\n        self._controller.join_game(join)\n\n        game_info = self._controller.game_info()\n        map_info = game_info.start_raw\n        map_play_area_min = map_info.playable_area.p0\n        map_play_area_max = map_info.playable_area.p1\n        self.max_distance_x = map_play_area_max.x - map_play_area_min.x\n        self.max_distance_y = map_play_area_max.y - map_play_area_min.y\n        self.map_x = map_info.map_size.x\n        self.map_y = map_info.map_size.y\n\n        if map_info.pathing_grid.bits_per_pixel == 1:\n            vals = np.array(list(map_info.pathing_grid.data)).reshape(\n                self.map_x, int(self.map_y / 8)\n            )\n            self.pathing_grid = np.transpose(\n                np.array(\n                    [\n                        [(b >> i) & 1 for b in row for i in range(7, -1, -1)]\n                        for row in vals\n                    ],\n                    dtype=np.bool,\n                )\n            )\n        else:\n            self.pathing_grid = np.invert(\n                np.flip(\n                    np.transpose(\n                        np.array(\n                            list(map_info.pathing_grid.data), dtype=np.bool\n                        ).reshape(self.map_x, self.map_y)\n                    ),\n                    axis=1,\n                )\n            )\n\n        self.terrain_height = (\n            np.flip(\n                np.transpose(\n                    np.array(list(map_info.terrain_height.data)).reshape(\n                        self.map_x, self.map_y\n                    )\n                ),\n                1,\n            )\n            / 255\n        )\n\n    def reset(self):\n        \"\"\"Reset the environment. Required after each full episode.\n        Returns initial observations and states.\n        \"\"\"\n        self._episode_steps = 0\n        if self._episode_count == 0:\n            # Launch StarCraft II\n            self._launch()\n        else:\n            self._restart()\n\n        # Information kept for counting the reward\n        self.death_tracker_ally = np.zeros(self.n_agents)\n        self.death_tracker_enemy = np.zeros(self.n_enemies)\n        self.previous_ally_units = None\n        self.previous_enemy_units = None\n        self.win_counted = False\n        self.defeat_counted = False\n\n        self.last_action = np.zeros((self.n_agents, self.n_actions))\n\n        if self.heuristic_ai:\n            self.heuristic_targets = [None] * self.n_agents\n\n        try:\n            self._obs = self._controller.observe()\n            self.init_units()\n        except (protocol.ProtocolError, protocol.ConnectionError):\n            self.full_restart()\n\n        if self.debug:\n            logging.debug(\n                \"Started Episode {}\".format(self._episode_count).center(\n                    60, \"*\"\n                )\n            )\n\n        return self.get_obs(), self.get_state()\n\n    def _restart(self):\n        \"\"\"Restart the environment by killing all units on the map.\n        There is a trigger in the SC2Map file, which restarts the\n        episode when there are no units left.\n        \"\"\"\n        try:\n            self._kill_all_units()\n            self._controller.step(2)\n        except (protocol.ProtocolError, protocol.ConnectionError):\n            self.full_restart()\n\n    def full_restart(self):\n        \"\"\"Full restart. Closes the SC2 process and launches a new one.\"\"\"\n        self._sc2_proc.close()\n        self._launch()\n        self.force_restarts += 1\n\n    def threejs_renderer(self):\n        if not hasattr(self, 'threejs_bridge'):\n            # 濡傛灉娌℃湁鍒濆鍖栵紝鍏堝垵濮嬪寲\n            from VISUALIZE.mcom import mcom\n            self.threejs_bridge = mcom(ip='127.0.0.1',\n                                        port=12084,\n                                        path='RECYCLE/v2d_logger/',\n                                        digit=8,\n                                        rapid_flush=False,\n                                        draw_mode='Threejs')\n            self.threejs_bridge.v2d_init()\n            self.threejs_bridge.set_style('grid')\n            # self.threejs_bridge.set_style('gray')\n            self.threejs_bridge.set_style('star')\n            self.threejs_bridge.geometry_rotate_scale_translate('ball', 0, 0, 0, 1, 1, 1, 0,0,0)\n            self.threejs_bridge.geometry_rotate_scale_translate('box', 0, 0, 0, 1, 1, 1, 0,0,0)\n\n\n        for a_id, a_unit in self.agents.items():\n            color = 'green' if a_unit.health != 0 else 'yellow'\n            self.threejs_bridge.v2dx(\n                'ball|%d|%s|0.25'%(a_id, color),\n                a_unit.pos.x,\n                a_unit.pos.y,\n                a_unit.pos.z,\n                vel_dir=a_unit.facing,\n                label='%d'%a_unit.health,\n                label_color='white',\n                attack_range=0)\n            for healer_id in self.action_heal[a_id]:\n                self.threejs_bridge.flash('beam', src=healer_id, dst=a_id, dur=0.2, size=0.1, color='LimeGreen')\n\n        for a_id, a_unit in self.enemies.items():\n            color = 'red' if a_unit.health != 0 else 'yellow'\n            self.threejs_bridge.v2dx(\n                'box|%d|%s|0.5'%(a_id+len(self.agents), color),\n                a_unit.pos.x,\n                a_unit.pos.y,\n                a_unit.pos.z,\n                label='%d'%a_unit.health,\n                label_color='white',\n                attack_range=0)\n            for attack_id in self.action_blow[a_id]:\n                self.threejs_bridge.flash('beam', src=attack_id, dst=a_id+len(self.agents), dur=0.2, size=0.2, color='HotPink')\n\n        self.threejs_bridge.v2d_show()\n\n    def step(self, actions):\n        # print('-----step-----')\n        \"\"\"A single environment step. Returns reward, terminated, info.\"\"\"\n        actions_int = [int(a) for a in actions]\n\n        self.last_action = np.eye(self.n_actions)[np.array(actions_int)]    # onehot version of actions\n        self.action_blow = [[] for _ in self.enemies]\n        self.action_heal = [[] for _ in self.agents]\n        # Collect individual actions\n        sc_actions = []\n        if self.debug:\n            logging.debug(\"Actions\".center(60, \"-\"))\n\n        for a_id, action in enumerate(actions_int):\n            if not self.heuristic_ai:\n                sc_action = self.get_agent_action(a_id, action)\n            else:\n                sc_action, action_num = self.get_agent_action_heuristic(\n                    a_id, action\n                )\n                actions[a_id] = action_num\n            if sc_action:\n                sc_actions.append(sc_action)\n\n        # Send action request\n        req_actions = sc_pb.RequestAction(actions=sc_actions)\n        try:\n            self._controller.actions(req_actions)\n            # Make step in SC2, i.e. apply actions\n            self._controller.step(self._step_mul)\n            # Observe here so that we know if the episode is over.\n            self._obs = self._controller.observe()\n        except (protocol.ProtocolError, protocol.ConnectionError):\n            self.full_restart()\n            reward = 0 if not self.reward_vec else np.array([0. for _ in self.agents])\n            return reward, True, {}\n\n        self._total_steps += 1\n        self._episode_steps += 1\n\n        # Update units, \n        # return None: game is still on,\n        # return -1: lose the game\n        # return +1: win the game\n        # return 0: draw\n        game_end_code = self.update_units()\n\n        terminated = False\n        reward = self.reward_battle()   # <1>璁＄畻绋犲瘑濂栧姳锛屼絾濡傛灉self.reward_sparse锛岃繖涓猺eward鏍规湰涓嶇敤\n        info = {\"battle_won\": False}\n\n        # count units that are still alive\n        dead_allies, dead_enemies = 0, 0\n        for _al_id, al_unit in self.agents.items():\n            if al_unit.health == 0:\n                dead_allies += 1\n        for _e_id, e_unit in self.enemies.items():\n            if e_unit.health == 0:\n                dead_enemies += 1\n\n        info[\"dead_allies\"] = dead_allies\n        info[\"dead_enemies\"] = dead_enemies\n\n        if self.enable_threejs_render: \n            self.threejs_renderer()\n        if game_end_code is not None:\n            # Battle is over\n            terminated = True\n            self.battles_game += 1\n            if game_end_code == 1 and not self.win_counted: # 鑳滃埄\n                print('\\t [starcraft]: win')\n                self.battles_won += 1\n                self.win_counted = True\n                info[\"battle_won\"] = True\n                if not self.reward_sparse: \n                    if self.reward_vec:\n                        assert False\n                        reward += 4  # <2> 鑳滃埄鐨勯澶栧鍔?                    else:\n                        reward += self.reward_win  # <2> 鑳滃埄鐨勯澶栧鍔?                else:\n                    reward = 1\n            elif game_end_code == -1 and not self.defeat_counted:   # 澶辫触\n                self.defeat_counted = True\n                if not self.reward_sparse:\n                    if self.reward_vec:\n                        assert False\n                        reward += -4  # <2> 鑳滃埄鐨勯澶栧鍔?                    else:\n                        reward += self.reward_defeat\n                else:\n                    reward = -1\n\n        elif self._episode_steps >= self.episode_limit:\n            # Episode limit reached\n            terminated = True\n            if self.continuing_episode:\n                info[\"episode_limit\"] = True\n            self.battles_game += 1\n            self.timeouts += 1\n\n        if self.debug:\n            logging.debug(\"Reward = {}\".format(reward).center(60, '-'))\n\n        if terminated:\n            self._episode_count += 1\n\n        # print(reward)\n        if self.reward_scale and (not self.reward_vec): # <3> 鑳滃埄鐨剆cale\n            reward /= self.max_reward / self.reward_scale_rate\n            # reward = reward / (self.max_reward/self.reward_scale_rate)\n        self.reward = reward\n\n        return reward, terminated, info\n\n    def get_agent_action(self, a_id, action):\n        \"\"\"Construct the action for agent a_id.\"\"\"\n        avail_actions = self.get_avail_agent_actions(a_id)\n        assert (\n            avail_actions[action] == 1\n        ), \"Agent {} cannot perform action {}\".format(a_id, action)\n\n        unit = self.get_unit_by_id(a_id)\n        tag = unit.tag\n        x = unit.pos.x\n        y = unit.pos.y\n\n        if action == 0:\n            # no-op (valid only when dead)\n            assert unit.health == 0, \"No-op only available for dead agents.\"\n            if self.debug:\n                logging.debug(\"Agent {}: Dead\".format(a_id))\n            return None\n        elif action == 1:\n            # stop\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"stop\"],\n                unit_tags=[tag],\n                queue_command=False,\n            )\n            if self.debug:\n                logging.debug(\"Agent {}: Stop\".format(a_id))\n\n        elif action == 2:\n            # move north\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"move\"],\n                target_world_space_pos=sc_common.Point2D(\n                    x=x, y=y + self._move_amount\n                ),\n                unit_tags=[tag],\n                queue_command=False,\n            )\n            if self.debug:\n                logging.debug(\"Agent {}: Move North\".format(a_id))\n\n        elif action == 3:\n            # move south\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"move\"],\n                target_world_space_pos=sc_common.Point2D(\n                    x=x, y=y - self._move_amount\n                ),\n                unit_tags=[tag],\n                queue_command=False,\n            )\n            if self.debug:\n                logging.debug(\"Agent {}: Move South\".format(a_id))\n\n        elif action == 4:\n            # move east\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"move\"],\n                target_world_space_pos=sc_common.Point2D(\n                    x=x + self._move_amount, y=y\n                ),\n                unit_tags=[tag],\n                queue_command=False,\n            )\n            if self.debug:\n                logging.debug(\"Agent {}: Move East\".format(a_id))\n\n        elif action == 5:\n            # move west\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"move\"],\n                target_world_space_pos=sc_common.Point2D(\n                    x=x - self._move_amount, y=y\n                ),\n                unit_tags=[tag],\n                queue_command=False,\n            )\n            if self.debug:\n                logging.debug(\"Agent {}: Move West\".format(a_id))\n        else:\n            # attack/heal units that are in range\n            target_id = action - self.n_actions_no_attack\n            if self.map_type == \"MMM\" and unit.unit_type == self.medivac_id:\n                target_unit = self.agents[target_id]\n                action_name = \"heal\"\n                self.action_heal[target_id].append(a_id)\n            else:\n                target_unit = self.enemies[target_id]\n                action_name = \"attack\"\n                self.action_blow[target_id].append(a_id)\n\n            action_id = actions[action_name]\n            target_tag = target_unit.tag\n\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=action_id,\n                target_unit_tag=target_tag,\n                unit_tags=[tag],\n                queue_command=False,\n            )\n\n            if self.debug:\n                logging.debug(\n                    \"Agent {} {}s unit # {}\".format(\n                        a_id, action_name, target_id\n                    )\n                )\n\n        sc_action = sc_pb.Action(action_raw=r_pb.ActionRaw(unit_command=cmd))\n        return sc_action\n\n    def get_agent_action_heuristic(self, a_id, action):\n        unit = self.get_unit_by_id(a_id)\n        tag = unit.tag\n\n        target = self.heuristic_targets[a_id]\n        if unit.unit_type == self.medivac_id:\n            if (\n                target is None\n                or self.agents[target].health == 0\n                or self.agents[target].health == self.agents[target].health_max\n            ):\n                min_dist = math.hypot(self.max_distance_x, self.max_distance_y)\n                min_id = -1\n                for al_id, al_unit in self.agents.items():\n                    if al_unit.unit_type == self.medivac_id:\n                        continue\n                    if (\n                        al_unit.health != 0\n                        and al_unit.health != al_unit.health_max\n                    ):\n                        dist = self.distance(\n                            unit.pos.x,\n                            unit.pos.y,\n                            al_unit.pos.x,\n                            al_unit.pos.y,\n                        )\n                        if dist < min_dist:\n                            min_dist = dist\n                            min_id = al_id\n                self.heuristic_targets[a_id] = min_id\n                if min_id == -1:\n                    self.heuristic_targets[a_id] = None\n                    return None, 0\n            action_id = actions[\"heal\"]\n            target_tag = self.agents[self.heuristic_targets[a_id]].tag\n        else:\n            if target is None or self.enemies[target].health == 0:\n                min_dist = math.hypot(self.max_distance_x, self.max_distance_y)\n                min_id = -1\n                for e_id, e_unit in self.enemies.items():\n                    if (\n                        unit.unit_type == self.marauder_id\n                        and e_unit.unit_type == self.medivac_id\n                    ):\n                        continue\n                    if e_unit.health > 0:\n                        dist = self.distance(\n                            unit.pos.x, unit.pos.y, e_unit.pos.x, e_unit.pos.y\n                        )\n                        if dist < min_dist:\n                            min_dist = dist\n                            min_id = e_id\n                self.heuristic_targets[a_id] = min_id\n                if min_id == -1:\n                    self.heuristic_targets[a_id] = None\n                    return None, 0\n            action_id = actions[\"attack\"]\n            target_tag = self.enemies[self.heuristic_targets[a_id]].tag\n\n        action_num = self.heuristic_targets[a_id] + self.n_actions_no_attack\n\n        # Check if the action is available\n        if (\n            self.heuristic_rest\n            and self.get_avail_agent_actions(a_id)[action_num] == 0\n        ):\n\n            # Move towards the target rather than attacking/healing\n            if unit.unit_type == self.medivac_id:\n                target_unit = self.agents[self.heuristic_targets[a_id]]\n            else:\n                target_unit = self.enemies[self.heuristic_targets[a_id]]\n\n            delta_x = target_unit.pos.x - unit.pos.x\n            delta_y = target_unit.pos.y - unit.pos.y\n\n            if abs(delta_x) > abs(delta_y):  # east or west\n                if delta_x > 0:  # east\n                    target_pos = sc_common.Point2D(\n                        x=unit.pos.x + self._move_amount, y=unit.pos.y\n                    )\n                    action_num = 4\n                else:  # west\n                    target_pos = sc_common.Point2D(\n                        x=unit.pos.x - self._move_amount, y=unit.pos.y\n                    )\n                    action_num = 5\n            else:  # north or south\n                if delta_y > 0:  # north\n                    target_pos = sc_common.Point2D(\n                        x=unit.pos.x, y=unit.pos.y + self._move_amount\n                    )\n                    action_num = 2\n                else:  # south\n                    target_pos = sc_common.Point2D(\n                        x=unit.pos.x, y=unit.pos.y - self._move_amount\n                    )\n                    action_num = 3\n\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=actions[\"move\"],\n                target_world_space_pos=target_pos,\n                unit_tags=[tag],\n                queue_command=False,\n            )\n        else:\n            # Attack/heal the target\n            cmd = r_pb.ActionRawUnitCommand(\n                ability_id=action_id,\n                target_unit_tag=target_tag,\n                unit_tags=[tag],\n                queue_command=False,\n            )\n\n        sc_action = sc_pb.Action(action_raw=r_pb.ActionRaw(unit_command=cmd))\n        return sc_action, action_num\n\n    def reward_battle(self):\n        \"\"\"Reward function when self.reward_spare==False.\n        Returns accumulative hit/shield point damage dealt to the enemy\n        + reward_death_value per enemy unit killed, and, in case\n        self.reward_only_positive == False, - (damage dealt to ally units\n        + reward_death_value per ally unit killed) * self.reward_negative_scale\n        \"\"\"\n        if self.reward_sparse:\n            return 0\n\n        reward = 0\n        delta_deaths = 0\n        delta_ally = 0\n        delta_enemy = 0\n\n        neg_scale = self.reward_negative_scale\n        if self.reward_vec:\n            assert False\n            reward_awise_compat = [0. for _ in self.agents] \n\n        # update deaths, al_id begin from 0\n        for al_id, al_unit in self.agents.items():\n            if not self.death_tracker_ally[al_id]:\n                # did not die so far\n                prev_health = (\n                    self.previous_ally_units[al_id].health\n                    + self.previous_ally_units[al_id].shield\n                )\n                if al_unit.health == 0:\n                    # just died\n                    self.death_tracker_ally[al_id] = 1\n                    if not self.reward_only_positive:\n                        delta_deaths -= self.reward_death_value * neg_scale # reward_death_value=10, reward_negative_scale=0.5, (-5)\n                    if self.reward_vec: \n                        reward_awise_compat[al_id] -= 1\n\n                    delta_ally += prev_health * neg_scale\n                else:\n                    # still alive\n                    delta_ally += neg_scale * (\n                        prev_health - al_unit.health - al_unit.shield\n                    )\n\n        # e_id also begin from 0\n        for e_id, e_unit in self.enemies.items():\n            if not self.death_tracker_enemy[e_id]:\n                prev_health = (\n                    self.previous_enemy_units[e_id].health\n                    + self.previous_enemy_units[e_id].shield\n                )\n                if e_unit.health == 0:\n                    # just died\n                    self.death_tracker_enemy[e_id] = 1\n                    delta_deaths += self.reward_death_value\n                    delta_enemy += prev_health\n                else:\n                    delta_enemy += prev_health - e_unit.health - e_unit.shield\n\n                if self.reward_vec:\n                    assert False\n                    if e_unit.health == 0:\n                        # just died\n                        for al_id in self.action_blow[e_id]:\n                            reward_awise_compat[al_id] += (-1) / len(self.action_blow[e_id])\n                    else:\n                        for al_id in self.action_blow[e_id]:\n                            scale_= 0.01\n                            reward_awise_compat[al_id] += (prev_health - (e_unit.health+e_unit.shield))*scale_ / len(self.action_blow[e_id])\n\n        if self.reward_only_positive:\n            reward = abs(delta_enemy + delta_deaths)  # shield regeneration\n        else:\n            reward = delta_enemy + delta_deaths - delta_ally\n\n        if self.reward_vec:\n            assert False\n            reward = np.array(reward_awise_compat)\n            \n        return reward\n\n    def get_total_actions(self):\n        \"\"\"Returns the total number of actions an agent could ever take.\"\"\"\n        return self.n_actions\n\n    @staticmethod\n    def distance(x1, y1, x2, y2):\n        \"\"\"Distance between two points.\"\"\"\n        return math.hypot(x2 - x1, y2 - y1)\n\n    def unit_shoot_range(self, agent_id):\n        \"\"\"Returns the shooting range for an agent.\"\"\"\n        return 6\n\n    def unit_sight_range(self, agent_id):\n        \"\"\"Returns the sight range for an agent.\"\"\"\n        return 9\n\n    def unit_max_cooldown(self, unit):\n        \"\"\"Returns the maximal cooldown for a unit.\"\"\"\n        switcher = {\n            self.marine_id: 15,\n            self.marauder_id: 25,\n            self.medivac_id: 200,  # max energy\n            self.stalker_id: 35,\n            self.zealot_id: 22,\n            self.colossus_id: 24,\n            self.hydralisk_id: 10,\n            self.zergling_id: 11,\n            self.baneling_id: 1,\n        }\n        return switcher.get(unit.unit_type, 15)\n\n    def save_replay(self):\n        \"\"\"Save a replay.\"\"\"\n        prefix = self.replay_prefix or self.map_name\n        replay_dir = self.replay_dir or \"\"\n        replay_path = self._run_config.save_replay(\n            self._controller.save_replay(),\n            replay_dir=replay_dir,\n            prefix=prefix,\n        )\n        logging.info(\"Replay saved at: %s\" % replay_path)\n\n    def unit_max_shield(self, unit):\n        \"\"\"Returns maximal shield for a given unit.\"\"\"\n        if unit.unit_type == 74 or unit.unit_type == self.stalker_id:\n            return 80  # Protoss's Stalker\n        if unit.unit_type == 73 or unit.unit_type == self.zealot_id:\n            return 50  # Protoss's Zaelot\n        if unit.unit_type == 4 or unit.unit_type == self.colossus_id:\n            return 150  # Protoss's Colossus\n\n    def can_move(self, unit, direction):\n        \"\"\"Whether a unit can move in a given direction.\"\"\"\n        m = self._move_amount / 2\n\n        if direction == Direction.NORTH:\n            x, y = int(unit.pos.x), int(unit.pos.y + m)\n        elif direction == Direction.SOUTH:\n            x, y = int(unit.pos.x), int(unit.pos.y - m)\n        elif direction == Direction.EAST:\n            x, y = int(unit.pos.x + m), int(unit.pos.y)\n        else:\n            x, y = int(unit.pos.x - m), int(unit.pos.y)\n\n        if self.check_bounds(x, y) and self.pathing_grid[x, y]:\n            return True\n\n        return False\n\n    def get_surrounding_points(self, unit, include_self=False):\n        \"\"\"Returns the surrounding points of the unit in 8 directions.\"\"\"\n        x = int(unit.pos.x)\n        y = int(unit.pos.y)\n\n        ma = self._move_amount\n\n        points = [\n            (x, y + 2 * ma),\n            (x, y - 2 * ma),\n            (x + 2 * ma, y),\n            (x - 2 * ma, y),\n            (x + ma, y + ma),\n            (x - ma, y - ma),\n            (x + ma, y - ma),\n            (x - ma, y + ma),\n        ]\n\n        if include_self:\n            points.append((x, y))\n\n        return points\n\n    def check_bounds(self, x, y):\n        \"\"\"Whether a point is within the map bounds.\"\"\"\n        return (0 <= x < self.map_x and 0 <= y < self.map_y)\n\n    def get_surrounding_pathing(self, unit):\n        \"\"\"Returns pathing values of the grid surrounding the given unit.\"\"\"\n        points = self.get_surrounding_points(unit, include_self=False)\n        vals = [\n            self.pathing_grid[x, y] if self.check_bounds(x, y) else 1\n            for x, y in points\n        ]\n        return vals\n\n    def get_surrounding_height(self, unit):\n        \"\"\"Returns height values of the grid surrounding the given unit.\"\"\"\n        points = self.get_surrounding_points(unit, include_self=True)\n        vals = [\n            self.terrain_height[x, y] if self.check_bounds(x, y) else 1\n            for x, y in points\n        ]\n        return vals\n\n\n    @staticmethod\n    def stack_vec_with_padding(arr_list, max_len=None, padding=0):\n        _len = [len(arr) for arr in arr_list]\n        if max_len is None: max_len = max(_len)\n        n_subject = len(arr_list)\n        dtype = arr_list[0].dtype\n        arr_np = np.zeros(shape=(n_subject, max_len), dtype=dtype) + padding\n        for i, arr in enumerate(arr_list):\n            arr_np[i,:_len[i]] = arr\n        return arr_np\n\n    def get_obs_agent(self, agent_id, return_mat=False):\n        \"\"\"Returns observation for agent_id. The observation is composed of:\n\n        - agent movement features (where it can move to, height information\n            and pathing grid)\n        - enemy features (available_to_attack, health, relative_x, relative_y,\n            shield, unit_type)\n        - ally features (visible, distance, relative_x, relative_y, shield,\n            unit_type)\n        - agent unit features (health, shield, unit_type)\n\n        All of this information is flattened and concatenated into a list,\n        in the aforementioned order. To know the sizes of each of the\n        features inside the final list of features, take a look at the\n        functions ``get_obs_move_feats_size()``,\n        ``get_obs_enemy_feats_size()``, ``get_obs_ally_feats_size()`` and\n        ``get_obs_own_feats_size()``.\n\n        The size of the observation vector may vary, depending on the\n        environment configuration and type of units present in the map.\n        For instance, non-Protoss units will not have shields, movement\n        features may or may not include terrain height and pathing grid,\n        unit_type is not included if there is only one type of unit in the\n        map etc.).\n\n        NOTE: Agents should have access only to their local observations\n        during decentralised execution.\n        \"\"\"\n        unit = self.get_unit_by_id(agent_id)\n\n        move_feats_dim = self.get_obs_move_feats_size()\n        enemy_feats_dim = self.get_obs_enemy_feats_size()\n        ally_feats_dim = self.get_obs_ally_feats_size()\n        own_feats_dim = self.get_obs_own_feats_size()\n\n        move_feats = np.zeros(move_feats_dim, dtype=np.float32)\n        enemy_feats = np.zeros(enemy_feats_dim, dtype=np.float32)\n        ally_feats = np.zeros(ally_feats_dim, dtype=np.float32)\n        own_feats = np.zeros(own_feats_dim, dtype=np.float32)\n\n        if unit.health > 0:  # otherwise dead, return all zeros\n            x = unit.pos.x\n            y = unit.pos.y\n            sight_range = self.unit_sight_range(agent_id)\n\n            # Movement features\n            avail_actions = self.get_avail_agent_actions(agent_id)\n            for m in range(self.n_actions_move):\n                move_feats[m] = avail_actions[m + 2]\n\n            ind = self.n_actions_move\n\n            if self.obs_pathing_grid:\n                move_feats[\n                    ind : ind + self.n_obs_pathing  # noqa\n                ] = self.get_surrounding_pathing(unit)\n                ind += self.n_obs_pathing\n\n            if self.obs_terrain_height:\n                move_feats[ind:] = self.get_surrounding_height(unit)\n\n            # Enemy features\n            for e_id, e_unit in self.enemies.items():\n                e_x = e_unit.pos.x\n                e_y = e_unit.pos.y\n                dist = self.distance(x, y, e_x, e_y)\n\n                if (\n                    dist < sight_range and e_unit.health > 0\n                ):  # visible and alive\n                    # Sight range > shoot range\n                    enemy_feats[e_id, 0] = avail_actions[\n                        self.n_actions_no_attack + e_id\n                    ]  # available\n                    enemy_feats[e_id, 1] = dist / sight_range  # distance\n                    enemy_feats[e_id, 2] = (\n                        e_x - x\n                    ) / sight_range  # relative X\n                    enemy_feats[e_id, 3] = (\n                        e_y - y\n                    ) / sight_range  # relative Y\n\n                    ind = 4\n                    if self.obs_all_health:\n                        enemy_feats[e_id, ind] = (\n                            e_unit.health / e_unit.health_max\n                        )  # health\n                        ind += 1\n                        if self.shield_bits_enemy > 0:\n                            max_shield = self.unit_max_shield(e_unit)\n                            enemy_feats[e_id, ind] = (\n                                e_unit.shield / max_shield\n                            )  # shield\n                            ind += 1\n\n                    if self.unit_type_bits > 0:\n                        type_id = self.get_unit_type_id(e_unit, False)\n                        enemy_feats[e_id, ind + type_id] = 1  # unit type\n\n            # Ally features\n            al_ids = [\n                al_id for al_id in range(self.n_agents) if al_id != agent_id\n            ]\n            for i, al_id in enumerate(al_ids):\n\n                al_unit = self.get_unit_by_id(al_id)\n                al_x = al_unit.pos.x\n                al_y = al_unit.pos.y\n                dist = self.distance(x, y, al_x, al_y)\n\n                if (\n                    dist < sight_range and al_unit.health > 0\n                ):  # visible and alive\n                    ally_feats[i, 0] = 1  # visible\n                    ally_feats[i, 1] = dist / sight_range  # distance\n                    ally_feats[i, 2] = (al_x - x) / sight_range  # relative X\n                    ally_feats[i, 3] = (al_y - y) / sight_range  # relative Y\n\n                    ind = 4\n                    if self.obs_all_health:\n                        ally_feats[i, ind] = (\n                            al_unit.health / al_unit.health_max\n                        )  # health\n                        ind += 1\n                        if self.shield_bits_ally > 0:\n                            max_shield = self.unit_max_shield(al_unit)\n                            ally_feats[i, ind] = (\n                                al_unit.shield / max_shield\n                            )  # shield\n                            ind += 1\n\n                    if self.unit_type_bits > 0:\n                        type_id = self.get_unit_type_id(al_unit, True)\n                        ally_feats[i, ind + type_id] = 1\n                        ind += self.unit_type_bits\n\n                    if self.obs_last_action:\n                        ally_feats[i, ind:] = self.last_action[al_id]\n\n            # Own features\n            ind = 0\n            if self.obs_own_health:\n                own_feats[ind] = unit.health / unit.health_max\n                ind += 1\n                if self.shield_bits_ally > 0:\n                    max_shield = self.unit_max_shield(unit)\n                    own_feats[ind] = unit.shield / max_shield\n                    ind += 1\n\n            if self.unit_type_bits > 0:\n                type_id = self.get_unit_type_id(unit, True)\n                own_feats[ind + type_id] = 1\n\n        if not return_mat:\n            agent_obs = np.concatenate(\n                (\n                    move_feats.flatten(),\n                    enemy_feats.flatten(),\n                    ally_feats.flatten(),\n                    own_feats.flatten(),\n                )\n            )\n    \n            if self.obs_timestep_number:\n                agent_obs = np.append(\n                    agent_obs, self._episode_steps / self.episode_limit)\n\n        if return_mat:\n            assert False\n            # ally_feats  = np.concatenate((ally_feats,  -1 * np.ones_like(ally_feats[:,(0,)] )), -1)\n            # enemy_feats = np.concatenate((enemy_feats, +1 * np.ones_like(enemy_feats[:,(0,)])), -1)\n            max_len = len(enemy_feats) + len(ally_feats) + 1 # maximum len of core dimension\n\n            _core_max_len = max(\n                move_feats_dim+own_feats_dim,\n                max(enemy_feats_dim[1], ally_feats_dim[1])+1\n            )\n            agent_obs = self.stack_vec_with_padding([\n                                np.concatenate((  move_feats.flatten(),  own_feats.flatten()  )),\n                            *(  np.append(feat, -1) for feat in enemy_feats if not (feat==0).all()  ),\n                            *(  np.append(feat, +1) for feat in ally_feats if not (feat==0).all()   ),\n                        ], max_len=_core_max_len, padding=0)     #   shape = (??, max_len)\n            padding = np.zeros(shape=(max_len - agent_obs.shape[0], agent_obs.shape[1]), dtype=agent_obs.dtype) + np.nan    #   shape = (max_len - ??, max_len)\n            agent_obs = np.concatenate((agent_obs, padding), 0)\n            if (unit.health <= 0): agent_obs+=np.nan\n            # print('agent_obs', agent_obs)\n            # pad to max_len with np.nan to compat\n            # (30, 7)\n\n        if self.debug:\n            logging.debug(\"Obs Agent: {}\".format(agent_id).center(60, \"-\"))\n            logging.debug(\n                \"Avail. actions {}\".format(\n                    self.get_avail_agent_actions(agent_id)\n                )\n            )\n            logging.debug(\"Move feats {}\".format(move_feats))\n            logging.debug(\"Enemy feats {}\".format(enemy_feats))\n            logging.debug(\"Ally feats {}\".format(ally_feats))\n            logging.debug(\"Own feats {}\".format(own_feats))\n\n        return agent_obs\n\n    def get_obs(self):\n        \"\"\"Returns all agent observations in a list.\n        NOTE: Agents should have access only to their local observations\n        during decentralised execution.\n        \"\"\"\n        agents_obs = [self.get_obs_agent(i, self.return_mat) for i in range(self.n_agents)]\n        return agents_obs\n\n    def get_state(self):\n        \"\"\"Returns the global state.\n        NOTE: This functon should not be used during decentralised execution.\n        \"\"\"\n        if self.obs_instead_of_state:\n            obs_concat = np.concatenate(self.get_obs(), axis=0).astype(\n                np.float32\n            )\n            return obs_concat\n\n        state_dict = self.get_state_dict()\n\n        state = np.append(\n            state_dict[\"allies\"].flatten(), state_dict[\"enemies\"].flatten()\n        )\n        if \"last_action\" in state_dict:\n            state = np.append(state, state_dict[\"last_action\"].flatten())\n        if \"timestep\" in state_dict:\n            state = np.append(state, state_dict[\"timestep\"])\n\n        state = state.astype(dtype=np.float32)\n\n        if self.debug:\n            logging.debug(\"STATE\".center(60, \"-\"))\n            logging.debug(\"Ally state {}\".format(state_dict[\"allies\"]))\n            logging.debug(\"Enemy state {}\".format(state_dict[\"enemies\"]))\n            if self.state_last_action:\n                logging.debug(\"Last actions {}\".format(self.last_action))\n\n        return state\n\n    def get_ally_num_attributes(self):\n        return len(self.ally_state_attr_names)\n\n    def get_enemy_num_attributes(self):\n        return len(self.enemy_state_attr_names)\n\n    def get_state_dict(self):\n        \"\"\"Returns the global state as a dictionary.\n\n        - allies: numpy array containing agents and their attributes\n        - enemies: numpy array containing enemies and their attributes\n        - last_action: numpy array of previous actions for each agent\n        - timestep: current no. of steps divided by total no. of steps\n\n        NOTE: This function should not be used during decentralised execution.\n        \"\"\"\n\n        # number of features equals the number of attribute names\n        nf_al = self.get_ally_num_attributes()\n        nf_en = self.get_enemy_num_attributes()\n\n        ally_state = np.zeros((self.n_agents, nf_al))\n        enemy_state = np.zeros((self.n_enemies, nf_en))\n\n        center_x = self.map_x / 2\n        center_y = self.map_y / 2\n\n        for al_id, al_unit in self.agents.items():\n            if al_unit.health > 0:\n                x = al_unit.pos.x\n                y = al_unit.pos.y\n                max_cd = self.unit_max_cooldown(al_unit)\n\n                ally_state[al_id, 0] = (\n                    al_unit.health / al_unit.health_max\n                )  # health\n                if (\n                    self.map_type == \"MMM\"\n                    and al_unit.unit_type == self.medivac_id\n                ):\n                    ally_state[al_id, 1] = al_unit.energy / max_cd  # energy\n                else:\n                    ally_state[al_id, 1] = (\n                        al_unit.weapon_cooldown / max_cd\n                    )  # cooldown\n                ally_state[al_id, 2] = (\n                    x - center_x\n                ) / self.max_distance_x  # relative X\n                ally_state[al_id, 3] = (\n                    y - center_y\n                ) / self.max_distance_y  # relative Y\n\n                if self.shield_bits_ally > 0:\n                    max_shield = self.unit_max_shield(al_unit)\n                    ally_state[al_id, 4] = (\n                        al_unit.shield / max_shield\n                    )  # shield\n\n                if self.unit_type_bits > 0:\n                    type_id = self.get_unit_type_id(al_unit, True)\n                    ally_state[al_id, type_id - self.unit_type_bits] = 1\n\n        for e_id, e_unit in self.enemies.items():\n            if e_unit.health > 0:\n                x = e_unit.pos.x\n                y = e_unit.pos.y\n\n                enemy_state[e_id, 0] = (\n                    e_unit.health / e_unit.health_max\n                )  # health\n                enemy_state[e_id, 1] = (\n                    x - center_x\n                ) / self.max_distance_x  # relative X\n                enemy_state[e_id, 2] = (\n                    y - center_y\n                ) / self.max_distance_y  # relative Y\n\n                if self.shield_bits_enemy > 0:\n                    max_shield = self.unit_max_shield(e_unit)\n                    enemy_state[e_id, 3] = e_unit.shield / max_shield  # shield\n\n                if self.unit_type_bits > 0:\n                    type_id = self.get_unit_type_id(e_unit, False)\n                    enemy_state[e_id, type_id - self.unit_type_bits] = 1\n\n        state = {\"allies\": ally_state, \"enemies\": enemy_state}\n\n        if self.state_last_action:\n            state[\"last_action\"] = self.last_action\n        if self.state_timestep_number:\n            state[\"timestep\"] = self._episode_steps / self.episode_limit\n\n        return state\n\n    def get_obs_enemy_feats_size(self):\n        \"\"\"Returns the dimensions of the matrix containing enemy features.\n        Size is n_enemies x n_features.\n        \"\"\"\n        nf_en = 4 + self.unit_type_bits\n\n        if self.obs_all_health:\n            nf_en += 1 + self.shield_bits_enemy\n\n        return self.n_enemies, nf_en\n\n    def get_obs_ally_feats_size(self):\n        \"\"\"Returns the dimensions of the matrix containing ally features.\n        Size is n_allies x n_features.\n        \"\"\"\n        nf_al = 4 + self.unit_type_bits\n\n        if self.obs_all_health:\n            nf_al += 1 + self.shield_bits_ally\n\n        if self.obs_last_action:\n            nf_al += self.n_actions\n\n        return self.n_agents - 1, nf_al\n\n    def get_obs_own_feats_size(self):\n        \"\"\"\n        Returns the size of the vector containing the agents' own features.\n        \"\"\"\n        own_feats = self.unit_type_bits\n        if self.obs_own_health:\n            own_feats += 1 + self.shield_bits_ally\n        if self.obs_timestep_number:\n            own_feats += 1\n\n        return own_feats\n\n    def get_obs_move_feats_size(self):\n        \"\"\"Returns the size of the vector containing the agents's movement-\n        related features.\n        \"\"\"\n        move_feats = self.n_actions_move\n        if self.obs_pathing_grid:\n            move_feats += self.n_obs_pathing\n        if self.obs_terrain_height:\n            move_feats += self.n_obs_height\n\n        return move_feats\n\n    def get_obs_size(self):\n        \"\"\"Returns the size of the observation.\"\"\"\n        own_feats = self.get_obs_own_feats_size()\n        move_feats = self.get_obs_move_feats_size()\n\n        n_enemies, n_enemy_feats = self.get_obs_enemy_feats_size()\n        n_allies, n_ally_feats = self.get_obs_ally_feats_size()\n        if not self.return_mat:\n            enemy_feats = n_enemies * n_enemy_feats\n            ally_feats = n_allies * n_ally_feats\n    \n            return move_feats + enemy_feats + ally_feats + own_feats\n        else:\n            return (\n                n_allies + n_enemies + 1,\n                max(max(own_feats+move_feats, n_enemy_feats+1), n_ally_feats+1)\n                )\n\n\n    def get_state_size(self):\n        \"\"\"Returns the size of the global state.\"\"\"\n        if self.obs_instead_of_state:\n            return self.get_obs_size() * self.n_agents\n\n        nf_al = 4 + self.shield_bits_ally + self.unit_type_bits\n        nf_en = 3 + self.shield_bits_enemy + self.unit_type_bits\n\n        enemy_state = self.n_enemies * nf_en\n        ally_state = self.n_agents * nf_al\n\n        size = enemy_state + ally_state\n\n        if self.state_last_action:\n            size += self.n_agents * self.n_actions\n        if self.state_timestep_number:\n            size += 1\n\n        return size\n\n    def get_visibility_matrix(self):\n        \"\"\"Returns a boolean numpy array of dimensions\n        (n_agents, n_agents + n_enemies) indicating which units\n        are visible to each agent.\n        \"\"\"\n        arr = np.zeros(\n            (self.n_agents, self.n_agents + self.n_enemies),\n            dtype=np.bool,\n        )\n\n        for agent_id in range(self.n_agents):\n            current_agent = self.get_unit_by_id(agent_id)\n            if current_agent.health > 0:  # it agent not dead\n                x = current_agent.pos.x\n                y = current_agent.pos.y\n                sight_range = self.unit_sight_range(agent_id)\n\n                # Enemies\n                for e_id, e_unit in self.enemies.items():\n                    e_x = e_unit.pos.x\n                    e_y = e_unit.pos.y\n                    dist = self.distance(x, y, e_x, e_y)\n\n                    if (dist < sight_range and e_unit.health > 0):\n                        # visible and alive\n                        arr[agent_id, self.n_agents + e_id] = 1\n\n                # The matrix for allies is filled symmetrically\n                al_ids = [\n                    al_id for al_id in range(self.n_agents) if al_id > agent_id\n                ]\n                for _, al_id in enumerate(al_ids):\n                    al_unit = self.get_unit_by_id(al_id)\n                    al_x = al_unit.pos.x\n                    al_y = al_unit.pos.y\n                    dist = self.distance(x, y, al_x, al_y)\n\n                    if (dist < sight_range and al_unit.health > 0):\n                        # visible and alive\n                        arr[agent_id, al_id] = arr[al_id, agent_id] = 1\n\n        return arr\n\n    def get_unit_type_id(self, unit, ally):\n        \"\"\"Returns the ID of unit type in the given scenario.\"\"\"\n        if ally:  # use new SC2 unit types\n            type_id = unit.unit_type - self._min_unit_type\n        else:  # use default SC2 unit types\n            if self.map_type == \"stalkers_and_zealots\":\n                # id(Stalker) = 74, id(Zealot) = 73\n                type_id = unit.unit_type - 73\n            elif self.map_type == \"colossi_stalkers_zealots\":\n                # id(Stalker) = 74, id(Zealot) = 73, id(Colossus) = 4\n                if unit.unit_type == 4:\n                    type_id = 0\n                elif unit.unit_type == 74:\n                    type_id = 1\n                else:\n                    type_id = 2\n            elif self.map_type == \"bane\":\n                if unit.unit_type == 9:\n                    type_id = 0\n                else:\n                    type_id = 1\n            elif self.map_type == \"MMM\":\n                if unit.unit_type == 51:\n                    type_id = 0\n                elif unit.unit_type == 48:\n                    type_id = 1\n                else:\n                    type_id = 2\n\n        return type_id\n\n    def get_avail_agent_actions(self, agent_id):\n        \"\"\"Returns the available actions for agent_id.\"\"\"\n        unit = self.get_unit_by_id(agent_id)\n        if unit.health > 0:\n            # cannot choose no-op when alive\n            avail_actions = [0] * self.n_actions\n\n            # stop should be allowed\n            avail_actions[1] = 1\n\n            # see if we can move\n            if self.can_move(unit, Direction.NORTH):\n                avail_actions[2] = 1\n            if self.can_move(unit, Direction.SOUTH):\n                avail_actions[3] = 1\n            if self.can_move(unit, Direction.EAST):\n                avail_actions[4] = 1\n            if self.can_move(unit, Direction.WEST):\n                avail_actions[5] = 1\n\n            # Can attack only alive units that are alive in the shooting range\n            shoot_range = self.unit_shoot_range(agent_id)\n\n            target_items = self.enemies.items()\n            if self.map_type == \"MMM\" and unit.unit_type == self.medivac_id:\n                # Medivacs cannot heal themselves or other flying units\n                target_items = [\n                    (t_id, t_unit)\n                    for (t_id, t_unit) in self.agents.items()\n                    if t_unit.unit_type != self.medivac_id\n                ]\n\n            for t_id, t_unit in target_items:\n                if t_unit.health > 0:\n                    dist = self.distance(\n                        unit.pos.x, unit.pos.y, t_unit.pos.x, t_unit.pos.y\n                    )\n                    if dist <= shoot_range:\n                        avail_actions[t_id + self.n_actions_no_attack] = 1\n\n            return avail_actions\n\n        else:\n            # only no-op allowed\n            return [1] + [0] * (self.n_actions - 1)\n\n    def get_avail_actions(self):\n        \"\"\"Returns the available actions of all agents in a list.\"\"\"\n        avail_actions = []\n        for agent_id in range(self.n_agents):\n            avail_agent = self.get_avail_agent_actions(agent_id)\n            avail_actions.append(avail_agent)\n        return avail_actions\n\n    def close(self):\n        \"\"\"Close StarCraft II.\"\"\"\n        if self.renderer is not None:\n            self.renderer.close()\n            self.renderer = None\n        if self._sc2_proc:\n            self._sc2_proc.close()\n\n    def seed(self):\n        \"\"\"Returns the random seed used by the environment.\"\"\"\n        return self._seed\n\n    def render_original(self, mode=\"human\"):\n        if self.renderer is None:\n            from smac.env.starcraft2.render import StarCraft2Renderer\n\n            self.renderer = StarCraft2Renderer(self, mode)\n        assert (\n            mode == self.renderer.mode\n        ), \"mode must be consistent across render calls\"\n        return self.renderer.render(mode)\n\n    def _kill_all_units(self):\n        \"\"\"Kill all units on the map.\"\"\"\n        units_alive = [\n            unit.tag for unit in self.agents.values() if unit.health > 0\n        ] + [unit.tag for unit in self.enemies.values() if unit.health > 0]\n        debug_command = [\n            d_pb.DebugCommand(kill_unit=d_pb.DebugKillUnit(tag=units_alive))\n        ]\n        self._controller.debug(debug_command)\n\n    def init_units(self):\n        \"\"\"Initialise the units.\"\"\"\n        while True:\n            # Sometimes not all units have yet been created by SC2\n            self.agents = {}\n            self.enemies = {}\n            # ------------ ally units ----------\n            ally_units = [\n                unit\n                for unit in self._obs.observation.raw_data.units\n                if unit.owner == 1\n            ]\n            ally_units_sorted = sorted(\n                ally_units,\n                key=attrgetter(\"unit_type\", \"pos.x\", \"pos.y\"),\n                reverse=False,\n            )\n\n            for i in range(len(ally_units_sorted)):\n                self.agents[i] = ally_units_sorted[i]\n                if self.debug:\n                    logging.debug(\n                        \"Unit {} is {}, x = {}, y = {}\".format(\n                            len(self.agents),\n                            self.agents[i].unit_type,\n                            self.agents[i].pos.x,\n                            self.agents[i].pos.y,\n                        )\n                    )\n\n            # ------------ enemy units ----------\n            for unit in self._obs.observation.raw_data.units:\n                if unit.owner == 2:\n                    self.enemies[len(self.enemies)] = unit\n                    if self._episode_count == 0:\n                        self.max_reward += unit.health_max + unit.shield_max\n\n            if self._episode_count == 0:\n                min_unit_type = min(\n                    unit.unit_type for unit in self.agents.values()\n                )\n                self._init_ally_unit_types(min_unit_type)\n\n            all_agents_created = (len(self.agents) == self.n_agents)\n            all_enemies_created = (len(self.enemies) == self.n_enemies)\n\n            self._unit_types = [\n                unit.unit_type for unit in ally_units_sorted\n            ] + [\n                unit.unit_type\n                for unit in self._obs.observation.raw_data.units\n                if unit.owner == 2\n            ]\n\n            if all_agents_created and all_enemies_created:  # all good\n                return\n\n            try:\n                self._controller.step(1)\n                self._obs = self._controller.observe()\n            except (protocol.ProtocolError, protocol.ConnectionError):\n                self.full_restart()\n                self.reset()\n\n    def get_unit_types(self):\n        if self._unit_types is None:\n            warn(\n                \"unit types have not been initialized yet, please call\"\n                \"env.reset() to populate this and call t1286he method again.\"\n            )\n\n        return self._unit_types\n\n    def update_units(self):\n        \"\"\"Update units after an environment step.\n        This function assumes that self._obs is up-to-date.\n        \"\"\"\n        n_ally_alive = 0\n        n_enemy_alive = 0\n\n        # Store previous state\n        self.previous_ally_units = deepcopy(self.agents)\n        self.previous_enemy_units = deepcopy(self.enemies)\n\n        for al_id, al_unit in self.agents.items():\n            updated = False\n            for unit in self._obs.observation.raw_data.units:\n                if al_unit.tag == unit.tag:\n                    self.agents[al_id] = unit\n                    updated = True\n                    n_ally_alive += 1\n                    break\n\n            if not updated:  # dead\n                al_unit.health = 0\n\n        for e_id, e_unit in self.enemies.items():\n            updated = False\n            for unit in self._obs.observation.raw_data.units:\n                if e_unit.tag == unit.tag:\n                    self.enemies[e_id] = unit\n                    updated = True\n                    n_enemy_alive += 1\n                    break\n\n            if not updated:  # dead\n                e_unit.health = 0\n\n        if (\n            n_ally_alive == 0\n            and n_enemy_alive > 0\n            or self.only_medivac_left(ally=True)\n        ):\n            return -1  # lost\n        if (\n            n_ally_alive > 0\n            and n_enemy_alive == 0\n            or self.only_medivac_left(ally=False)\n        ):\n            return 1  # won\n        if n_ally_alive == 0 and n_enemy_alive == 0:\n            return 0\n\n        return None\n\n    def _init_ally_unit_types(self, min_unit_type):\n        \"\"\"Initialise ally unit types. Should be called once from the\n        init_units function.\n        \"\"\"\n        self._min_unit_type = min_unit_type\n        if self.map_type == \"marines\":\n            self.marine_id = min_unit_type\n        elif self.map_type == \"stalkers_and_zealots\":\n            self.stalker_id = min_unit_type\n            self.zealot_id = min_unit_type + 1\n        elif self.map_type == \"colossi_stalkers_zealots\":\n            self.colossus_id = min_unit_type\n            self.stalker_id = min_unit_type + 1\n            self.zealot_id = min_unit_type + 2\n        elif self.map_type == \"MMM\":\n            self.marauder_id = min_unit_type\n            self.marine_id = min_unit_type + 1\n            self.medivac_id = min_unit_type + 2\n        elif self.map_type == \"zealots\":\n            self.zealot_id = min_unit_type\n        elif self.map_type == \"hydralisks\":\n            self.hydralisk_id = min_unit_type\n        elif self.map_type == \"stalkers\":\n            self.stalker_id = min_unit_type\n        elif self.map_type == \"colossus\":\n            self.colossus_id = min_unit_type\n        elif self.map_type == \"bane\":\n            self.baneling_id = min_unit_type\n            self.zergling_id = min_unit_type + 1\n\n    def only_medivac_left(self, ally):\n        \"\"\"Check if only Medivac units are left.\"\"\"\n        if self.map_type != \"MMM\":\n            return False\n\n        if ally:\n            units_alive = [\n                a\n                for a in self.agents.values()\n                if (a.health > 0 and a.unit_type != self.medivac_id)\n            ]\n            if len(units_alive) == 0:\n                return True\n            return False\n        else:\n            units_alive = [\n                a\n                for a in self.enemies.values()\n                if (a.health > 0 and a.unit_type != self.medivac_id)\n            ]\n            if len(units_alive) == 1 and units_alive[0].unit_type == 54:\n                return True\n            return False\n\n    def get_unit_by_id(self, a_id):\n        \"\"\"Get unit by ID.\"\"\"\n        return self.agents[a_id]\n\n    def get_stats(self):\n        stats = {\n            \"battles_won\": self.battles_won,\n            \"battles_game\": self.battles_game,\n            \"battles_draw\": self.timeouts,\n            \"win_rate\": self.battles_won / self.battles_game,\n            \"timeouts\": self.timeouts,\n            \"restarts\": self.force_restarts,\n        }\n        return stats\n\n    def get_env_info(self):\n        env_info = super().get_env_info()\n        env_info[\"agent_features\"] = self.ally_state_attr_names\n        env_info[\"enemy_features\"] = self.enemy_state_attr_names\n        return env_info"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/setup_docker.md",
    "content": "\n# 1. Install nvidia docker runtime\nCuda is needed inside our docker container, which need toolkits from Nvidia for GPU support.\nPlease install nvidia docker runtime on the host ubuntu system.\n\nFor details, refer to nvidia official document: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-on-ubuntu-and-debian\n\nAccording the link above, we write a manual about installing nvidia docker runtime:\nPlease read [SetupUbuntu](./setup_ubuntu.md).\n\n\n\n# 2. Start docker container\nFrom the host:\n```bash\n$ docker run -itd   --name  hmp-$USER \\\n--net host \\\n--gpus all \\\n--shm-size=16G \\\nfuqingxu/hmp:latest\n```\nWarning! Need at least 50GB disk space because cuda, Starcraft environment and all needed python package is packed inside.\n\nWarning! we use ```--net host``` to bridge the docker container for a lot of convenience.\n\nUnpredictable errors may occur if the port inside container conflict with the host network, e.g. port 3389(rdp), 6379(redis), 2233(ssh), make sure the host system is not using them!\n\nUnpredictable errors may occur if you decide to use ```-p``` parameter to mount other ports.\n\nFinally check docker status with ```docker ps```, should be seeing a container named ```hmp``` at running state.\n\n\n\n# (3. Optional) Get inside HMP container via SSH\n```\n$ docker exec -it hmp-$USER service ssh start\n```\n\nNow find a computer to ssh into it: ```ssh hmp@your_host_ip -p 2233```\n```\n# IP Addr: share with the host\n# SSH Port 2233\n# UserName: hmp\n# Password: hmp\n```\n\nNote: The environment is not configured in the ```root``` account! \nIf you enter directly after ```docker run``` (not using ssh), \nyou have to switch the account manually from ```root``` to ```hmp``` (using linux command ```su hmp```), \n\n\n# (3. Optional) Connect to HMP container with remote desktop (RDP)\n(choice 1) Use SSH to get ```inside``` the HMP container.\n\n(choice 2) From the host, use ``` docker exec -it hmp-$USER bash ``` command to get inside the HMP container.\n\nThen:\n```sh\n# before continue, make sure the host port 3389 is free to use for RDP\n\n(hmp-docker)$ sudo /etc/init.d/xrdp stop; sleep 5;\n(hmp-docker)$ sudo rm -rf /var/run/xrdp/xrdp-sesman.pid; sleep 5;\n(hmp-docker)$ sudo xrdp; sleep 5;\n(hmp-docker)$ sudo /etc/init.d/xrdp start; sleep 5;\n```\nNow, you should see xrdp-sesman running via:\n```sh\n(hmp-docker)$ /etc/init.d/xrdp status\n\n# Successful if you see >>\n#   * xrdp-sesman is running\n#   * xrdp is running\n\n# note: if multiple instances of hmp-docker is running,\n# you should modify following settings to avoid port collision into some value that is not default:\n#  /etc/xrdp/sesman.ini: The 'X11DisplayOffset' and 'ListenPort' option\n#  /etc/xrdp/xrdp.ini: The 'port' option\n```\n\n\n\nNext, use the remote desktop tool of MS Windows (or anything supporting RDP) to get inside the HMP container.\n```\n# IP Addr: share with the host\n# RDP Port: 3389.\n# UserName: hmp\n# Password: hmp\n\n(It's normal that xrdp is a bit slow, but there is no better RDP solution for docker container yet, please use SSH when GUI is not needed)\n```\n\n# 4. Run HMP\nAfter getting ```inside``` the HMP container:\n\n```\n# if current user is 'root', change to a user with name 'hmp' (password also 'hmp'):\n(hmp-container)$ su hmp\n\n# goto its home directory\n(hmp-container)$ cd\n\n# clone rep from github:\n(hmp-container)$ git clone https://github.com/binary-husky/hmp2g.git\n\n# or gitee (sync once a week, may not be the latest, please use gitee rep if possible)\n(hmp-container)$ git clone https://gitee.com/hh505030475/hmp-2g.git\n\n# cd into it.\n(hmp-container)$ cd hmp2g\n\n# run an trained model to find out if everthing works well ^_^\n(hmp-container)$ git pull && python main.py -c ZHECKPOINT/50RL-55opp/test-50RL-55opp.jsonc\n\n```\n<img src=\"../ZHECKPOINT/test-50+50/butterfly.webp\" width=\"200\" >\n\n# Docker in Docker (If need to run air combat env)\n\nIf you want to play ```docker in docker```, please mount ```/var/run/docker.sock```:\n```bash\ndocker run -itd   --name  hmp-$USER \\\n--volume /var/run/docker.sock:/var/run/docker.sock \\\n--net host \\\n--gpus all \\\n--shm-size=16G \\\nfuqingxu/hmp:latest\n```\n<img src=\"../ZHECKPOINT/test-50+50/butterfly.webp\" width=\"200\" >\n\n# Appendix：requirement.txt (install on Windows)\nIf possible, please ```use docker``` to Avoid following\npip package management.\nThis requirement list is provided only as \na reminder of dependencies being used,\n```do NOT use it for configuration unless no other choice is available!```\n\n\nPlease read [pip_requirement](pip_requirement.md)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/setup_no_docker.md",
    "content": "# setup on ubuntu without docker\n\nWarning! Always use docker if possible! \n\n如果可以选择，请一定先尝试docker方案\n\nThis file is written for those who are very confident to solve all kinds of problems and errors on their own!\n\n本文件仅写给对解决各种软件依赖十分自信的老手作为参考\n\n## python version\n\n``` sh\npython 3.8\n```\n## pip requirements \nPlease read [pip_requirement](pip_requirement.md)\n\n\n## Download and extract starcraft\n``` sh\ncd /home/hmp\ngit clone https://github.com/binary-husky/uhmap-visual-tool.git\npython linux_deploy_starcraft_all_versions.py\nmv /home/hmp/uhmap-visual-tool/UnrealEngine/home/hmp/*  /home/hmp\n```\n\n## Download Unreal-HMAP binary client\nPlease read [get UHMP](use_unreal_hmap.md)\n<!-- ## download and extract starcraft and unreal engine\n``` sh\ncd /home/hmp\ngit clone https://github.com/binary-husky/uhmap-visual-tool.git\ncd /home/hmp/uhmap-visual-tool/\npython linux_deploy.py\npython linux_deploy_starcraft_all_versions.py\nmv /home/hmp/uhmap-visual-tool/UnrealEngine/home/hmp/*  /home/hmp\n```  -->\n\n## Download and extract HMAP main framework\n``` sh\ncd /home/hmp\ngit clone https://github.com/binary-husky/hmp2g.git\ncd /home/hmp/hmp2g\n```\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/setup_ubuntu.md",
    "content": "# 1. install docker \n```\nsudo apt update\nsudo apt install docker docker.io curl\n```\n\n# 2. intsall nvidia-runtime \n\n``` sh\n# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-on-ubuntu-and-debian\n\n# step 1\ncurl https://get.docker.com | sh \\\n  && sudo systemctl --now enable docker\n\n# step 2\ndistribution=$(. /etc/os-release;echo $ID$VERSION_ID) \\\n      && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \\\n      && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \\\n            sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \\\n            sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list\n\n# step 3:Install the nvidia-docker2 package (and dependencies) after updating the package listing:\n\nsudo apt-get update\nsudo apt-get install -y nvidia-docker2\n\n# Restart the Docker daemon to complete the installation after setting the default runtime:\n\nsudo systemctl restart docker\n```\n\n\n# 3. Download docker image and open it \n\nIn this part, please read [SetupDocker](./setup_docker.md)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/setup_ue_docker.md",
    "content": "# Start up UE Docker Container | \nWarning! Unreal engine is included in this docker, 500GB disk space is needed.\n\n\n```sh\n# 检查docker是否可用 （如果已经身处某个docker容器内，则docker不可用，请找到宿主系统，然后再运行以下命令）\nsudo docker ps\n```\n\n```sh\n# 启动docker容器\nsudo docker run -itd   --name  $USER-swarm \\\n--net host \\\n--memory 500G \\\n--gpus all \\\n--shm-size=32G \\\nfuqingxu/hmp:unreal-trim\n\n\n# 修改docker容器的ssh的端口到 4567，自行选择合适的空闲端口\nsudo docker exec -it  $USER-swarm sed -i 's/2266/4567/g' /etc/ssh/sshd_config\n# 运行docker容器的ssh\nsudo docker exec -it  $USER-swarm service ssh start\n# 运行docker容器的bash\nsudo docker exec -it  $USER-swarm bash\n```\n\nNow find a computer to ssh into it: ```ssh hmp@your_host_ip -p 2233```\n```\n# IP Addr: share with the host\n# SSH Port 4567\n# UserName: hmp\n# Password: hmp\n```\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/ssh_pubkey.sh",
    "content": "key_name=home2\nmkdir -p ./TEMP\nwget --user=fuqingxu  --password=PASSWORD_FOR_NEXTCLOUD   http://cloud.fuqingxu.top:4080/remote.php/dav/files/fuqingxu/keys/$key_name.pub -O ./TEMP/_xkey\nmkdir -p  ~/.ssh/\ncat  ./TEMP/_xkey >>  ~/.ssh/authorized_keys \ncat  ~/.ssh/authorized_keys\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/test_examples.py",
    "content": "def validate_path():\n    import os, sys\n    dir_name = os.path.dirname(__file__)\n    root_dir_assume = os.path.abspath(os.path.dirname(__file__) +  '/..')\n    os.chdir(root_dir_assume)\n    sys.path.append(root_dir_assume)\nvalidate_path()\n\nimport glob\nimport subprocess\nimport copy, os\nimport time\nimport json\nfrom UTIL.network import get_host_ip\nfrom UTIL.colorful import *\n\ntest_subjects = glob.glob('ZDOCS/examples/**/*.jsonc',recursive=True)\nprint(test_subjects)\n\nn_run = len(test_subjects)\ntarget_server = [\n    {\n        \"addr\": \"localhost:2266\",\n        \"usr\": \"hmp\",\n        \"pwd\": \"hmp\"\n    },\n]*n_run\n\n\ndef get_info(script_path):\n    info = {\n        'HostIP': get_host_ip(),\n        'RunPath': os.getcwd(),\n        'ScriptPath': os.path.abspath(script_path),\n        'StartDateTime': time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\n    }\n    try:\n        info['DockerContainerHash'] = subprocess.getoutput(r'cat /proc/self/cgroup | grep -o -e \"docker/.*\"| head -n 1 |sed \"s/docker\\\\/\\\\(.*\\\\)/\\\\1/\" |cut -c1-12')\n    except: \n        info['DockerContainerHash'] = 'None'\n    return info\n\n\ndef run_batch_exp(n_run, n_run_mode, test_subjects, script_path, sum_note='run-hmp-test'):\n    arg_base = ['python', 'main.py']\n    time_mark_only = time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.localtime())\n    time_mark = time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.localtime()) + '-' + sum_note\n    log_dir = '%s/'%time_mark\n    exp_log_dir = log_dir+'exp_log'\n    if not os.path.exists('PROFILE/%s'%exp_log_dir):\n        os.makedirs('PROFILE/%s'%exp_log_dir)\n    exp_json_dir = log_dir+'exp_json'\n    if not os.path.exists('PROFILE/%s'%exp_json_dir):\n        os.makedirs('PROFILE/%s'%exp_json_dir)\n\n    conf_list = []\n    new_json_paths = []\n    for i in range(n_run):\n        new_json_paths.append(test_subjects[i])\n\n    print红('\\n')\n    print红('\\n')\n    print红('\\n')\n\n    printX = [\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n        print亮红, print亮绿, print亮黄, print亮蓝, print亮紫, print亮靛, \n        print红,   print绿,   print黄,   print蓝,   print紫,   print靛,\n    ]\n    \n    final_arg_list = []\n\n    for ith_run in range(n_run):\n        final_arg = copy.deepcopy(arg_base)\n        final_arg.append('--cfg')\n        final_arg.append(new_json_paths[ith_run])\n        final_arg_list.append(final_arg)\n        print('')\n\n\n    def local_worker(ith_run):\n        log_path = open('PROFILE/%s/run-%d.log'%(exp_log_dir, ith_run+1), 'w+')\n        printX[ith_run%len(printX)](final_arg_list[ith_run])\n        subprocess.run(final_arg_list[ith_run], stdout=log_path, stderr=log_path)\n\n    def remote_worker(ith_run):\n        # step 1: transfer all files\n        from UTIL.exp_helper import get_ssh_sftp\n        \n        addr = n_run_mode[ith_run]['addr']\n        if 'exe_here' in addr: \n            _, addr = addr.split('=>')\n            usr = n_run_mode[ith_run]['usr']\n            pwd = n_run_mode[ith_run]['pwd']\n            ssh, sftp = get_ssh_sftp(addr, usr, pwd)\n            src_path = os.getcwd()\n        else:\n            # assert False\n            usr = n_run_mode[ith_run]['usr']\n            pwd = n_run_mode[ith_run]['pwd']\n            ssh, sftp = get_ssh_sftp(addr, usr, pwd)\n            sftp.mkdir('/home/%s/MultiServerMission'%(usr), ignore_existing=True)\n            sftp.mkdir('/home/%s/MultiServerMission/%s'%(usr, time_mark), ignore_existing=True)\n            src_path = '/home/%s/MultiServerMission/%s/src'%(usr, time_mark)\n            try:\n                sftp.mkdir(src_path, ignore_existing=False)\n                sftp.put_dir('./', src_path, ignore_list=['.vscode', '__pycache__','TEMP','ZHECKPOINT'])\n                sftp.close()\n                print紫('upload complete')\n            except:\n                sftp.close()\n                print紫('do not need upload')\n\n        print('byobu attach -t %s'%time_mark_only)\n        addr_ip, addr_port = addr.split(':')\n        print亮蓝(\"Attach cmd: ssh %s@%s -p %s -t \\\"byobu attach -t %s\\\"\"%(usr, addr_ip, addr_port, time_mark_only))\n        \n        stdin, stdout, stderr = ssh.exec_command(command='byobu new-session -d -s %s'%time_mark_only, timeout=1)\n        print亮紫('byobu new-session -d -s %s'%time_mark_only)\n        time.sleep(1)\n\n        byobu_win_name = '%s--run-%d'%(time_mark_only, ith_run)\n        byobu_win_name = byobu_win_name\n        stdin, stdout, stderr = ssh.exec_command(command='byobu new-window -t %s'%time_mark_only, timeout=1)\n        print亮紫('byobu new-window -t %s'%time_mark_only)\n        time.sleep(1)\n\n        cmd = 'cd  ' + src_path\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n        \n        cmd = ' '.join(['echo',  str(get_info(script_path)) ,'>>', './private_remote_execution.log'])\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n\n        cmd = ' '.join(final_arg_list[ith_run])\n        stdin, stdout, stderr = ssh.exec_command(command='byobu send-keys -t %s \"%s\" C-m'%(time_mark_only, cmd), timeout=1)\n        print亮紫('byobu send-keys \"%s\" C-m'%cmd)\n        time.sleep(1)\n\n        print亮蓝(\"command send is done!\")\n        time.sleep(120)\n\n        print亮蓝(\"kill\")\n        ssh.exec_command(command='byobu send-keys -t %s C-c'%(time_mark_only), timeout=1)\n        time.sleep(2)\n        ssh.exec_command(command='byobu send-keys -t %s C-c'%(time_mark_only), timeout=1)\n        time.sleep(2)\n        ssh.exec_command(command='byobu send-keys -t %s C-c'%(time_mark_only), timeout=1)\n        print亮蓝(\"kill finish\")\n\n        # 杀死\n        # stdin, stdout, stderr = ssh.exec_command(command='byobu kill-session -t %s'%byobu_win_name, timeout=1)\n        pass\n\n    def worker(ith_run):\n        if n_run_mode[ith_run] is None: \n            local_worker(ith_run)\n        else:\n            remote_worker(ith_run)\n\n\n\n    def clean_process(pid):\n        import psutil\n        parent = psutil.Process(pid)\n        for child in parent.children(recursive=True):\n            try:\n                print亮红('sending Terminate signal to', child)\n                child.terminate()\n                time.sleep(5)\n                print亮红('sending Kill signal to', child)\n                child.kill()\n            except: pass\n        parent.kill()\n\n    def clean_up():\n        print亮红('clean up!')\n        parent_pid = os.getpid()   # my example\n        clean_process(parent_pid)\n\n    \n            \n    input('Confirm execution? 确认执行?')\n    input('Confirm execution! 确认执行!')\n\n    t = 0\n    while (t >= 0):\n        print('Counting down ', t)\n        time.sleep(1)\n        t -= 1\n\n    DELAY = 5\n    for ith_run in range(n_run):\n        worker(ith_run)\n        for i in range(DELAY):\n            print(f'\\rrunning in {DELAY-i}', end='', flush=True)\n            time.sleep(1)\n\n    print('all submitted')\n\n\nrun_batch_exp(\n    n_run=n_run, \n    n_run_mode=target_server, \n    test_subjects=test_subjects, \n    script_path=__file__, \n    sum_note='run-hmp-test')\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/use_pymarl2.md",
    "content": "# Using pymarl2 as Algorithm\n\n## step 1: clone hmap code\n```\ngit clone https://github.com/binary-husky/hmp2g.git\n\ncd hmp2g\n\ngit submodule update --init\n\ngit submodule foreach -q --recursive 'branch=\"$(git config -f $toplevel/.gitmodules submodule.$name.branch)\"; git switch $branch'\n```\n\nHow to clean temp files when things are not working as expected:\n```sh\n# switch to master branch\ngit checkout master --force\n# pull lastest code\ngit pull --force\n# clean work directory\ngit clean -xfd\n# clean submodule\ngit submodule foreach git clean -xfd\n```\n\n## step 2: run example\nSave following file as ```private_debug.jsonc```: \n``` json\n{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"RVE-drone2-qmix-fixstate-run1\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 8,\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 8529,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/pymarl2_compat\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.pymarl2_compat.pymarl2_compat->PymarlFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":4,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":5,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":6,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":7,   \"type\":\"RLA_CAR\",         \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":8,   \"type\":\"RLA_CAR_Laser\",   \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":9,   \"type\":\"RLA_UAV_Support\", \"init_fn_name\":\"init_air\"      },\n        ]\n    },\n\n\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.pymarl2_compat.pymarl2_compat.py->AlgorithmConfig\": {\n        \"use_shell\": \"mini_shell_uhmap\",\n        \"state_compat\": \"pad\",\n        \"pymarl_config_injection\": {\n            \"controllers.my_n_controller.py->PymarlAlgorithmConfig\": {\n                \"use_normalization\": \"True\",\n                \"use_vae\": \"False\"\n            },\n            \"config.py->GlobalConfig\": {\n                \"batch_size\": 128,\n                \"load_checkpoint\": \"False\"\n            }\n        }\n    }\n}\n```\n\nThen start training with:\n```sh\npython main.py -c private_debug.jsonc\n```"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZDOCS/use_unreal_hmap.md",
    "content": "\n\n# Get Unreal-HMAP Binary Client (Win & Linux)\n\n- Method 1: \n``` python \nfrom MISSION.uhmap.auto_download import download_client_binary_on_platform\ndownload_client_binary_on_platform(\n    desired_path=\"./UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\", \n    # desired_path=\"./UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.exe\", \n    desired_version=\"3.5\", \n    is_render_client=True,\n    platform=\"Linux\",\n    # platform=\"Windows\",\n\n    \n)\n```\n\n- Method 2 (manual): download uhmap file manifest (a json file)\n```\nhttps://ageasga-my.sharepoint.com/:u:/g/personal/fuqingxu_yiteam_tech/EVmCQMSUWV5MgREWaxiz_GoBalBRV3DWBU3ToSJ5OTQaLQ?e=I8yjl9\n```\nOpen this json file, choose the version and platform you want, download and unzip it.\n\n- Method 3 (Compile from source): \n```\nhttps://github.com/binary-husky/unreal-hmp\n```\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZHECKPOINT/uhmap_hete10vs10/experiment_test.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"prob0d2-cos-run1\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 32,\n        \"report_reward_interval\": 512,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 2721,\n        \"device\": \"cuda\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/hete_league_onenet_fix\",\n            \"MISSION/uhmap\"\n        ],\n        \"gpu_fraction\": 0.5,\n        \"gpu_party\": \"cuda2_party1\",\n        \"test_only\": true\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": false,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.hete_league_onenet_fix.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n            {\n                \"team\": 0,\n                \"tid\": 0,\n                \"type\": \"RLA_UAV_Support\",\n                \"init_fn_name\": \"init_air\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 1,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 2,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 3,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 4,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 5,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 6,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 7,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 8,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 0,\n                \"tid\": 9,\n                \"type\": \"RLA_UAV_Support\",\n                \"init_fn_name\": \"init_air\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 0,\n                \"type\": \"RLA_UAV_Support\",\n                \"init_fn_name\": \"init_air\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 1,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 2,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 3,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 4,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 5,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 6,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 7,\n                \"type\": \"RLA_CAR\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 8,\n                \"type\": \"RLA_CAR_Laser\",\n                \"init_fn_name\": \"init_ground\"\n            },\n            {\n                \"team\": 1,\n                \"tid\": 9,\n                \"type\": \"RLA_UAV_Support\",\n                \"init_fn_name\": \"init_air\"\n            }\n        ]\n    },\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.hete_league_onenet_fix.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.hete_league_onenet_fix.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 128,\n        \"hete_n_alive_frontend\": 2,\n        \"hete_n_net_placeholder\": 5,\n        \"hete_same_prob\": 0.2,\n        \"load_checkpoint\": true,\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"hete_lasted_n\": 3,\n        \"policy_resonance\": true,\n        \"hete_exclude_zero_wr\": true,\n        \"debug\": false,\n        \"n_entity_placeholder\": 11,\n        \"load_specific_checkpoint\": \"history_cpt/model_2677_{'win_rate': 1.0, 'mean_reward': 1.8328125000000002}.pt\",\n        \"policy_matrix_testing\": true,\n        \"test_which_cpk\": 4,\n        \"type_sel_override\": true,\n        \"type_sel_override_list\": [\n            2\n        ]\n    },\n    \"ALGORITHM.hete_league_onenet_fix.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-cos\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZHECKPOINT/uhmap_hete10vs10/render_result.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"uhmap_hete10vs10\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,\n        \"report_reward_interval\": 4,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 2721,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/hete_league_onenet_fix\",\n            \"MISSION/uhmap\"\n        ],\n        \"gpu_fraction\": 0.5,\n        \"gpu_party\": \"cuda2_party1\",\n        \"test_only\": true\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": true,\n        \"UElink2editor\": false, // false, //true\n        \"HeteAgents\": true,\n        \"UhmapVersion\": \"3.5\",\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapRenderExe\": \"../WindowsNoEditor/UHMP.exe\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64, // simulation time speed up, larger is faster\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.hete_league_onenet_fix.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n            { \"team\": 0,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 0,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   }\n        ]\n    },\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.hete_league_onenet_fix.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.hete_league_onenet_fix.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 128,\n        \"hete_n_alive_frontend\": 3,\n        \"hete_n_net_placeholder\": 5,\n        \"hete_same_prob\": 0.2,\n        \"load_checkpoint\": true,\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"hete_lasted_n\": 3,\n        \"policy_resonance\": true,\n        \"hete_exclude_zero_wr\": true,\n        \"debug\": false,\n        \"n_entity_placeholder\": 11,\n        \"load_specific_checkpoint\": \"model_trained.pt\",\n        \"policy_matrix_testing\": true,\n        \"allow_fast_test\": false,\n        \"test_which_cpk\": 1,\n    },\n    \"ALGORITHM.hete_league_onenet_fix.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-cos\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZHECKPOINT/uhmap_hete10vs10/render_result_editor.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"uhmap_hete10vs10\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,\n        \"report_reward_interval\": 4,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 2721,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/hete_league_onenet_fix\",\n            \"MISSION/uhmap\"\n        ],\n        \"gpu_fraction\": 0.5,\n        \"gpu_party\": \"cuda2_party1\",\n        \"test_only\": true\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 142,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": true, // false, //true\n        \"HeteAgents\": true,\n        \"UhmapVersion\": \"3.5\",\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 32,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.hete_league_onenet_fix.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n            { \"team\": 0,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 0,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   }\n        ]\n    },\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.hete_league_onenet_fix.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.hete_league_onenet_fix.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 128,\n        \"hete_n_alive_frontend\": 3,\n        \"hete_n_net_placeholder\": 5,\n        \"hete_same_prob\": 0.2,\n        \"load_checkpoint\": true,\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"hete_lasted_n\": 3,\n        \"policy_resonance\": true,\n        \"hete_exclude_zero_wr\": true,\n        \"debug\": false,\n        \"n_entity_placeholder\": 11,\n        \"load_specific_checkpoint\": \"model_trained.pt\",\n        \"policy_matrix_testing\": true,\n        \"allow_fast_test\": false,\n        \"test_which_cpk\": 1,\n    },\n    \"ALGORITHM.hete_league_onenet_fix.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-cos\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/ZHECKPOINT/uhmap_hete10vs10/render_result_editor2.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"uhmap_hete10vs10\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,\n        \"report_reward_interval\": 4,\n        \"test_interval\": 1280,\n        \"test_epoch\": 128,\n        \"interested_team\": 0,\n        \"seed\": 2721,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/hete_league_onenet_fix\",\n            \"MISSION/uhmap\"\n        ],\n        \"gpu_fraction\": 0.5,\n        \"gpu_party\": \"cuda2_party1\",\n        \"test_only\": true\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [10, 10], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 142,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": true, // false, //true\n        \"HeteAgents\": true,\n        \"UhmapVersion\": \"3.5\",\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapAdversial\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 32,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.hete_league_onenet_fix.foundation->ReinforceAlgorithmFoundation\",\n            \"ALGORITHM.script_ai.uhmap_ls->DummyAlgorithmLinedAttack\"\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapAdversialConf.py->SubTaskConfig\": {\n        \"agent_list\": [\n            { \"team\": 0,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 0,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 0,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 0,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   },\n            { \"team\": 1,   \"tid\": 1,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 2,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 3,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 4,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 5,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 6,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 7,   \"type\": \"RLA_CAR\",            \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 8,   \"type\": \"RLA_CAR_Laser\",      \"init_fn_name\": \"init_ground\"},\n            { \"team\": 1,   \"tid\": 9,   \"type\": \"RLA_UAV_Support\",    \"init_fn_name\": \"init_air\"   }\n        ]\n    },\n    \"ALGORITHM.script_ai.uhmap_ls.py->DummyAlgConfig\": {\n        \"reserve\": \"\"\n    },\n    \"ALGORITHM.hete_league_onenet_fix.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.hete_league_onenet_fix.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 128,\n        \"hete_n_alive_frontend\": 3,\n        \"hete_n_net_placeholder\": 5,\n        \"hete_same_prob\": 0.2,\n        \"load_checkpoint\": true,\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"hete_lasted_n\": 3,\n        \"policy_resonance\": true,\n        \"hete_exclude_zero_wr\": true,\n        \"debug\": false,\n        \"n_entity_placeholder\": 11,\n        \"load_specific_checkpoint\": \"model_trained.pt\",\n        \"policy_matrix_testing\": true,\n        \"allow_fast_test\": false,\n        \"test_which_cpk\": 1,\n    },\n    \"ALGORITHM.hete_league_onenet_fix.stage_planner.py->PolicyRsnConfig\": {\n        \"resonance_start_at_update\": 1,\n        \"yita_min_prob\": 0.05,\n        \"yita_max\": 0.5,\n        \"yita_shift_method\": \"-cos\",\n        \"yita_shift_cycle\": 1000,\n        \"yita_inc_per_update\": 0.01\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/agent_with_sensor.jsonc",
    "content": "{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"ppoma-uhmap10vs10\",\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 256,\n        \"test_interval\": 5120,\n        \"test_epoch\": 256,\n        \"interested_team\": 0,\n        \"seed\": 8834,\n        \"device\": \"cuda\",\n        \"mt_act_order\": \"new_method\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"ALGORITHM/ppo_ma\",\n            \"MISSION/uhmap\"\n        ]\n    },\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n        \"N_AGENT_EACH_TEAM\": [4, 4], // update N_AGENT_EACH_TEAM\n        \"MaxEpisodeStep\": 125,\n        \"StepGameTime\": 0.5,\n        \"StateProvided\": false,\n        \"render\": false,\n        \"UElink2editor\": true,\n        \"HeteAgents\": true,\n        \"UnrealLevel\": \"UhmapLargeScale\",\n        \"SubTaskSelection\": \"UhmapLargeScale\",\n        \"UhmapVersion\": \"3.5\",\n        \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxNoEditor/UHMP.sh\",\n        \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.5/LinuxServer/UHMPServer.sh\",\n        \"TimeDilation\": 64,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.random.foundation->DummyRandomControllerWithActionSetV1\",\n            \"TEMP.ALGORITHM.random.foundation->DummyRandomControllerWithActionSetV1\",\n        ]\n    },\n    \"MISSION.uhmap.SubTasks.UhmapLargeScaleConf.py->SubTaskConfig\":{\n        \"agent_list\": [\n            { \"team\":0,  \"tid\":0,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_air\"      },\n            { \"team\":0,  \"tid\":1,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":2,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n            { \"team\":0,  \"tid\":3,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n\n\n            { \"team\":1,  \"tid\":0,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_air\"      },\n            { \"team\":1,  \"tid\":1,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":2,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n            { \"team\":1,  \"tid\":3,   \"type\":\"Lv2_TestAgentSensor\",  \"init_fn_name\":\"init_ground\"   },\n\n        ],\n        \"ActionFormat\": \"ASCII\"\n\n    },\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n        \"preserve\": \"\"\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"ALGORITHM.ppo_ma.shell_env.py->ShellEnvConfig\": {\n        \"add_avail_act\": true\n    },\n    \"ALGORITHM.ppo_ma.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": 256,\n        \"use_normalization\": true,\n        \"load_specific_checkpoint\": \"\",\n        \"gamma\": 0.99,\n        \"gamma_in_reward_forwarding\": \"True\",\n        \"gamma_in_reward_forwarding_value\": 0.95,\n        \"prevent_batchsize_oom\": \"True\",\n        \"lr\": 0.0004,\n        \"ppo_epoch\": 24,\n        \"policy_resonance\": false,\n        \"debug\": true,\n        \"n_entity_placeholder\": 11\n    }\n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/attack_post.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"random-attackpost\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 8, 1 ], // 10 ships, 2 waterdrops\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": true, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": false,\n      \"HeteAgents\": false,\n      \"UnrealLevel\": \"UhmapAttackPost\",\n      \"SubTaskSelection\": \"UhmapAttackPost\",\n      \"UhmapVersion\":\"3.8\",\n      \"UhmapRenderExe\": \"../../Build/WindowsNoEditor/UHMP.exe\",\n      \"UhmapServerExe\": \"../../Build/WindowsServer/UHMPServer.exe\",\n      // \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxNoEditor/UHMP.sh\",\n      // \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.script_ai.a_attackpost->AttackPostPreprogramBaseline\",\n        // \"ALGORITHM.random.foundation->RandomControllerWithActionSetV2\",\n        \"TEMP.TEAM2.ALGORITHM.random.foundation->RandomControllerWithActionSetV4\",\n      ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.a_attackpost.py->AlgorithmConfig\": {\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n    },\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/carrier.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"random-attackpost\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 20, 20 ], // 10 ships, 2 waterdrops\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": false, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": true,\n      \"HeteAgents\": false,\n      \"UnrealLevel\": \"UhmapCarrier\",\n      \"SubTaskSelection\": \"UhmapCarrier\",\n      \"UhmapVersion\":\"3.8\",\n      \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxNoEditor/UHMP.sh\",\n      \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.random.foundation->RandomControllerWithActionSetV1\",\n        // \"ALGORITHM.random.foundation->RandomControllerWithActionSetV2\",\n        \"TEMP.TEAM2.ALGORITHM.random.foundation->RandomControllerWithActionSetV1\",\n      ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n    },\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/config.py",
    "content": "import time\nimport numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.config_args import ChainVar\n\n\n\n'''\n    GlobalConfig: This config class will be 'injected' with new settings from JSONC.\n    (E.g., override configs with ```python main.py --cfg example.jsonc```)\n    (As the name indicated, ChainVars will change WITH vars it 'chained_with' during config injection)\n    (please see UTIL.config_args to find out how this advanced trick works out.)\n\n    * Explaining a very important setting option: \n        - align_episode (True/False):\n            In align mode, all threads begin new episode synchronously,\n            which means a env thread that ends early has to 'wait' for other threads before restarting\n            If set to 'False', threads will not wait for each others, and will 'reset' immediately on 'done'\n\n        - note (str):\n            Name you experiment carefully with note setting.\n            The note defines where the results of a single experiment will go. \n            for example, if note='conc', everything produced in the experiment will be save in ZHECKPOINT/conc/*,\n            including images, saved pytorch model, \n        \n        - env_name:\n            Which mission/environment/task to use, \n            See ./MISSION/env_router.py for the dictionary of available envs.\n\n        - env_path:\n            The path of selected mission. In fact, hmp do not need this setting at all,\n            it exists here only to Double Check that you have chosen the correct mission env.\n\n    * Why the Algorithm selection is not here ?!\n        It is the missions (envs) that choose the algorithm(s)! Please go to mission configuration!\n        Important to remember:\n            - hmp selects a mission, \n            - mission selects algorithm(s).\n        In fact, if you have two teams in env, \n        you can choose two different algorithms to fight each other in the same env!\n            - Please goto ./MISSION/env_router.py to find out where the ScenarioConfig of your env is written,\n            - Please set ```TEAM_NAMES``` to include the path of your favored algorithm(s)\n'''\n\n\nclass GlobalConfig(object): # ADD_TO_CONF_SYSTEM //DO NOT remove this comment//\n    align_episode = True                                # ! please try to understand this with TOP priority\n\n    env_name = 'sr_tasks->cargo'                        # which environment, see ./MISSION/env_router.py\n    env_path = 'MISSION.sr_tasks.multiagent.cargo'      # path of environment, double check to prevent mistake\n    draw_mode = 'OFF'                                   # 'Img','Threejs','Web','Native' \n    activate_logger = True                              # activate data plotting (Tensorboard is not used because I do not like it)\n    data_logger = 'auto load, do not change this var!'  # activate data plotting (Tensorboard is not used because I do not like it)\n    resume_mod = False                                  # resume unfinished\n    mt_act_order = 'old_method'                         # resume unfinished\n    mt_parallel = False                         # resume unfinished\n\n    seed = np.random.randint(0, 100000)                 # seed for numpy and pytorch\n\n    # ! warning, the note also determine where the experiment log is stored, typically at ./ZHECKPOINT/$note/*\n    note = 'more_testing'                               # in case you forget the purpose of this trainning session, write a note\n    logdir = './ZHECKPOINT/%s/'%note\n    logdir_cv = ChainVar(lambda note: './ZHECKPOINT/%s/'%note, chained_with=['note']) \n    recall_previous_session = False                     # continue previously interrupted training session\n\n    test_only = False                                   # only testing and no training, it controlls a flag sending to Alg side\n    test_logger = 'test_only_profile.txt'               # logger path, experimental, writing win rate in a file\n\n    device = 'cuda'                                     # choose from 'cpu' (no GPU), 'cuda' (auto select GPU), 'cuda:3' (manual select GPU)\n    gpu_party = 'off'                                   # GPU memory is precious! assign multiple training process to a 'party', they will share GPU memory\n    manual_gpu_ctl = False                              # auto variable, do not change!\n    gpu_fraction = 1.0                                  # maximum GPU memory usage percent, e.g., 0.5 means using half GPU memory\n\n    num_threads = 64                                    # run N parallel envs, a 'env' is refered to as a 'thread'\n    fold = 1                                            # A 'linux process' can handle multiple envs ('thread'), run N parallel envs, on (N//fold) processes\n                                                        # this 'folding' is designed for IPC efficiency, you can thank python GIL for such a strange design... \n    \n    n_parallel_frame = int(5e6)                         # Number of frames to run (in each frame, all parallel-envs step once)\n    max_n_episode = int(2e5)                            # max number of episodes\n\n    use_float64 = False                                 # force float64 when converting numpy->tensor\n\n    interested_team = 0                                 # the interested agents, used in reward recording\n    interested_agent_num = 50                           # the interested agents, used in reward recording\n    interested_agent_uid = range(0,50)                  # the indices of interested agents, used in reward recording\n    interested_agent_uid_cv = ChainVar(lambda interested_agent_num:range(0,interested_agent_num), chained_with=['interested_agent_num']) \n\n    report_reward_interval = num_threads                # reporting interval\n    report_reward_interval_cv = ChainVar(lambda num_threads:num_threads, chained_with=['num_threads'])\n\n    train_time_testing = True                           # allow hmp to test algorithm policies every test_interval episodes\n    test_interval = 32*num_threads                      # test interval\n    test_interval_cv = ChainVar(lambda num_threads:4*num_threads, chained_with=['num_threads'])\n\n    test_epoch = 32 if num_threads <= 32 else num_threads    # test epoch\n    test_epoch_cv = ChainVar(lambda num_threads: 32 if num_threads <= 32 else num_threads, chained_with=['num_threads'])\n\n    ScenarioConfig = 'This ScenarioConfig var will be automatically linked to task configuration later in ./MISSION/env_router.py'\n\n\n    backup_files = []                                   # a list of files that needs to be backed up at each run\n    hmap_logger = None                                  # this is just a global logger\n    heartbeat_on = True                                 # some fancy commandline visual effect to show that envirenment is running\n    \n    cfg_ready = False                                   # DO NOT change! automatically set to True when Json configuration is all locked-and-loaded\n\n    # ! uploading \"./ZHECKPOINT/$note\" to a data storage server \n    allow_res_upload = True                             # upload results to a data storage server when exiting\n    upload_after_test = False                           # upload results to a data storage server when completing a test run\n    machine_info = 'auto load, do not change this var!'\n    remote_server_ops = \"\"\n    # KEY = {\"addr\": None, \"usr\":None, \"pwd\":None}        # data storage server ip addr, username and password"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/cradle.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"new port: 60459\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"from UTIL.network import find_free_port_no_repeat\\n\",\n    \"\\n\",\n    \"port, release_fn = find_free_port_no_repeat()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"--2022-09-16 16:40:35--  http://cloud.fuqingxu.top:4080/remote.php/dav/files/fuqingxu/keys/notebook_old.pub\\n\",\n      \"Resolving cloud.fuqingxu.top (cloud.fuqingxu.top)... 43.154.70.224\\n\",\n      \"Connecting to cloud.fuqingxu.top (cloud.fuqingxu.top)|43.154.70.224|:4080... connected.\\n\",\n      \"HTTP request sent, awaiting response... 401 Unauthorized\\n\",\n      \"Authentication selected: Basic realm=\\\"Nextcloud\\\", charset=\\\"UTF-8\\\"\\n\",\n      \"Reusing existing connection to cloud.fuqingxu.top:4080.\\n\",\n      \"HTTP request sent, awaiting response... 200 OK\\n\",\n      \"Length: 403 [application/octet-stream]\\n\",\n      \"Saving to: ‘./TEMP/_xkey’\\n\",\n      \"\\n\",\n      \"./TEMP/_xkey        100%[===================>]     403  --.-KB/s    in 0s      \\n\",\n      \"\\n\",\n      \"2022-09-16 16:40:38 (76.4 MB/s) - ‘./TEMP/_xkey’ saved [403/403]\\n\",\n      \"\\n\",\n      \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnPRZXNNBttb6XvGf/W5rf9YKKCXCjzffhAV241Gbx/m/KA3niQR/1Y5U9YC3S+Paia2wvVJpKX1dTOwJKCIZuWvBv4ynHgaQ4occrrc2t4SAW1BzD/YeQM+Y/KrqRQ38emUartiYVDzgsCq1euE4pw1dESh9uazv9pZyS3ieEz+UVCiDeyeXXcI3hlKba7ARLA15txrxp/1em4T8nnGsPaWsuF/pXGmoYO+6P4nlWKXgJxvaHrEZUZSIR0JNOUjLPNjITHWv2eqdhXo+18svTqYkAp/4knVHwJpAxUKJdU7j1tYsDjtzrv8WWr9lf/DqrZFINsbZ11WZn0Q71aG9h Qingxu@DESKTOP-Qingxu\\n\",\n      \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFcsiud8tYOVUuszyoe9M0ymlIdgvK27f2fviAdrxkf5 fqxmax@hotmail.com\\n\",\n      \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFcsiud8tYOVUuszyoe9M0ymlIdgvK27f2fviAdrxkf5 fqxmax@hotmail.com\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"def arrange_id(N_AGENT_EACH_TEAM):\\n\",\n    \"    N_AGENT_EACH_TEAM = np.array(N_AGENT_EACH_TEAM) \\n\",\n    \"    AGENT_ID_EACH_TEAM_cv = []\\n\",\n    \"    begin = 0\\n\",\n    \"    for _, n in enumerate(N_AGENT_EACH_TEAM):\\n\",\n    \"        b = begin\\n\",\n    \"        s = begin + n\\n\",\n    \"        AGENT_ID_EACH_TEAM_cv.append(range(b, s))\\n\",\n    \"        begin = s\\n\",\n    \"    return AGENT_ID_EACH_TEAM_cv\\n\",\n    \"arrange_id([25,25])\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 168,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"7\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import numpy as np\\n\",\n    \"ckpg_info = [\\n\",\n    \"    {'win_rate':0.0},\\n\",\n    \"    {'win_rate':0.2},\\n\",\n    \"    {'win_rate':0.2},\\n\",\n    \"    {'win_rate':0.5},\\n\",\n    \"    {'win_rate':0.7},\\n\",\n    \"    {'win_rate':0.8},\\n\",\n    \"    {'win_rate':0.9},\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"def random_select():\\n\",\n    \"    \\\"\\\"\\\"randomly select a group index\\n\",\n    \"\\n\",\n    \"    Args:\\n\",\n    \"        AlgorithmConfig.hete_same_prob: a probability about choosing the frontier net as the teammate\\n\",\n    \"\\n\",\n    \"    Returns:\\n\",\n    \"        int: a group index\\n\",\n    \"    \\\"\\\"\\\"\\n\",\n    \"    # when random win rate is high, direct to frontend nets\\n\",\n    \"    hete_same_prob = 0.0\\n\",\n    \"    if np.random.rand() < hete_same_prob:\\n\",\n    \"        return 0\\n\",\n    \"    \\n\",\n    \"    n_option = len(ckpg_info)\\n\",\n    \"    rand_winrate = np.random.randint(low=1, high=n_option+1)\\n\",\n    \"        \\n\",\n    \"    return rand_winrate\\n\",\n    \"\\n\",\n    \"print(random_select())\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 53,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"tensor(True)\\n\",\n      \"tensor(True)\\n\",\n      \"tensor(True)\\n\",\n      \"tensor(True)\\n\",\n      \"torch.Size([4, 30, 7])\\n\",\n      \"torch.Size([4, 3, 2, 5, 7])\\n\",\n      \"torch.Size([4, 3, 2, 5, 7])\\n\",\n      \"torch.Size([4, 6, 45]) [4,6,45]\\n\",\n      \"torch.Size([4, 6, 90]) [4,6,90]\\n\",\n      \"torch.Size([4, 6, 90, 5]) [4,6,90, 5]\\n\",\n      \"torch.Size([4, 6, 90, 5]) [4,6,90, 5]\\n\",\n      \"torch.Size([4, 30, 18, 5]) [4,30,18, 5]\\n\",\n      \"torch.Size([4, 30, 18]) [4,30,18]\\n\",\n      \"torch.Size([30, 18, 5]) [30,18, 5]\\n\",\n      \"torch.Size([4, 30, 18, 5]) [4,30,18, 5]\\n\",\n      \"torch.Size([4, 30, 18]) [4,30,18]\\n\",\n      \"torch.Size([30, 18, 5]) [30,18, 5]\\n\",\n      \"torch.Size([4, 30, 18, 5]) [4,30,18, 5]\\n\",\n      \"torch.Size([4, 30, 18]) [4,30,18]\\n\",\n      \"torch.Size([30, 18, 5]) [30,18, 5]\\n\",\n      \"torch.Size([12, 5, 6])\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import torch, copy\\n\",\n    \"import numpy as np\\n\",\n    \"\\\"\\\"\\\"\\n\",\n    \"    improve np.reshape and torch.view function\\n\",\n    \"    If a dim is assigned with 0, it will keep its original dimension\\n\",\n    \"    eg.1    x.shape = (4, 5, 6, 7); new_shape = [0, 0, -1]\\n\",\n    \"            y = my_view(x, new_shape)\\n\",\n    \"            y.shape = (4, 5, 6*7)\\n\",\n    \"\\n\",\n    \"    eg.2    x.shape = (4, 5, 6, 7); new_shape = [-1, 0, 0]\\n\",\n    \"            y = my_view(x, new_shape)\\n\",\n    \"            y.shape = (4*5, 6, 7)\\n\",\n    \"\\n\",\n    \"    eg.3    x.shape = (4, 5, 6); new_shape = [0, 0, -1, 3]\\n\",\n    \"            y = my_view(x, new_shape)\\n\",\n    \"            y.shape = [4, 5, 2, 3]\\n\",\n    \"\\n\",\n    \"    eg.4    x.shape = (3, 4, 5, 6); new_shape = [0, 2, -1, 0, 0]\\n\",\n    \"            y = my_view(x, new_shape)\\n\",\n    \"            y.shape = [3, 2, 2, 5, 6]\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\\"\\\"\\\"\\n\",\n    \"def my_view(x, shape):\\n\",\n    \"    if -1 in shape[1:-1]: return my_view_test(x, shape)\\n\",\n    \"    reverse_lookup = True if shape[0] == -1 else False\\n\",\n    \"    if not reverse_lookup:\\n\",\n    \"        for i, dim in enumerate(shape):\\n\",\n    \"            if dim == 0:\\n\",\n    \"                shape[i] = x.shape[i]\\n\",\n    \"    else:\\n\",\n    \"        for i in range(len(shape)):\\n\",\n    \"            ni = -(i + 1)  # iter -1,-2,-3,...\\n\",\n    \"            dim = shape[ni]\\n\",\n    \"            if dim == 0:\\n\",\n    \"                shape[ni] = x.shape[ni]\\n\",\n    \"    if isinstance(x, np.ndarray):\\n\",\n    \"        return x.reshape(*shape)\\n\",\n    \"    return x.view(*shape)\\n\",\n    \"\\n\",\n    \"# def my_view_test(x, shape):\\n\",\n    \"#     # fill both way until meet -1 \\n\",\n    \"#     for i, dim in enumerate(shape):\\n\",\n    \"#         if dim == 0: shape[i] = x.shape[i]\\n\",\n    \"#         elif dim == -1: break\\n\",\n    \"#         elif dim != x.shape[i]: break\\n\",\n    \"#     for i in range(len(shape)):\\n\",\n    \"#         ni = -(i + 1); dim = shape[ni]\\n\",\n    \"#         if dim == 0: shape[ni] = x.shape[ni]\\n\",\n    \"#         elif dim == -1: break\\n\",\n    \"#     # print(shape)\\n\",\n    \"#     if isinstance(x, np.ndarray):\\n\",\n    \"#         return x.reshape(*shape)\\n\",\n    \"#     return x.view(*shape)\\n\",\n    \"\\n\",\n    \"def my_view_test(x, shape):\\n\",\n    \"    # fill both way until meet -1 \\n\",\n    \"    for i, dim in enumerate(shape):\\n\",\n    \"        if dim == 0: shape[i] = x.shape[i]\\n\",\n    \"        elif dim == -1: break\\n\",\n    \"        elif dim != x.shape[i]: break\\n\",\n    \"    for i in range(len(shape)):\\n\",\n    \"        ni = -(i + 1); dim = shape[ni]\\n\",\n    \"        if dim == 0: shape[ni] = x.shape[ni]\\n\",\n    \"        elif dim == -1: break\\n\",\n    \"    # print(shape)\\n\",\n    \"    if isinstance(x, np.ndarray):\\n\",\n    \"        return x.reshape(*shape)\\n\",\n    \"    return x.view(*shape)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 5, 6, 7); \\n\",\n    \"new_shape = [0, 0, -1]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape))==my_view(x, copy.copy(new_shape))).all())\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 5, 6, 7); \\n\",\n    \"new_shape = [-1, 0, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape))==my_view(x, copy.copy(new_shape))).all())\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 5, 6); \\n\",\n    \"new_shape = [0, 0, -1, 3]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape))==my_view(x, copy.copy(new_shape))).all())\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(3, 4, 5, 6); \\n\",\n    \"new_shape = [0, 2, -1, 0, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape))==my_view(x, copy.copy(new_shape))).all())\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 5, 6, 7)\\n\",\n    \"new_shape = [0,30,0]\\n\",\n    \"print(my_view_test(x, copy.copy(new_shape)).shape)\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 7)\\n\",\n    \"new_shape = [0, 3, -1, 0, 0]\\n\",\n    \"print(my_view_test(x, copy.copy(new_shape)).shape)\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 7)\\n\",\n    \"new_shape = [0, 3, 2, 0, 0]\\n\",\n    \"print(my_view_test(x, copy.copy(new_shape)).shape)\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9)\\n\",\n    \"new_shape = [0, -1, 45]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,6,45]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2)\\n\",\n    \"new_shape = [0, -1, 90]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,6,90]')\\n\",\n    \"# x = torch.rand(4, 6, 5, 7)\\n\",\n    \"# new_shape = [0, 3, 0, 0, 0] # error\\n\",\n    \"# print(my_view_test(x, copy.copy(new_shape)).shape)\\n\",\n    \"\\n\",\n    \"# 规则： 异变数字(!=, -1)最多两个，如果是两个，则必须相连\\n\",\n    \"\\n\",\n    \"# x = torch.rand(4, 5, 6, 7, 8, 9)\\n\",\n    \"# new_shape = [0,30,0,-1] # error\\n\",\n    \"# print(my_view_test(x, copy.copy(new_shape)).shape)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [0, -1, 90, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,6,90, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [0, 0, 90, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,6,90, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [0, 30, 18, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2)\\n\",\n    \"new_shape = [0, 30, 18]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [30, 18, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[30,18, 5]')\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [0, -1, 18, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2)\\n\",\n    \"new_shape = [0, -1, 18]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [-1, 18, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[30,18, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [0, 30, -1, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18, 5]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(4, 6, 5, 9, 2)\\n\",\n    \"new_shape = [0, 30, -1]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[4,30,18]')\\n\",\n    \"\\n\",\n    \"x = torch.rand(6, 5, 9, 2, 5)\\n\",\n    \"new_shape = [30, -1, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape), '[30,18, 5]')\\n\",\n    \"'''\\n\",\n    \"            x.shape = (3, 4, 5, 6); new_shape = [12, 0, -1]\\n\",\n    \"            Error: 12(!=3) and -1 must stick together!\\n\",\n    \"            Fix: new_shape = [12, 0, 0]\\n\",\n    \"            Fix: new_shape = [12, -1, 6]\\n\",\n    \"            Fix: new_shape = [12, -1, 0]\\n\",\n    \"'''\\n\",\n    \"x = torch.rand(3, 4, 5, 6)\\n\",\n    \"new_shape = [12, 0, 0]\\n\",\n    \"new_shape = [12, -1, 6]\\n\",\n    \"new_shape = [12, -1, 0]\\n\",\n    \"print((my_view_test(x, copy.copy(new_shape)).shape))\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 20,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"tensor([[[True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True]],\\n\",\n       \"\\n\",\n       \"        [[True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True]],\\n\",\n       \"\\n\",\n       \"        [[True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True]],\\n\",\n       \"\\n\",\n       \"        [[True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True],\\n\",\n       \"         [True, True, True, True, True, True, True]]])\"\n      ]\n     },\n     \"execution_count\": 20,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"my_view_test(x, new_shape)==my_view(x, new_shape)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"array([0, 0, 0, 0, 0, 1, 0, 0, 8, 6])\"\n      ]\n     },\n     \"execution_count\": 6,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"import torch, time\\n\",\n    \"import numpy as np\\n\",\n    \"def tear_apart(x, n_digit):\\n\",\n    \"    out = np.zeros(n_digit, dtype=x.dtype)\\n\",\n    \"    p = n_digit\\n\",\n    \"    for _ in range(n_digit):\\n\",\n    \"        p-=1\\n\",\n    \"        tmp = x % 10\\n\",\n    \"        out[p] = tmp\\n\",\n    \"        x = x // 10\\n\",\n    \"    return out\\n\",\n    \"\\n\",\n    \"tear_apart(np.array([10086]), 10)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 109,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"The Cython extension is already loaded. To reload it, use:\\n\",\n      \"  %reload_ext Cython\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"%load_ext Cython\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 181,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"In file included from /home/hmp/.local/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h:1969,\\n\",\n      \"                 from /home/hmp/.local/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\\n\",\n      \"                 from /home/hmp/.local/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h:4,\\n\",\n      \"                 from /home/hmp/.cache/ipython/cython/_cython_magic_05262793914bae67f251919f56f690a1.c:683:\\n\",\n      \"/home/hmp/.local/lib/python3.8/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \\\"Using deprecated NumPy API, disable it with \\\" \\\"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\\\" [-Wcpp]\\n\",\n      \"   17 | #warning \\\"Using deprecated NumPy API, disable it with \\\" \\\\\\n\",\n      \"      |  ^~~~~~~\\n\"\n     ]\n    },\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[-0. -0. -0. -0. -0. -0. -1. -2. -0. -0.]\\n\",\n      \"-1200.0\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"\\n\",\n    \"%%cython\\n\",\n    \"import numpy as np\\n\",\n    \"cimport numpy as np\\n\",\n    \"cimport cython\\n\",\n    \"np.import_array()\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"ctypedef fused DTYPE_all_t:\\n\",\n    \"    np.float32_t\\n\",\n    \"    np.float64_t\\n\",\n    \"    np.int64_t\\n\",\n    \"    np.int32_t  # to compat Wi\\n\",\n    \"ctypedef fused DTYPE_t:\\n\",\n    \"    np.float32_t\\n\",\n    \"    np.float64_t\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"ctypedef fused DTYPE_intlong_t:\\n\",\n    \"    np.int64_t\\n\",\n    \"    np.int32_t  # to compat Windows\\n\",\n    \"    \\n\",\n    \"    \\n\",\n    \"# x: input\\n\",\n    \"# n_digit: output dimension\\n\",\n    \"# base: 进制\\n\",\n    \"@cython.boundscheck(False)\\n\",\n    \"@cython.wraparound(False)\\n\",\n    \"@cython.nonecheck(False)\\n\",\n    \"def tear_number_apart(np.float64_t x, DTYPE_intlong_t n_digit, DTYPE_intlong_t base=16, DTYPE_all_t mv_left=8):\\n\",\n    \"    cdef np.ndarray out = np.zeros(n_digit, dtype=float)\\n\",\n    \"    cdef int p = n_digit\\n\",\n    \"    cdef float tmp = 0\\n\",\n    \"    reverse = x < 0\\n\",\n    \"\\n\",\n    \"    cdef float m_init = base\\n\",\n    \"    if reverse: x = -x\\n\",\n    \"    m_init = m_init ** mv_left\\n\",\n    \"    x = x * m_init\\n\",\n    \"    for _ in range(n_digit):\\n\",\n    \"        p -= 1\\n\",\n    \"        if p==0: \\n\",\n    \"            out[p] = x\\n\",\n    \"            break\\n\",\n    \"        tmp = x % base\\n\",\n    \"        out[p] = tmp\\n\",\n    \"        x = x // base\\n\",\n    \"    if reverse: out = -out\\n\",\n    \"    return out\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# [-1.  9.  9.  9.  9.  9.  8.  8.  0.  0.]\\n\",\n    \"# -1200.0\\n\",\n    \"# parts = tear_number_apart(-1200, n_digit=10, base=10, mv_left=0)\\n\",\n    \"# print(parts)\\n\",\n    \"# comb_num_back(parts, n_digit=10, base=10, mv_left=0)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# def put_number_together(parts, n_digit, base):\\n\",\n    \"#     out = 0\\n\",\n    \"#     z = n_digit//2\\n\",\n    \"#     mp = base**(n_digit - n_digit//2 - 1)\\n\",\n    \"#     for p in range(n_digit):\\n\",\n    \"#         out += parts[p] * mp\\n\",\n    \"#         mp = mp / base\\n\",\n    \"        \\n\",\n    \"#     return out\\n\",\n    \"def comb_num_back(arr, n_digit, base, mv_left):\\n\",\n    \"    out = 0\\n\",\n    \"    tmp = base ** (n_digit - mv_left - 1)\\n\",\n    \"    for x in arr:\\n\",\n    \"        out += x * tmp\\n\",\n    \"        tmp = tmp/base\\n\",\n    \"        \\n\",\n    \"    return out\\n\",\n    \"\\n\",\n    \"def tear_num_arr(arr, DTYPE_intlong_t n_digit, DTYPE_intlong_t base, DTYPE_all_t mv_left):\\n\",\n    \"    return np.concatenate([tear_number_apart(x, n_digit, base, mv_left) for x in arr], axis=0)\\n\",\n    \"\\n\",\n    \"parts = tear_number_apart(-1200, n_digit=10, base=10, mv_left=0)\\n\",\n    \"print(parts)\\n\",\n    \"print(comb_num_back(parts, n_digit=10, base=10, mv_left=0))\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 272,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"230.82640393813236 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         0.82640392]\\n\",\n      \"492.6766723569501 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         2.67667246]\\n\",\n      \"59.956808644741066 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         9.95680904]\\n\",\n      \"-59.651731736030555 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -9.65173149]\\n\",\n      \"-238.3352484366702 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -8.33524799]\\n\",\n      \"5.7337278365590105 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         5.73372793]\\n\",\n      \"275.885923960165 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         5.88592386]\\n\",\n      \"-98.07044541018584 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -9.         -8.07044506]\\n\",\n      \"489.5286973606359 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         9.52869701]\\n\",\n      \"137.54638532913899 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         7.54638529]\\n\",\n      \"-2.103895639164466 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -2.10389566]\\n\",\n      \"26.845777550867478 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         6.84577751]\\n\",\n      \"6.174535651712332 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         6.17453575]\\n\",\n      \"39.318904285643555 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         9.31890392]\\n\",\n      \"-143.12573808130801 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -3.12573814]\\n\",\n      \"-347.5528761176796 [-0.       -0.       -0.       -0.       -0.       -0.       -0.\\n\",\n      \" -3.       -4.       -7.552876]\\n\",\n      \"188.97558479563136 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         8.97558498]\\n\",\n      \"46.795050181884015 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         6.79505014]\\n\",\n      \"16.406017666173778 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         6.40601778]\\n\",\n      \"376.55079371029865 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         6.55079365]\\n\",\n      \"170.80500847078605 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         0.80500847]\\n\",\n      \"349.25103126825076 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         9.25103092]\\n\",\n      \"-473.4017140290807 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -3.40171409]\\n\",\n      \"60.37018421759788 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         0.37018421]\\n\",\n      \"188.6137912927477 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         8.61379147]\\n\",\n      \"356.75735183505543 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         6.75735188]\\n\",\n      \"299.86878482237387 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        9.        9.8687849]\\n\",\n      \"355.80549476673696 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         5.80549479]\\n\",\n      \"-282.5901315597383 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -8.         -2.59013152]\\n\",\n      \"337.46020036885136 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         7.46020031]\\n\",\n      \"-293.7165480768427 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -9.         -3.71654797]\\n\",\n      \"256.9969188086662 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         5.         6.99691868]\\n\",\n      \"-443.913267928026 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -3.91326785]\\n\",\n      \"-265.0478090022421 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -5.04780912]\\n\",\n      \"-471.2142031060221 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -1.21420312]\\n\",\n      \"-369.7175572019752 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -9.71755695]\\n\",\n      \"-449.1111081489547 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -9.11110783]\\n\",\n      \"-64.89185973656608 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -4.89185953]\\n\",\n      \"404.7852267269422 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         4.78522682]\\n\",\n      \"-484.6104562950904 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -4.61045647]\\n\",\n      \"-491.8318958035397 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -1.83189583]\\n\",\n      \"341.75084747935693 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         1.75084746]\\n\",\n      \"-161.9878143217315 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -1.98781431]\\n\",\n      \"-0.5227803408573983 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -0.52278036]\\n\",\n      \"-495.7167584916426 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -5.71675873]\\n\",\n      \"392.18254810438526 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         2.18254805]\\n\",\n      \"355.7805804652482 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         5.78058052]\\n\",\n      \"121.58216570700253 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         1.58216572]\\n\",\n      \"-85.9208312664893 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -8.        -5.9208312]\\n\",\n      \"-221.66995828488245 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -1.66995823]\\n\",\n      \"-463.12324927995627 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -3.12324929]\\n\",\n      \"220.16909036312236 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         0.16909036]\\n\",\n      \"479.67136964284293 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         9.67136955]\\n\",\n      \"-434.6094126740596 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -4.60941267]\\n\",\n      \"369.8775505932793 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         9.87755013]\\n\",\n      \"239.68600116266592 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         9.68600082]\\n\",\n      \"-94.02128223839445 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -9.        -4.0212822]\\n\",\n      \"-392.4472062144585 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -2.44720626]\\n\",\n      \"-257.83747123068747 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -7.83747101]\\n\",\n      \"149.2744757568001 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         9.27447605]\\n\",\n      \"221.5218708247577 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         1.52187085]\\n\",\n      \"-135.48763858373147 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -5.48763847]\\n\",\n      \"65.61677451503122 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         5.61677456]\\n\",\n      \"298.6917627464725 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         8.69176292]\\n\",\n      \"244.83693698559205 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         4.83693695]\\n\",\n      \"336.7943197636023 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         6.79431963]\\n\",\n      \"137.48377484776475 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         7.48377466]\\n\",\n      \"360.82992727136497 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         0.82992727]\\n\",\n      \"-375.2222241123636 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -5.22222424]\\n\",\n      \"27.965407036407818 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         7.96540689]\\n\",\n      \"428.3203546424583 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         8.32035446]\\n\",\n      \"-246.49758950751533 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -6.49758959]\\n\",\n      \"-329.003990686788 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -9.00399113]\\n\",\n      \"377.65676905990983 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         7.65676928]\\n\",\n      \"-452.3022625244916 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -2.30226254]\\n\",\n      \"-55.50855235104668 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -5.50855255]\\n\",\n      \"328.05721372694904 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         8.05721378]\\n\",\n      \"-131.62576089992484 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -1.62576091]\\n\",\n      \"-428.25905917247667 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -8.25905895]\\n\",\n      \"44.60946453956227 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         4.60946465]\\n\",\n      \"434.7273175713673 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         3.         4.72731733]\\n\",\n      \"-21.22929536962248 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -1.22929537]\\n\",\n      \"-334.72156136835383 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -3.         -4.72156143]\\n\",\n      \"-247.26735284192014 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -7.26735306]\\n\",\n      \"-259.4297610097589 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -9.42976093]\\n\",\n      \"-332.7135076625024 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -3.         -2.71350765]\\n\",\n      \"-91.13378156422525 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -9.         -1.13378155]\\n\",\n      \"-80.06103945114351 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -0.06103945]\\n\",\n      \"313.4959717860629 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         3.49597168]\\n\",\n      \"-220.0728630675326 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -0.07286306]\\n\",\n      \"-326.7995034605826 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -6.79950333]\\n\",\n      \"-243.43991102652728 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -3.43991113]\\n\",\n      \"340.15380656736306 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         0.15380657]\\n\",\n      \"487.6076045926337 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        8.        7.6076045]\\n\",\n      \"127.04895777989222 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         7.04895782]\\n\",\n      \"468.21983556578226 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         6.         8.21983528]\\n\",\n      \"102.79211050610738 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         2.79211044]\\n\",\n      \"55.88231540426791 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         5.88231564]\\n\",\n      \"-73.19657120602452 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -3.19657111]\\n\",\n      \"345.8591615643367 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         5.85916138]\\n\",\n      \"391.4506532621457 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         1.45065331]\\n\",\n      \"-233.4536250607684 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -3.45362496]\\n\",\n      \"395.765201751574 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         5.76520157]\\n\",\n      \"421.03272289044156 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         1.03272295]\\n\",\n      \"-45.95333744524888 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -5.95333767]\\n\",\n      \"41.915363806866665 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         1.91536379]\\n\",\n      \"112.51040994637785 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         2.51040983]\\n\",\n      \"291.98255116790705 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         1.98255122]\\n\",\n      \"-474.77035527455513 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -4.77035522]\\n\",\n      \"253.77934048125195 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         5.         3.77934051]\\n\",\n      \"-79.86958361799856 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -9.86958408]\\n\",\n      \"-299.4443655644471 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -9.        -9.4443655]\\n\",\n      \"-215.8931536851104 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -1.         -5.89315367]\\n\",\n      \"-0.9617640788727178 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -0.        -0.9617641]\\n\",\n      \"-348.44507577573216 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -8.44507599]\\n\",\n      \"83.11253482463665 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         3.11253476]\\n\",\n      \"280.67948490762774 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        8.        0.6794849]\\n\",\n      \"-183.39057881777788 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -3.39057875]\\n\",\n      \"-38.99148157777588 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -8.99148178]\\n\",\n      \"442.4752907907672 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         4.         2.47529078]\\n\",\n      \"161.6157902424954 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         1.61579025]\\n\",\n      \"-318.77332001786885 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -1.        -8.7733202]\\n\",\n      \"366.9611132273823 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         6.96111345]\\n\",\n      \"-353.7128895196047 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -3.71288943]\\n\",\n      \"-105.41674095739417 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -5.41674089]\\n\",\n      \"-67.51418672344033 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -7.51418686]\\n\",\n      \"369.25465887217155 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        6.        9.2546587]\\n\",\n      \"245.16029222778346 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         5.16029215]\\n\",\n      \"-55.14855658542428 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -5.14855671]\\n\",\n      \"77.6565780498072 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         7.65657806]\\n\",\n      \"353.1104473172967 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         3.11044741]\\n\",\n      \"-312.7778325343301 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -2.77783251]\\n\",\n      \"187.22779530008071 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         7.22779512]\\n\",\n      \"-53.79208269188496 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -3.79208279]\\n\",\n      \"-210.29741353576236 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -1.         -0.29741353]\\n\",\n      \"-381.91815621412127 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -1.91815627]\\n\",\n      \"81.84209252296482 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         1.84209251]\\n\",\n      \"-277.2724126200516 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -7.27241278]\\n\",\n      \"217.41065477658893 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         7.41065454]\\n\",\n      \"-188.72875372469466 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -8.72875404]\\n\",\n      \"170.70329876620394 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         0.70329875]\\n\",\n      \"-164.395594201303 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -4.39559412]\\n\",\n      \"-222.6287168406985 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -2.62871695]\\n\",\n      \"-256.9310806257938 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -6.93108082]\\n\",\n      \"-264.4290492903771 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -4.42904949]\\n\",\n      \"215.122617709276 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         5.12261772]\\n\",\n      \"-382.11248326728185 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -2.11248326]\\n\",\n      \"499.5267383052591 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         9.52673817]\\n\",\n      \"129.86317393748737 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         9.86317348]\\n\",\n      \"-75.89369305940852 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -5.89369297]\\n\",\n      \"394.9595561956092 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        9.        4.9595561]\\n\",\n      \"-401.5964692613657 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -0.         -1.59646928]\\n\",\n      \"-413.3471785648082 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -3.34717846]\\n\",\n      \"-348.4194415279612 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -8.41944122]\\n\",\n      \"260.9353986701871 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        6.        0.9353987]\\n\",\n      \"-275.33039367908185 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -5.33039379]\\n\",\n      \"426.2687566040506 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         6.26875639]\\n\",\n      \"-330.3729411219386 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -3.         -0.37294114]\\n\",\n      \"-355.3817148053097 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -5.38171482]\\n\",\n      \"-65.4056922555991 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -6.        -5.4056921]\\n\",\n      \"218.73429117003363 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         8.73429108]\\n\",\n      \"-73.74184204527012 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -3.74184203]\\n\",\n      \"33.62706825943218 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         3.62706828]\\n\",\n      \"-372.5021613209377 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -2.50216126]\\n\",\n      \"-68.80320608490165 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -8.80320644]\\n\",\n      \"2.7182951368065478 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        0.        2.7182951]\\n\",\n      \"-150.5381485291709 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -0.53814852]\\n\",\n      \"-280.1988088180758 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -8.         -0.19880882]\\n\",\n      \"-486.8794519220411 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -6.87945175]\\n\",\n      \"39.433947408987976 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         9.43394756]\\n\",\n      \"229.14300188862046 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         9.14300156]\\n\",\n      \"5.826423428465399 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         5.82642365]\\n\",\n      \"-344.6744864969875 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -4.67448664]\\n\",\n      \"135.97333273783929 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         5.97333288]\\n\",\n      \"103.52874775866606 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        0.        3.5287478]\\n\",\n      \"64.3924450077713 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         4.39244509]\\n\",\n      \"-75.75051244361086 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -7.        -5.7505126]\\n\",\n      \"-274.7153755980284 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -4.71537542]\\n\",\n      \"255.29646524195104 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        5.        5.2964654]\\n\",\n      \"401.32474918608807 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         1.32474923]\\n\",\n      \"-253.3440567391716 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -3.34405684]\\n\",\n      \"-365.8168835853613 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -5.81688356]\\n\",\n      \"111.35007160127886 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         1.35007155]\\n\",\n      \"340.3595673726175 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         0.35956737]\\n\",\n      \"141.2416975473597 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         1.24169755]\\n\",\n      \"41.9582763714772 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         1.95827639]\\n\",\n      \"-342.1534272676331 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -2.15342736]\\n\",\n      \"-15.11909178977644 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -5.11909199]\\n\",\n      \"-0.7494675660728589 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -0.74946755]\\n\",\n      \"-29.96639377147026 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -9.96639347]\\n\",\n      \"188.75365570851156 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         8.75365543]\\n\",\n      \"-134.1307149460681 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -4.13071489]\\n\",\n      \"-22.04566163712185 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -2.04566169]\\n\",\n      \"162.98835728088645 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         2.98835731]\\n\",\n      \"-237.32906203766657 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -7.32906199]\\n\",\n      \"143.69109320357998 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         3.69109321]\\n\",\n      \"-427.9069244344028 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -7.90692425]\\n\",\n      \"-119.05578467604805 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -9.05578423]\\n\",\n      \"477.95205588886824 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         7.95205593]\\n\",\n      \"-134.60817487303999 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -1.        -3.        -4.6081748]\\n\",\n      \"248.595822682531 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         8.59582233]\\n\",\n      \"-6.1861013460756675 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -6.18610144]\\n\",\n      \"204.28005914300783 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         0.         4.28005934]\\n\",\n      \"-301.24513736058566 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -1.24513733]\\n\",\n      \"395.40034818484907 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         5.40034819]\\n\",\n      \"-279.5330324753561 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -9.53303242]\\n\",\n      \"-308.16000650304653 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -8.16000652]\\n\",\n      \"-193.97612080123028 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -3.97612071]\\n\",\n      \"279.11887354516483 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        7.        9.1188736]\\n\",\n      \"449.5502759692573 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        4.        9.5502758]\\n\",\n      \"-56.21456194255825 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -6.21456194]\\n\",\n      \"249.6582448928404 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         9.65824509]\\n\",\n      \"38.85054021639822 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         8.85054016]\\n\",\n      \"275.2262080837393 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         5.22620821]\\n\",\n      \"99.4580298448201 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         9.45802975]\\n\",\n      \"-428.93773114042585 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -8.93773079]\\n\",\n      \"-304.8547723931375 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -4.85477257]\\n\",\n      \"-205.72042913948187 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -5.72042894]\\n\",\n      \"-242.9435511123559 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -2.94355106]\\n\",\n      \"154.31539917899352 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         4.31539917]\\n\",\n      \"140.32973007812433 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         0.32973006]\\n\",\n      \"24.71974023528267 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         4.71974039]\\n\",\n      \"-428.36942650681954 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -8.36942673]\\n\",\n      \"-4.580391401526551 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -4.58039141]\\n\",\n      \"-215.62973227728955 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -1.         -5.62973213]\\n\",\n      \"-21.064262976175364 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -1.06426299]\\n\",\n      \"112.12558604645817 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         2.12558603]\\n\",\n      \"-427.6535546042114 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -7.65355444]\\n\",\n      \"382.57639839635937 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         2.57639837]\\n\",\n      \"313.92626332910214 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         3.92626333]\\n\",\n      \"-1.0876759854191453 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -1.08767593]\\n\",\n      \"343.13521909309964 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        4.        3.1352191]\\n\",\n      \"305.237482528453 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         0.         5.23748255]\\n\",\n      \"-116.1804176856277 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -6.18041754]\\n\",\n      \"-350.90531670170844 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -0.90531671]\\n\",\n      \"467.1961516052979 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         6.         7.19615173]\\n\",\n      \"106.27341464948992 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         6.27341461]\\n\",\n      \"-396.271931063796 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -6.27193117]\\n\",\n      \"-268.56477381913425 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -8.56477356]\\n\",\n      \"265.773743173759 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         6.         5.77374315]\\n\",\n      \"95.06479874326656 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         5.06479883]\\n\",\n      \"409.1636418464082 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         9.16364193]\\n\",\n      \"-67.29429973668488 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -6.        -7.2942996]\\n\",\n      \"152.36388169623393 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         2.36388159]\\n\",\n      \"-354.8097704105878 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -4.80977058]\\n\",\n      \"-199.38158235100366 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -9.38158226]\\n\",\n      \"-129.93805959805604 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -9.93805981]\\n\",\n      \"-106.8290890645831 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -6.82908916]\\n\",\n      \"319.3416568477493 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         9.34165668]\\n\",\n      \"-5.080162704495295 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -5.08016253]\\n\",\n      \"344.7352473389096 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         4.73524714]\\n\",\n      \"162.27433620440402 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        6.        2.2743361]\\n\",\n      \"-383.3693688889532 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -3.36936879]\\n\",\n      \"-378.32840355299004 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -8.32840347]\\n\",\n      \"-275.99695802067305 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -5.99695826]\\n\",\n      \"-327.0515893701671 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -7.05158949]\\n\",\n      \"-168.24713922298395 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -8.24713898]\\n\",\n      \"321.67135518040755 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         1.67135513]\\n\",\n      \"163.22357991678726 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         3.22357988]\\n\",\n      \"71.91056099916793 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         1.91056097]\\n\",\n      \"498.814643990679 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         8.81464386]\\n\",\n      \"-223.0083297010419 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -3.00832963]\\n\",\n      \"-398.987968189617 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -8.98796844]\\n\",\n      \"-244.32367001227072 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -4.32366991]\\n\",\n      \"118.17755856479472 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        1.        8.1775589]\\n\",\n      \"8.548031675793322 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         8.54803181]\\n\",\n      \"371.4827803057329 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         1.48278034]\\n\",\n      \"-79.22767431119327 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -9.22767448]\\n\",\n      \"173.9483971602199 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         3.94839716]\\n\",\n      \"-417.47146232437274 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -7.47146225]\\n\",\n      \"331.54890549942206 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         1.54890549]\\n\",\n      \"125.99311892439235 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         5.99311876]\\n\",\n      \"88.75680410553277 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         8.75680447]\\n\",\n      \"-135.71750819537488 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -5.71750832]\\n\",\n      \"69.58315804562842 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         9.58315849]\\n\",\n      \"114.92769886847631 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         4.92769909]\\n\",\n      \"480.92408090108694 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         0.92408091]\\n\",\n      \"243.31667585358463 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        4.        3.3166759]\\n\",\n      \"394.5451591577459 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         4.54515934]\\n\",\n      \"460.64826220185904 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        6.        0.6482622]\\n\",\n      \"162.64211846754228 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         2.64211845]\\n\",\n      \"-495.27673648536796 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -5.27673626]\\n\",\n      \"0.642284945905458 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         0.64228493]\\n\",\n      \"-359.02190943847677 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -9.02190971]\\n\",\n      \"305.5005243310127 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         0.         5.50052452]\\n\",\n      \"-396.49363348366853 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -6.49363327]\\n\",\n      \"-55.26309863901702 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -5.26309872]\\n\",\n      \"-446.62208851308816 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -6.62208843]\\n\",\n      \"358.92521179047253 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         8.92521191]\\n\",\n      \"-72.97871803885269 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -2.97871804]\\n\",\n      \"121.43397712575864 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         1.43397713]\\n\",\n      \"330.8118891252132 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         0.81188911]\\n\",\n      \"496.83282294944155 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        9.        6.8328228]\\n\",\n      \"107.7720563432073 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         7.77205658]\\n\",\n      \"-9.159264060972006 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -9.15926361]\\n\",\n      \"428.27568758691683 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         8.27568722]\\n\",\n      \"-398.10858735870505 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -8.10858727]\\n\",\n      \"159.6271524098143 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         9.62715244]\\n\",\n      \"-474.64083828355854 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -4.64083815]\\n\",\n      \"-154.07682304830328 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -4.07682323]\\n\",\n      \"-422.49255259174004 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -2.49255252]\\n\",\n      \"-393.99154773458775 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -3.99154782]\\n\",\n      \"-43.680149041343206 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -3.68014908]\\n\",\n      \"196.38240634887373 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         6.38240623]\\n\",\n      \"130.71341992735174 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         0.71341991]\\n\",\n      \"-480.39908467459304 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -0.39908469]\\n\",\n      \"48.9476828347416 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         8.94768238]\\n\",\n      \"-165.91167076139158 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -5.91167068]\\n\",\n      \"-14.69766907147163 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -4.69766903]\\n\",\n      \"224.21995340736567 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         4.21995354]\\n\",\n      \"208.94993270938622 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         0.         8.94993305]\\n\",\n      \"-387.40332146149905 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -7.40332127]\\n\",\n      \"229.51442547683266 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         9.51442528]\\n\",\n      \"-207.23020490028986 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -7.23020506]\\n\",\n      \"52.78104204933109 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        5.        2.7810421]\\n\",\n      \"-335.923602395204 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -3.         -5.92360258]\\n\",\n      \"109.35836256575948 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        0.        9.3583622]\\n\",\n      \"-207.1579309132331 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -7.15793085]\\n\",\n      \"-459.55903378031746 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -9.55903339]\\n\",\n      \"-49.90224371661034 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -9.90224361]\\n\",\n      \"46.39335776746378 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         6.39335775]\\n\",\n      \"235.77923410562752 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         5.77923393]\\n\",\n      \"-32.191817634135475 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -2.19181752]\\n\",\n      \"-233.93915567101863 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -3.93915558]\\n\",\n      \"-192.36199899619766 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -2.36199903]\\n\",\n      \"226.79709439660357 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         6.79709435]\\n\",\n      \"71.75980737462272 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         1.75980735]\\n\",\n      \"-438.0500776346408 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -8.05007744]\\n\",\n      \"274.9978681145407 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         4.99786806]\\n\",\n      \"-363.2875978673524 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -3.28759789]\\n\",\n      \"-100.31595210478217 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -0.31595209]\\n\",\n      \"-184.2750594078917 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -4.27505922]\\n\",\n      \"339.6683586151754 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        3.        9.6683588]\\n\",\n      \"-368.91351031988637 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -8.91351032]\\n\",\n      \"490.1801885420003 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         0.18018854]\\n\",\n      \"497.70422280087377 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         7.70422268]\\n\",\n      \"124.87402441437656 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         4.87402439]\\n\",\n      \"149.71164764510536 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         9.71164799]\\n\",\n      \"366.94536955771116 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         6.94536972]\\n\",\n      \"130.05211944266736 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         0.05211944]\\n\",\n      \"91.97282170601173 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         1.97282171]\\n\",\n      \"-121.41741765030145 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -1.41741765]\\n\",\n      \"-428.35317935634953 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -8.35317898]\\n\",\n      \"-264.62772070879726 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -4.62772083]\\n\",\n      \"-121.26872275912415 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -1.26872277]\\n\",\n      \"433.35537740412076 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         3.         3.35537744]\\n\",\n      \"-62.60810641060033 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -2.60810637]\\n\",\n      \"317.4220234092721 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        1.        7.4220233]\\n\",\n      \"-459.77532719992377 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -9.77532673]\\n\",\n      \"-235.69328173167213 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -5.69328165]\\n\",\n      \"295.76992501258815 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         5.76992512]\\n\",\n      \"-46.5767514360983 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -6.57675123]\\n\",\n      \"-16.260424008672338 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -6.26042414]\\n\",\n      \"6.166947614218987 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         6.16694784]\\n\",\n      \"189.46045326550276 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         9.46045303]\\n\",\n      \"243.6327416956484 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         3.63274169]\\n\",\n      \"-489.5028098630434 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -9.50280952]\\n\",\n      \"-273.52312248930224 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -3.52312255]\\n\",\n      \"457.1623504602803 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         7.16235065]\\n\",\n      \"466.9010169501754 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         6.         6.90101671]\\n\",\n      \"494.18583154539607 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         4.18583155]\\n\",\n      \"408.5891265885484 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         8.58912659]\\n\",\n      \"441.58131108547485 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         4.         1.58131111]\\n\",\n      \"-179.5921687462062 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -9.59216881]\\n\",\n      \"141.9769862849909 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         1.97698629]\\n\",\n      \"385.88868398228004 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        8.        5.8886838]\\n\",\n      \"-316.4533324461213 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -6.45333242]\\n\",\n      \"-209.6742744982063 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -9.67427444]\\n\",\n      \"-171.54918451534817 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -1.54918456]\\n\",\n      \"-70.36953060501439 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -0.36953062]\\n\",\n      \"99.05283762290674 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         9.05283737]\\n\",\n      \"129.11174458588494 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         9.11174488]\\n\",\n      \"471.9882523727814 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        7.        1.9882524]\\n\",\n      \"122.81049861332927 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         2.81049871]\\n\",\n      \"345.85173314635733 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         5.85173321]\\n\",\n      \"314.8918967802342 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         4.89189672]\\n\",\n      \"-478.6498395683444 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -7.        -8.6498394]\\n\",\n      \"386.0198720794964 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         6.01987219]\\n\",\n      \"-123.95955841412554 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -3.95955849]\\n\",\n      \"494.51049724132514 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         4.51049709]\\n\",\n      \"172.75407652243436 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         2.75407648]\\n\",\n      \"-52.06992109410802 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -2.06992102]\\n\",\n      \"49.11503170494424 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         9.11503124]\\n\",\n      \"327.6199769564614 [0.       0.       0.       0.       0.       0.       0.       3.\\n\",\n      \" 2.       7.619977]\\n\",\n      \"-378.4125093278964 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -8.41250896]\\n\",\n      \"396.35038149695924 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         6.35038137]\\n\",\n      \"-51.12398474363622 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -1.12398469]\\n\",\n      \"-186.9925750721937 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -6.99257517]\\n\",\n      \"-48.225954355239686 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -8.22595406]\\n\",\n      \"239.83349163488643 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         9.83349133]\\n\",\n      \"90.65102730698594 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         0.65102732]\\n\",\n      \"144.00594786281783 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         4.00594807]\\n\",\n      \"-308.2166339847694 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -0.        -8.2166338]\\n\",\n      \"-18.85589619473538 [-0.       -0.       -0.       -0.       -0.       -0.       -0.\\n\",\n      \" -0.       -1.       -8.855896]\\n\",\n      \"195.90798108890607 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         5.90798092]\\n\",\n      \"176.2255162850873 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         6.22551632]\\n\",\n      \"-314.52933019395766 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -4.52933025]\\n\",\n      \"-172.38655866728948 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -2.38655877]\\n\",\n      \"-127.60038582325383 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -7.60038567]\\n\",\n      \"338.1476394665358 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         8.14763927]\\n\",\n      \"377.3202896801408 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         7.32028961]\\n\",\n      \"225.2908821372236 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         5.29088211]\\n\",\n      \"398.27315425365197 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         8.27315426]\\n\",\n      \"197.89010977643008 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         7.89010954]\\n\",\n      \"212.1869640821229 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         2.18696404]\\n\",\n      \"-297.8495214165556 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -9.         -7.84952164]\\n\",\n      \"334.087286195963 [0.       0.       0.       0.       0.       0.       0.       3.\\n\",\n      \" 3.       4.087286]\\n\",\n      \"-267.64883785397984 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -7.64883804]\\n\",\n      \"-426.6585009294657 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -6.65850115]\\n\",\n      \"-6.859845581155377 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -6.85984564]\\n\",\n      \"6.879865961109011 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         6.87986612]\\n\",\n      \"-267.4203692982318 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -7.42036915]\\n\",\n      \"418.06422348770377 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         1.         8.06422329]\\n\",\n      \"-301.8162366756212 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -1.81623673]\\n\",\n      \"-125.05642239899085 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -5.05642223]\\n\",\n      \"327.4884713877 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         7.48847151]\\n\",\n      \"-130.26327510920964 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -0.26327512]\\n\",\n      \"39.365344674485605 [0.       0.       0.       0.       0.       0.       0.       0.\\n\",\n      \" 3.       9.365345]\\n\",\n      \"-209.2202559447528 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -9.22025585]\\n\",\n      \"-310.788863663181 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -0.78886366]\\n\",\n      \"470.15433177138135 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         0.15433177]\\n\",\n      \"197.09087017260273 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         7.09087038]\\n\",\n      \"-157.44386775374596 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -7.44386768]\\n\",\n      \"-3.4882267684137602 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -3.48822665]\\n\",\n      \"-356.50216883390726 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -6.50216866]\\n\",\n      \"-9.005468952834605 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -9.00546932]\\n\",\n      \"-118.97439881329319 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -8.97439861]\\n\",\n      \"-46.04439057162946 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -6.04439068]\\n\",\n      \"-115.50529705053869 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -5.50529718]\\n\",\n      \"-425.5952571650743 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -5.59525728]\\n\",\n      \"199.96211810021137 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         9.96211815]\\n\",\n      \"397.40966357564855 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         7.40966368]\\n\",\n      \"-405.7589344648791 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -0.        -5.7589345]\\n\",\n      \"34.827961432374096 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         4.82796144]\\n\",\n      \"-474.35956707656635 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -4.35956717]\\n\",\n      \"16.834656684333705 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         6.83465672]\\n\",\n      \"-35.660457992899076 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -5.66045809]\\n\",\n      \"-311.96448873569926 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -1.96448874]\\n\",\n      \"489.259269306771 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         9.25926971]\\n\",\n      \"493.1925497565006 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         3.19254971]\\n\",\n      \"457.42217600037515 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         7.42217588]\\n\",\n      \"-135.93972282352595 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -5.93972301]\\n\",\n      \"-472.5457513949065 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -2.54575133]\\n\",\n      \"325.6627341198152 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         5.66273403]\\n\",\n      \"282.0289312264831 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         2.02893114]\\n\",\n      \"234.32099350960888 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         4.32099342]\\n\",\n      \"292.828610585384 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         2.82861066]\\n\",\n      \"-326.3856640208916 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -6.38566399]\\n\",\n      \"10.804692295551144 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         0.80469227]\\n\",\n      \"-441.1245524698034 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -1.12455249]\\n\",\n      \"-352.92610149182815 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -2.92610145]\\n\",\n      \"-306.05287975475017 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -6.05287981]\\n\",\n      \"88.20061988262783 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        8.        8.2006197]\\n\",\n      \"110.71823236530875 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         0.71823239]\\n\",\n      \"379.1400278528342 [0.       0.       0.       0.       0.       0.       0.       3.\\n\",\n      \" 7.       9.140028]\\n\",\n      \"470.83819699184846 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         0.83819699]\\n\",\n      \"84.1104530993394 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         4.11045313]\\n\",\n      \"302.85687596131163 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        0.        2.8568759]\\n\",\n      \"341.9254378436502 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         1.92543781]\\n\",\n      \"-313.73801347392197 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -3.73801351]\\n\",\n      \"-72.01769439978145 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -2.01769447]\\n\",\n      \"10.43345122831385 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         0.43345124]\\n\",\n      \"385.9296140253349 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         5.92961407]\\n\",\n      \"-146.20214860066295 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -6.20214844]\\n\",\n      \"73.31352959894366 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         3.31352949]\\n\",\n      \"153.2274750443986 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         3.22747493]\\n\",\n      \"305.67975078487177 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         0.         5.67975092]\\n\",\n      \"47.57379073652168 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         7.57379055]\\n\",\n      \"8.30135737734583 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         8.30135727]\\n\",\n      \"366.29442354304643 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         6.29442358]\\n\",\n      \"-259.68484993223996 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -9.68484974]\\n\",\n      \"71.4172154540963 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         1.41721547]\\n\",\n      \"-4.087571782114163 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -4.08757162]\\n\",\n      \"340.79668512308956 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        4.        0.7966851]\\n\",\n      \"47.76956945993349 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        4.        7.7695694]\\n\",\n      \"379.402201676632 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         9.40220165]\\n\",\n      \"66.14579617955829 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        6.        6.1457963]\\n\",\n      \"104.63457822421118 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         4.63457823]\\n\",\n      \"-261.37880830073647 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -1.37880826]\\n\",\n      \"-423.59819062256577 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -3.59819055]\\n\",\n      \"411.4182070993884 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         1.         1.41820705]\\n\",\n      \"-140.84572606160106 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -0.84572607]\\n\",\n      \"57.43353414905128 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         7.43353415]\\n\",\n      \"171.03041113953887 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         1.03041112]\\n\",\n      \"455.54189799823587 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         5.54189777]\\n\",\n      \"389.7395132102449 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        8.        9.7395134]\\n\",\n      \"-274.30231915207514 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -4.30231905]\\n\",\n      \"226.60923836197455 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         6.60923815]\\n\",\n      \"35.941904822470505 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         5.94190502]\\n\",\n      \"-402.48016862471223 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -0.         -2.48016858]\\n\",\n      \"386.33079194287455 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         6.33079195]\\n\",\n      \"-201.2661261311176 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -1.26612616]\\n\",\n      \"-48.55932367702054 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -8.55932331]\\n\",\n      \"103.48067678220197 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         3.48067689]\\n\",\n      \"225.54703433267932 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         5.54703426]\\n\",\n      \"108.78328524535807 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         8.78328514]\\n\",\n      \"-320.5949347591963 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -0.59493476]\\n\",\n      \"193.1564032041887 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        9.        3.1564033]\\n\",\n      \"44.8716004344385 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         4.87160063]\\n\",\n      \"-404.5633448600623 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -0.         -4.56334496]\\n\",\n      \"138.5945354883208 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         8.59453583]\\n\",\n      \"-360.9242133110592 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -0.92421329]\\n\",\n      \"-431.8853225262498 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -1.88532257]\\n\",\n      \"10.807255056181075 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         0.80725503]\\n\",\n      \"288.2804734986652 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         8.28047371]\\n\",\n      \"-34.12729907827239 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -4.12729931]\\n\",\n      \"137.80427629118375 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         7.80427647]\\n\",\n      \"-425.0523126102047 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -5.05231237]\\n\",\n      \"309.63960681270106 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         0.         9.63960648]\\n\",\n      \"71.93690515422402 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         1.93690515]\\n\",\n      \"-359.4661713091961 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -9.46617126]\\n\",\n      \"399.53249184298534 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         9.53249168]\\n\",\n      \"-373.4253367614285 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -3.42533684]\\n\",\n      \"311.5071441848546 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         1.50714421]\\n\",\n      \"263.0624469974201 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         6.         3.06244707]\\n\",\n      \"-461.8864901848915 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -1.88649023]\\n\",\n      \"-238.18212203013601 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -8.18212223]\\n\",\n      \"-317.1419303897158 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -7.14193058]\\n\",\n      \"-480.99883370464494 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -0.99883372]\\n\",\n      \"-95.41177786914845 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -9.         -5.41177797]\\n\",\n      \"4.878546263106243 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         4.87854624]\\n\",\n      \"-101.32305327041213 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -1.32305324]\\n\",\n      \"53.479420394845654 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         3.47942042]\\n\",\n      \"-234.61908169376343 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -3.        -4.6190815]\\n\",\n      \"-146.93795373841567 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -6.93795395]\\n\",\n      \"44.7290546594219 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         4.72905445]\\n\",\n      \"308.70834180778706 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        0.        8.7083416]\\n\",\n      \"-196.22550483649437 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -6.22550488]\\n\",\n      \"200.09594322188295 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         0.         0.09594322]\\n\",\n      \"62.65719450099716 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         2.65719461]\\n\",\n      \"-211.02589183964338 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -1.         -1.02589178]\\n\",\n      \"-32.17613642473127 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -2.17613649]\\n\",\n      \"-465.6062206787892 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -5.60622072]\\n\",\n      \"24.318124079337533 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         4.31812429]\\n\",\n      \"236.5532985394688 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         6.55329847]\\n\",\n      \"206.0116616269524 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         0.         6.01166153]\\n\",\n      \"132.9122159541447 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         3.         2.91221595]\\n\",\n      \"-21.491944375434446 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -1.49194443]\\n\",\n      \"-457.1630075606471 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -7.16300774]\\n\",\n      \"-358.65396737134006 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -5.        -8.6539669]\\n\",\n      \"-220.31138050353405 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -0.31138051]\\n\",\n      \"58.80276180020616 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         8.80276203]\\n\",\n      \"-371.3043637295676 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -1.30436373]\\n\",\n      \"-288.04379491785556 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -8.         -8.04379463]\\n\",\n      \"47.5609333876138 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         7.56093359]\\n\",\n      \"-165.17873331391297 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -5.17873335]\\n\",\n      \"472.18912374704723 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         2.18912363]\\n\",\n      \"-148.00154848946946 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -8.00154877]\\n\",\n      \"-180.17147310350768 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -1.        -8.        -0.1714731]\\n\",\n      \"-201.01143982044323 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -0.        -1.0114398]\\n\",\n      \"293.0313394483371 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         3.03133941]\\n\",\n      \"-203.19987866093214 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -3.19987869]\\n\",\n      \"-428.45731044982847 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -8.45731068]\\n\",\n      \"258.5409222909009 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         5.         8.54092216]\\n\",\n      \"-365.91094966668726 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -5.91094971]\\n\",\n      \"391.5745414704725 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         1.57454145]\\n\",\n      \"-19.358951098474964 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -9.35895157]\\n\",\n      \"-365.13664320740094 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -5.13664341]\\n\",\n      \"-328.83735435850815 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -2.         -8.83735466]\\n\",\n      \"300.8552257779958 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        0.        0.8552258]\\n\",\n      \"491.6456938544073 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        9.        1.6456939]\\n\",\n      \"-79.41072618300538 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -9.41072655]\\n\",\n      \"-474.5273943174483 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -4.52739429]\\n\",\n      \"104.68058862767504 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         4.68058872]\\n\",\n      \"492.4059287026351 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         2.40592861]\\n\",\n      \"-441.7927029096442 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -1.79270291]\\n\",\n      \"-102.98490382098657 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -2.98490381]\\n\",\n      \"-205.54803118720577 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -5.54803133]\\n\",\n      \"-64.36046111296689 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -4.36046124]\\n\",\n      \"-150.98068675020514 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -0.98068672]\\n\",\n      \"143.1061801692821 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         3.10618019]\\n\",\n      \"-178.17113607606805 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -1.        -7.        -8.1711359]\\n\",\n      \"-341.5878848752122 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -4.        -1.5878849]\\n\",\n      \"362.760938432983 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         2.76093841]\\n\",\n      \"-379.9962298423384 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -9.99623013]\\n\",\n      \"-103.11938011302234 [-0.      -0.      -0.      -0.      -0.      -0.      -0.      -1.\\n\",\n      \" -0.      -3.11938]\\n\",\n      \"477.42939591800706 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         7.42939615]\\n\",\n      \"-69.41533124228216 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -9.41533089]\\n\",\n      \"-294.723730601968 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -9.         -4.72373056]\\n\",\n      \"-415.06480182477065 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -5.06480169]\\n\",\n      \"347.62520010890177 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         7.62520027]\\n\",\n      \"189.32811560034435 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         9.32811546]\\n\",\n      \"-123.19111691481832 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -3.19111681]\\n\",\n      \"100.67749296427242 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         0.67749298]\\n\",\n      \"-301.6772565952325 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -1.67725658]\\n\",\n      \"-108.16104999908393 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -8.16104984]\\n\",\n      \"-301.1506007896884 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -1.15060079]\\n\",\n      \"287.8108980948163 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        8.        7.8108983]\\n\",\n      \"-35.27037324554594 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -5.27037334]\\n\",\n      \"-281.92453604819434 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -8.         -1.92453599]\\n\",\n      \"-28.102911894934635 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -8.10291195]\\n\",\n      \"174.11574576855938 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         4.11574554]\\n\",\n      \"382.01137637649305 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         2.01137638]\\n\",\n      \"-112.08165918164481 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -2.08165908]\\n\",\n      \"188.17603492061784 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         8.17603493]\\n\",\n      \"60.01896569090337 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         0.01896569]\\n\",\n      \"-101.31680523070929 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -1.31680524]\\n\",\n      \"112.75970294458837 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         2.75970292]\\n\",\n      \"104.46314602901296 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         4.46314621]\\n\",\n      \"362.5516029067584 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         2.55160284]\\n\",\n      \"-205.58743971276715 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -5.58743954]\\n\",\n      \"-268.00667395274627 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -8.00667381]\\n\",\n      \"36.690715978517254 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         6.69071579]\\n\",\n      \"23.5690772212096 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         3.56907725]\\n\",\n      \"65.02841533574288 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        6.        5.0284152]\\n\",\n      \"499.28861154004267 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         9.28861141]\\n\",\n      \"341.65278350232853 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         1.65278351]\\n\",\n      \"470.5095001667428 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         0.50950015]\\n\",\n      \"46.3727180448521 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         6.37271786]\\n\",\n      \"-198.01066491754293 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -8.01066494]\\n\",\n      \"-49.82980921035684 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -9.82980919]\\n\",\n      \"-306.1319919199889 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -6.13199186]\\n\",\n      \"-196.86872839699376 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -6.86872816]\\n\",\n      \"324.3794098489308 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         4.37940979]\\n\",\n      \"-278.2084491978215 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -8.20844936]\\n\",\n      \"235.47179515598194 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         5.47179508]\\n\",\n      \"58.03797353363982 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        5.        8.0379734]\\n\",\n      \"-397.79281652253826 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -7.79281664]\\n\",\n      \"452.21342032158884 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         2.21342039]\\n\",\n      \"-464.8700016266698 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -4.87000179]\\n\",\n      \"88.35434029491918 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         8.35434055]\\n\",\n      \"-140.4241094999209 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -0.42410949]\\n\",\n      \"394.5904337294258 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        9.        4.5904336]\\n\",\n      \"-205.40100120911265 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -5.40100098]\\n\",\n      \"235.81069972655078 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         5.81069994]\\n\",\n      \"-421.29376600962377 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -1.29376602]\\n\",\n      \"-423.18550473600123 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -2.         -3.18550467]\\n\",\n      \"-236.3946824884371 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -6.39468241]\\n\",\n      \"494.2223525873546 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        9.        4.2223525]\\n\",\n      \"381.6328274813765 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         1.63282752]\\n\",\n      \"160.32544415298966 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         0.32544416]\\n\",\n      \"432.9686448287368 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         3.         2.96864486]\\n\",\n      \"-103.2079355574025 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -3.20793557]\\n\",\n      \"116.80507371838999 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         6.80507374]\\n\",\n      \"70.99137575797998 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         0.99137574]\\n\",\n      \"411.40926054906066 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         1.         1.40926051]\\n\",\n      \"352.0239038603196 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         2.02390385]\\n\",\n      \"-360.23523315880067 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -0.23523316]\\n\",\n      \"380.90689592642013 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         0.90689594]\\n\",\n      \"105.76604677715318 [0.       0.       0.       0.       0.       0.       0.       1.\\n\",\n      \" 0.       5.766047]\\n\",\n      \"-271.30788701930487 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -1.30788708]\\n\",\n      \"264.18151238517487 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         6.         4.18151236]\\n\",\n      \"158.51205801624957 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         8.51205826]\\n\",\n      \"-50.77058559724201 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -5.        -0.7705856]\\n\",\n      \"-491.1597928611564 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -9.        -1.1597929]\\n\",\n      \"-108.18441580996607 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -8.18441582]\\n\",\n      \"-224.23337553895306 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -4.23337555]\\n\",\n      \"301.97830446396404 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         0.         1.97830451]\\n\",\n      \"-72.52668100497195 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -2.52668095]\\n\",\n      \"490.30939283798614 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         0.30939284]\\n\",\n      \"-376.5117261858708 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -6.51172638]\\n\",\n      \"106.29975463503527 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         6.29975462]\\n\",\n      \"371.3995006582238 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         1.39950061]\\n\",\n      \"190.87484167790313 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         0.87484169]\\n\",\n      \"481.5972895318178 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         1.59728956]\\n\",\n      \"37.042906798599226 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         7.04290676]\\n\",\n      \"-498.16110408782555 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -9.        -8.1611042]\\n\",\n      \"-340.7034462841348 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -0.70344627]\\n\",\n      \"-180.09826116680705 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -0.09826117]\\n\",\n      \"-301.0064676597545 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -0.        -1.0064677]\\n\",\n      \"-33.64829914217948 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -3.64829922]\\n\",\n      \"-279.48708729432036 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -9.48708725]\\n\",\n      \"392.2456714659056 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         2.24567151]\\n\",\n      \"220.31526648905563 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         0.31526649]\\n\",\n      \"-377.5600848337253 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -7.         -7.56008482]\\n\",\n      \"-257.65576973408133 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -7.65576982]\\n\",\n      \"-258.3493022672273 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -8.34930229]\\n\",\n      \"332.94619520724655 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         2.94619513]\\n\",\n      \"163.71114802500176 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         3.71114802]\\n\",\n      \"101.52961442734143 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         1.52961445]\\n\",\n      \"-407.9271620860048 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -0.         -7.92716217]\\n\",\n      \"-8.786333525146128 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -8.78633308]\\n\",\n      \"-18.765718774419792 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -8.76571846]\\n\",\n      \"278.8577992776409 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         8.85779953]\\n\",\n      \"160.81692063288355 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         0.81692064]\\n\",\n      \"116.29082650264489 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         6.29082632]\\n\",\n      \"15.354438862586584 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         5.35443878]\\n\",\n      \"-244.13062770494653 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -4.13062763]\\n\",\n      \"345.72118579963063 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         5.72118568]\\n\",\n      \"-385.8418560723539 [-0.       -0.       -0.       -0.       -0.       -0.       -0.\\n\",\n      \" -3.       -8.       -5.841856]\\n\",\n      \"179.57264217101542 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         9.57264233]\\n\",\n      \"-190.29411175963108 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -0.29411176]\\n\",\n      \"377.9705994586952 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         7.97059965]\\n\",\n      \"10.74081593825149 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         0.74081594]\\n\",\n      \"-221.33604579579458 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -1.33604574]\\n\",\n      \"-224.37603698222864 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -4.37603712]\\n\",\n      \"-25.74779840048913 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -5.74779844]\\n\",\n      \"-265.90666459915957 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -5.90666437]\\n\",\n      \"-221.5273254263177 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -2.         -1.52732539]\\n\",\n      \"88.69715149587343 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         8.69715118]\\n\",\n      \"68.06962777988757 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         8.06962776]\\n\",\n      \"311.2903515355647 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         1.29035151]\\n\",\n      \"-202.4858709614571 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -2.48587108]\\n\",\n      \"-228.39104049882974 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -2.        -8.3910408]\\n\",\n      \"95.44997394878784 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         5.44997406]\\n\",\n      \"181.3291146362741 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         1.32911468]\\n\",\n      \"393.13724016604414 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         3.13724017]\\n\",\n      \"72.59507633738771 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         2.59507632]\\n\",\n      \"-134.14668103307827 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -4.14668083]\\n\",\n      \"233.2852735772447 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         3.28527355]\\n\",\n      \"1.3510576139152963 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         1.35105765]\\n\",\n      \"457.68828007852005 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         7.68828011]\\n\",\n      \"270.5911285372213 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         0.59112853]\\n\",\n      \"-382.6205955318277 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -2.62059546]\\n\",\n      \"408.4696338037548 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         8.46963406]\\n\",\n      \"-11.14007244084525 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -1.14007246]\\n\",\n      \"-293.3081269608415 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -9.         -3.30812693]\\n\",\n      \"-163.6039698433006 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -3.60396981]\\n\",\n      \"-254.26318054075148 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -4.26318073]\\n\",\n      \"174.83879607000398 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         4.83879614]\\n\",\n      \"106.23780564272745 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         6.23780584]\\n\",\n      \"-84.8229966527515 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -4.82299662]\\n\",\n      \"-141.69614787497665 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -1.69614792]\\n\",\n      \"364.7043829364108 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        6.        4.7043829]\\n\",\n      \"-316.7102987810195 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -6.71029902]\\n\",\n      \"271.08194095388825 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         1.08194101]\\n\",\n      \"95.2499405764915 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        9.        5.2499404]\\n\",\n      \"240.42990997904124 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         0.42990997]\\n\",\n      \"10.099758959124294 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         0.09975896]\\n\",\n      \"-158.24553829506982 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -8.24553871]\\n\",\n      \"459.1243318559735 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         9.12433147]\\n\",\n      \"-107.1290847183185 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -7.12908459]\\n\",\n      \"-234.13374234176632 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -4.13374233]\\n\",\n      \"225.9945626272164 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         5.99456263]\\n\",\n      \"-445.7044734883838 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -4.        -5.7044735]\\n\",\n      \"11.711333233610844 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         1.71133327]\\n\",\n      \"-313.5278872521896 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -3.52788734]\\n\",\n      \"-245.57377380966838 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -5.57377386]\\n\",\n      \"481.9432620693226 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        8.        1.9432621]\\n\",\n      \"58.027183812352945 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         8.02718353]\\n\",\n      \"-46.29274176582443 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -6.29274178]\\n\",\n      \"-415.1533099910012 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -5.15330982]\\n\",\n      \"-119.0978430425732 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -9.09784317]\\n\",\n      \"472.89082460701724 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         2.89082456]\\n\",\n      \"280.1018121441957 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         0.10181215]\\n\",\n      \"33.73377210998707 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         3.73377204]\\n\",\n      \"-474.6513747504427 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -4.65137482]\\n\",\n      \"210.96719424741684 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         0.96719426]\\n\",\n      \"-469.63333909932436 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -9.63333893]\\n\",\n      \"-472.11731052673514 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -2.11731052]\\n\",\n      \"336.4889093884846 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         6.48890924]\\n\",\n      \"275.8937723814684 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        7.        5.8937726]\\n\",\n      \"-126.70359558978927 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -6.70359564]\\n\",\n      \"-277.1860044695068 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -7.18600464]\\n\",\n      \"41.37815043077031 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         1.37815046]\\n\",\n      \"-357.4853082320448 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -7.48530817]\\n\",\n      \"190.95213411216662 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         0.95213413]\\n\",\n      \"-121.99755794429856 [-0.       -0.       -0.       -0.       -0.       -0.       -0.\\n\",\n      \" -1.       -2.       -1.997558]\\n\",\n      \"70.31055912391382 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         0.31055912]\\n\",\n      \"-134.50271167202166 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -3.         -4.50271177]\\n\",\n      \"89.4283156533564 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         9.42831612]\\n\",\n      \"96.58569028528862 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        9.        6.5856905]\\n\",\n      \"421.30848490271757 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         1.30848491]\\n\",\n      \"225.71324140417624 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         5.71324158]\\n\",\n      \"-4.9080859728388715 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -4.90808582]\\n\",\n      \"285.9187254104432 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         5.91872549]\\n\",\n      \"245.27993769584745 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         5.27993774]\\n\",\n      \"-457.04608698499993 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -7.04608679]\\n\",\n      \"407.9682434706801 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        0.        7.9682436]\\n\",\n      \"-173.49391504849444 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -3.49391508]\\n\",\n      \"-66.09086824199484 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -6.09086847]\\n\",\n      \"-230.71325918409258 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -0.71325916]\\n\",\n      \"-30.59336711767213 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -3.        -0.5933671]\\n\",\n      \"-162.73952346776878 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -2.73952341]\\n\",\n      \"-498.8933641318919 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -8.89336395]\\n\",\n      \"-40.50105805535187 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -0.50105804]\\n\",\n      \"97.73430280409312 [0.       0.       0.       0.       0.       0.       0.       0.\\n\",\n      \" 9.       7.734303]\\n\",\n      \"-469.6364262835193 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -9.63642597]\\n\",\n      \"-442.10781656949763 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -2.10781646]\\n\",\n      \"-415.1294428520661 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -5.12944269]\\n\",\n      \"-244.63706206242807 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -4.63706207]\\n\",\n      \"-478.8184347994656 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -8.81843472]\\n\",\n      \"113.76390205962028 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         3.76390195]\\n\",\n      \"-165.80498162082168 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -5.80498171]\\n\",\n      \"-477.01756480154376 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -7.01756477]\\n\",\n      \"177.5672718556065 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         7.56727171]\\n\",\n      \"298.6300529265613 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         8.63005257]\\n\",\n      \"389.4414749026629 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         9.44147491]\\n\",\n      \"-196.24800245632025 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -6.24800253]\\n\",\n      \"-121.34320151865907 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -1.34320152]\\n\",\n      \"-355.17597945261326 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -5.17597961]\\n\",\n      \"433.4172276355376 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         3.         3.41722775]\\n\",\n      \"215.49303268636777 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         5.49303246]\\n\",\n      \"379.5396987256737 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        7.        9.5396986]\\n\",\n      \"381.3216895372549 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         1.32168949]\\n\",\n      \"150.01414475568342 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         0.01414476]\\n\",\n      \"-31.51192967511207 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -1.51192963]\\n\",\n      \"-5.620754014434737 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -5.62075424]\\n\",\n      \"-336.31629800530646 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -3.         -6.31629801]\\n\",\n      \"89.9300951468136 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         8.         9.93009472]\\n\",\n      \"172.2096775060038 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         2.20967746]\\n\",\n      \"-33.89668864072615 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -3.        -3.8966887]\\n\",\n      \"-498.165187442586 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -8.16518784]\\n\",\n      \"-437.0412877435752 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -3.        -7.0412879]\\n\",\n      \"257.19761033967904 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         5.         7.19761038]\\n\",\n      \"353.3705562335615 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         3.37055612]\\n\",\n      \"-258.18931443980586 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -8.18931484]\\n\",\n      \"436.80120640806985 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         3.         6.80120659]\\n\",\n      \"-141.30037629492975 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -1.        -4.        -1.3003763]\\n\",\n      \"-69.45342368370999 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -6.        -9.4534235]\\n\",\n      \"458.75025124038405 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         8.75025082]\\n\",\n      \"-398.99433139442743 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -9.         -8.99433136]\\n\",\n      \"428.66915108701767 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         2.         8.66915131]\\n\",\n      \"234.44315025863872 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         4.44315004]\\n\",\n      \"147.87793561776962 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         7.87793541]\\n\",\n      \"35.96613662923942 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         5.96613646]\\n\",\n      \"55.87558309793705 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         5.87558317]\\n\",\n      \"-126.8375896195243 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -6.83758974]\\n\",\n      \"336.2080549585532 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         6.20805502]\\n\",\n      \"324.81818437858954 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         4.81818438]\\n\",\n      \"-419.2280071575505 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -9.22800732]\\n\",\n      \"-12.993722074701953 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -2.99372196]\\n\",\n      \"129.2746234810782 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         9.27462387]\\n\",\n      \"93.96696567690654 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         3.96696568]\\n\",\n      \"-79.43104670953916 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -9.43104649]\\n\",\n      \"397.4631631979908 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         7.46316338]\\n\",\n      \"-411.0499939449011 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -1.04999399]\\n\",\n      \"156.82618719745656 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         6.82618713]\\n\",\n      \"-432.18986528418736 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -2.18986535]\\n\",\n      \"133.33898326946704 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        3.        3.3389833]\\n\",\n      \"171.02163402545912 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         1.02163398]\\n\",\n      \"-142.99856433430503 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -2.99856424]\\n\",\n      \"-248.38987568060267 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -8.38987541]\\n\",\n      \"92.99514887321736 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 0.        9.        2.9951489]\\n\",\n      \"154.68059142436286 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         4.68059158]\\n\",\n      \"52.79489364924517 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         5.         2.79489374]\\n\",\n      \"-145.38723418396282 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -4.         -5.38723421]\\n\",\n      \"-232.52545737247422 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -2.52545738]\\n\",\n      \"173.58605343321975 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         7.         3.58605337]\\n\",\n      \"336.5748128599669 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         6.57481289]\\n\",\n      \"-251.04467989675783 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -1.04467988]\\n\",\n      \"441.15290990796296 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         4.         1.15290987]\\n\",\n      \"236.5576867062137 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         6.55768681]\\n\",\n      \"-172.27894632282704 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -1.        -7.        -2.2789464]\\n\",\n      \"-116.79032879409135 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -6.79032898]\\n\",\n      \"-443.077562417462 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -3.07756233]\\n\",\n      \"157.23964975224425 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         5.         7.23964977]\\n\",\n      \"-38.638314660690895 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -3.         -8.63831425]\\n\",\n      \"-171.17955822688901 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -1.17955828]\\n\",\n      \"346.861445315422 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         6.86144543]\\n\",\n      \"-308.8485358266215 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -8.84853554]\\n\",\n      \"-409.73564844750973 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -0.         -9.73564816]\\n\",\n      \"-191.42459846372884 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -1.42459846]\\n\",\n      \"-235.43165365193218 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -3.        -5.4316535]\\n\",\n      \"-413.3038378598505 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -3.30383778]\\n\",\n      \"76.87509437531736 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         6.87509441]\\n\",\n      \"-96.81345882273662 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -9.         -6.81345892]\\n\",\n      \"-408.30475144713915 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -0.        -8.3047514]\\n\",\n      \"202.7470809432891 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         0.         2.74708104]\\n\",\n      \"-82.55633168472276 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -2.55633163]\\n\",\n      \"-58.13794238350567 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -8.13794231]\\n\",\n      \"-152.64386206860158 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -2.64386201]\\n\",\n      \"262.7060240999891 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         6.         2.70602417]\\n\",\n      \"-56.17949312419257 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -6.17949295]\\n\",\n      \"403.1904456013009 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         3.19044566]\\n\",\n      \"19.85620831549251 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         1.         9.85620785]\\n\",\n      \"115.61117698472667 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         5.61117697]\\n\",\n      \"5.954821188715931 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         5.95482111]\\n\",\n      \"-44.95711342367026 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -4.         -4.95711327]\\n\",\n      \"-100.67725463828936 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -0.67725462]\\n\",\n      \"-211.86179807254547 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -1.         -1.86179805]\\n\",\n      \"-232.02071228253985 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -2.02071238]\\n\",\n      \"452.8033935987449 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        5.        2.8033936]\\n\",\n      \"380.0109961604446 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         0.01099616]\\n\",\n      \"-89.77200613038261 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -9.77200603]\\n\",\n      \"226.21404998082906 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         6.21404982]\\n\",\n      \"288.15834778539727 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         8.15834808]\\n\",\n      \"-497.5317910316236 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -7.53179121]\\n\",\n      \"-272.0713161267645 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -2.07131624]\\n\",\n      \"143.56966761942368 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         3.56966758]\\n\",\n      \"454.4934444948675 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         5.         4.49344444]\\n\",\n      \"-198.87129385867652 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -8.87129402]\\n\",\n      \"-356.0281315070064 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -5.         -6.02813148]\\n\",\n      \"-209.26119665170862 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -9.26119709]\\n\",\n      \"232.39983586563872 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         2.39983582]\\n\",\n      \"480.5631414035888 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         0.56314141]\\n\",\n      \"394.96687400636864 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         4.96687412]\\n\",\n      \"-453.4609836586365 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -5.         -3.46098375]\\n\",\n      \"378.2462428908964 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         7.         8.24624252]\\n\",\n      \"258.0182442421155 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         5.         8.01824379]\\n\",\n      \"-102.14994978984527 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -2.14994979]\\n\",\n      \"277.4427246086756 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        7.        7.4427247]\\n\",\n      \"-463.875069729917 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -3.87506962]\\n\",\n      \"230.90193659704616 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         0.90193659]\\n\",\n      \"30.566272369639712 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         0.56627238]\\n\",\n      \"194.86368437412972 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         4.86368418]\\n\",\n      \"470.3914816217548 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         0.39148161]\\n\",\n      \"-193.0088758835553 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -3.00887585]\\n\",\n      \"-87.04280875524573 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -7.04280853]\\n\",\n      \"397.5523441408838 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         7.55234432]\\n\",\n      \"-157.17261149315786 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -7.17261171]\\n\",\n      \"-13.495223681279978 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -3.49522376]\\n\",\n      \"45.91450753882454 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         5.91450739]\\n\",\n      \"-242.00682692464602 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -2.00682688]\\n\",\n      \"-413.44643357154473 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -1.         -3.44643354]\\n\",\n      \"-448.6542663401468 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -8.65426636]\\n\",\n      \"365.01672831561183 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 3.        6.        5.0167284]\\n\",\n      \"69.6478840660425 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         9.64788437]\\n\",\n      \"477.2856547118553 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         7.28565454]\\n\",\n      \"-432.7183458207543 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -2.71834588]\\n\",\n      \"-488.51688782640645 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -8.         -8.51688766]\\n\",\n      \"140.6537677427525 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         0.65376776]\\n\",\n      \"-26.499627754888124 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -2.         -6.49962759]\\n\",\n      \"-185.76791507179902 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -5.76791525]\\n\",\n      \"388.36605431568637 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         8.         8.36605453]\\n\",\n      \"498.5655137517721 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         9.         8.56551361]\\n\",\n      \"477.7796663580064 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         7.         7.77966642]\\n\",\n      \"-233.24559426102465 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -3.         -3.24559426]\\n\",\n      \"-347.3476787667839 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -7.34767866]\\n\",\n      \"282.3281198398767 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         2.32811975]\\n\",\n      \"322.1302301206603 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         2.         2.13023019]\\n\",\n      \"450.6465134105924 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        5.        0.6465134]\\n\",\n      \"-345.32896707859106 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -4.         -5.32896709]\\n\",\n      \"117.36118525673544 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         1.         7.36118507]\\n\",\n      \"-380.3425054209237 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -0.34250543]\\n\",\n      \"-269.96716558903364 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -9.96716595]\\n\",\n      \"-310.5571318531801 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -1.         -0.55713183]\\n\",\n      \"65.39532355399791 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         6.         5.39532375]\\n\",\n      \"211.23856910410933 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         1.         1.23856914]\\n\",\n      \"184.2989127204634 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         8.         4.29891253]\\n\",\n      \"357.526682424392 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         5.         7.52668238]\\n\",\n      \"101.73886601326609 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         0.         1.73886597]\\n\",\n      \"-179.88644722942837 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -9.88644695]\\n\",\n      \"-77.33659457028908 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -7.33659458]\\n\",\n      \"-54.813704913663216 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -5.         -4.81370497]\\n\",\n      \"-365.11512396755097 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -5.11512375]\\n\",\n      \"411.2206101227943 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         1.         1.22061014]\\n\",\n      \"-88.41044490114581 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -8.         -8.41044521]\\n\",\n      \"-100.48127503076132 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -0.48127502]\\n\",\n      \"430.5512495149471 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        3.        0.5512495]\\n\",\n      \"-4.3239491069823055 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -0.         -4.32394934]\\n\",\n      \"-191.3621257537933 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -9.         -1.36212575]\\n\",\n      \"288.89857550374467 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         8.89857578]\\n\",\n      \"-327.0922637515912 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -2.        -7.0922637]\\n\",\n      \"409.32301266971814 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         9.32301235]\\n\",\n      \"420.1764706960616 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 4.        2.        0.1764707]\\n\",\n      \"-471.73346827703165 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -7.         -1.73346829]\\n\",\n      \"316.0729322906193 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         1.         6.07293224]\\n\",\n      \"73.45207015585386 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         3.45207024]\\n\",\n      \"75.40587770092611 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         7.         5.40587759]\\n\",\n      \"-106.70699783494142 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -0.         -6.70699787]\\n\",\n      \"-111.47554540304449 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -1.         -1.47554541]\\n\",\n      \"-301.3739792165889 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -0.         -1.37397921]\\n\",\n      \"120.17488173866109 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         2.         0.17488174]\\n\",\n      \"25.42114751157365 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         5.42114735]\\n\",\n      \"464.8770355159141 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         6.         4.87703562]\\n\",\n      \"395.93832035682965 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         5.93832016]\\n\",\n      \"214.01871926757877 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 2.        1.        4.0187192]\\n\",\n      \"0.4973925977069804 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         0.         0.49739259]\\n\",\n      \"-167.3829570995724 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -6.         -7.38295698]\\n\",\n      \"-112.63641501670385 [-0.       -0.       -0.       -0.       -0.       -0.       -0.\\n\",\n      \" -1.       -1.       -2.636415]\\n\",\n      \"-268.0081724943796 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -6.         -8.00817204]\\n\",\n      \"-181.12118351102558 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -8.         -1.12118351]\\n\",\n      \"35.33764749968393 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         3.         5.33764744]\\n\",\n      \"336.0466576310549 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         6.04665756]\\n\",\n      \"91.70690447870089 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         9.         1.70690453]\\n\",\n      \"-469.47684008939564 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -6.         -9.47684002]\\n\",\n      \"237.38162439186615 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         7.38162422]\\n\",\n      \"-250.18701711814197 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -0.18701711]\\n\",\n      \"-79.68666730609564 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -7.         -9.68666744]\\n\",\n      \"484.1756040385994 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         8.         4.17560387]\\n\",\n      \"-276.73411216809484 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -7.         -6.73411226]\\n\",\n      \"340.53919140067103 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         0.53919142]\\n\",\n      \"-473.67346749490116 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -4.        -7.        -3.6734674]\\n\",\n      \"146.76415447671408 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         4.         6.76415443]\\n\",\n      \"266.20026024672217 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         6.         6.20026016]\\n\",\n      \"197.73846828991026 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         9.         7.73846817]\\n\",\n      \"-250.88250452010652 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -5.         -0.88250452]\\n\",\n      \"284.8622493449217 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         8.         4.86224937]\\n\",\n      \"-202.9958574458821 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -0.         -2.99585748]\\n\",\n      \"-338.2227458407002 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -3.        -3.        -8.2227459]\\n\",\n      \"162.09954343107947 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         1.         6.         2.09954333]\\n\",\n      \"-444.65383112843625 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -4.         -4.65383101]\\n\",\n      \"335.58028584878366 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         3.         5.58028603]\\n\",\n      \"-98.31265625987339 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -9.        -8.3126564]\\n\",\n      \"47.82935429848023 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         7.82935429]\\n\",\n      \"-432.5156562439786 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -3.         -2.51565623]\\n\",\n      \"279.2032389827309 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         7.         9.20323944]\\n\",\n      \"398.4135792571383 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         9.         8.41357899]\\n\",\n      \"230.36124762659583 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         3.         0.36124763]\\n\",\n      \"-361.1756393388362 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -6.         -1.17563939]\\n\",\n      \"227.5747455308219 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         2.         7.57474566]\\n\",\n      \"112.4548255051081 [0.        0.        0.        0.        0.        0.        0.\\n\",\n      \" 1.        1.        2.4548254]\\n\",\n      \"-155.3119163680934 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -5.         -5.31191635]\\n\",\n      \"-11.714341492229718 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -1.         -1.71434152]\\n\",\n      \"-269.436760941968 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -2.        -6.        -9.4367609]\\n\",\n      \"-22.859242111159418 [-0.        -0.        -0.        -0.        -0.        -0.\\n\",\n      \" -0.        -0.        -2.        -2.8592422]\\n\",\n      \"-129.76794512250322 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -2.         -9.76794529]\\n\",\n      \"-499.09822388770175 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -4.         -9.         -9.09822369]\\n\",\n      \"248.0023273172286 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         4.         8.00232697]\\n\",\n      \"348.18563198628715 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         4.         8.18563175]\\n\",\n      \"-241.11132893795008 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -2.         -4.         -1.11132896]\\n\",\n      \"-62.970755518410805 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -6.         -2.97075558]\\n\",\n      \"-178.78620921577294 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -1.         -7.         -8.78620911]\\n\",\n      \"298.5002801056519 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         2.         9.         8.50028038]\\n\",\n      \"-384.6884195872957 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -3.         -8.         -4.68841982]\\n\",\n      \"43.49249121452192 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         4.         3.49249125]\\n\",\n      \"-98.15937336954849 [-0.         -0.         -0.         -0.         -0.         -0.\\n\",\n      \" -0.         -0.         -9.         -8.15937328]\\n\",\n      \"404.4605816247577 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         4.         0.         4.46058178]\\n\",\n      \"363.14678611459215 [0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         3.         6.         3.14678621]\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"for i in range(1000):\\n\",\n    \"    q = (np.random.rand() - 0.5)*1e3\\n\",\n    \"    parts = tear_number_apart(q, n_digit=10, base=10, mv_left=0)\\n\",\n    \"    print(q, parts)\\n\",\n    \"    res = np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q) < 1e-6\\n\",\n    \"    if not res:\\n\",\n    \"        print('??? np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q)', np.abs(comb_num_back(parts, n_digit=10, base=10, mv_left=0)-q))\\n\",\n    \"        assert False\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 184,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[-0. -0. -0. -0. -0. -0. -1. -2. -0. -0.]\\n\",\n      \"-1200.0\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"parts = tear_number_apart(-1200, n_digit=10, base=10, mv_left=0)\\n\",\n    \"print(parts)\\n\",\n    \"print(comb_num_back(parts, n_digit=10, base=10, mv_left=0))\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 185,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[0. 1. 1. 1. 1. 1. 1. 1. 1. 0.]\\n\"\n     ]\n    },\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"255.0\"\n      ]\n     },\n     \"execution_count\": 185,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"\\n\",\n    \"\\n\",\n    \"parts = tear_number_apart(255, n_digit=10, base=2, mv_left=1)\\n\",\n    \"print(parts)\\n\",\n    \"comb_num_back(parts, n_digit=10, base=2, mv_left=1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"client.send_targeted_dgram('ddd')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 186,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[0.         0.         0.         0.         0.         0.\\n\",\n      \" 0.         0.         2.         5.57780027]\\n\"\n     ]\n    },\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"255.77800273895264\"\n      ]\n     },\n     \"execution_count\": 186,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"\\n\",\n    \"\\n\",\n    \"parts = tear_number_apart(255.778, n_digit=10, base=10, mv_left=-1)\\n\",\n    \"print(parts)\\n\",\n    \"comb_num_back(parts, n_digit=10, base=10, mv_left=-1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 187,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[-0. -0. -0. -0. -0. -0. -1. -2. -0. -0.]\\n\"\n     ]\n    },\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"-1200.0\"\n      ]\n     },\n     \"execution_count\": 187,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"\\n\",\n    \"parts = tear_number_apart(-1200, n_digit=10, base=10, mv_left=0)\\n\",\n    \"print(parts)\\n\",\n    \"comb_num_back(parts, n_digit=10, base=10, mv_left=0)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 54,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"tensor([[0.0000, 0.0226, 0.0226],\\n\",\n       \"        [0.0216, 0.0000, 0.0000],\\n\",\n       \"        [0.0216, 0.0000, 0.0000]])\"\n      ]\n     },\n     \"execution_count\": 54,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"import torch\\n\",\n    \"from torch.distributions.categorical import Categorical\\n\",\n    \"from torch.nn.functional import kl_div\\n\",\n    \"import torch.nn.functional as F\\n\",\n    \"from UTIL.tensor_ops import repeat_at\\n\",\n    \"probs = torch.Tensor(\\n\",\n    \"    [\\n\",\n    \"        [0.4, 0.6],\\n\",\n    \"        [0.3, 0.7],\\n\",\n    \"        [0.3, 0.7]\\n\",\n    \"    ]\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# probs.S # (?, n_agent, n_action)\\n\",\n    \"n_agent = probs.shape[-2]\\n\",\n    \"probs_rep = repeat_at(tensor=probs, insert_dim=-2, n_times=n_agent)\\n\",\n    \"# probs_rep.S # (?, n_agent, n_agent, n_action)\\n\",\n    \"probs_rep_transpose = probs_rep.swapaxes(-2,-3)\\n\",\n    \"mat = (probs_rep*probs_rep.log()-probs_rep*probs_rep_transpose.log()).sum(-1)\\n\",\n    \"mat # (?, n_agent, n_agent)\\n\",\n    \"# F.kl_div(probs_rep.log(), probs_rep_transpose, reduction='batchmean')\\n\",\n    \"\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 110,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"[[0. 2. 1. 1. 2. 1. 1. 2. 2. 2. 0. 2. 1. 0. 2. 0. 2. 2.]\\n\",\n      \" [2. 0. 2. 2. 1. 2. 2. 0. 0. 1. 2. 1. 2. 2. 0. 2. 1. 1.]\\n\",\n      \" [1. 2. 0. 0. 2. 0. 0. 2. 2. 2. 1. 2. 0. 1. 2. 1. 2. 2.]\\n\",\n      \" [1. 2. 0. 0. 2. 0. 0. 2. 2. 2. 1. 2. 0. 1. 2. 1. 2. 2.]\\n\",\n      \" [2. 1. 2. 2. 0. 2. 2. 1. 1. 0. 2. 0. 2. 2. 1. 2. 0. 0.]\\n\",\n      \" [1. 2. 0. 0. 2. 0. 0. 2. 2. 2. 1. 2. 0. 1. 2. 1. 2. 2.]\\n\",\n      \" [1. 2. 0. 0. 2. 0. 0. 2. 2. 2. 1. 2. 0. 1. 2. 1. 2. 2.]\\n\",\n      \" [2. 0. 2. 2. 1. 2. 2. 0. 0. 1. 2. 1. 2. 2. 0. 2. 1. 1.]\\n\",\n      \" [2. 0. 2. 2. 1. 2. 2. 0. 0. 1. 2. 1. 2. 2. 0. 2. 1. 1.]\\n\",\n      \" [2. 1. 2. 2. 0. 2. 2. 1. 1. 0. 2. 0. 2. 2. 1. 2. 0. 0.]\\n\",\n      \" [0. 2. 1. 1. 2. 1. 1. 2. 2. 2. 0. 2. 1. 0. 2. 0. 2. 2.]\\n\",\n      \" [2. 1. 2. 2. 0. 2. 2. 1. 1. 0. 2. 0. 2. 2. 1. 2. 0. 0.]\\n\",\n      \" [1. 2. 0. 0. 2. 0. 0. 2. 2. 2. 1. 2. 0. 1. 2. 1. 2. 2.]\\n\",\n      \" [0. 2. 1. 1. 2. 1. 1. 2. 2. 2. 0. 2. 1. 0. 2. 0. 2. 2.]\\n\",\n      \" [2. 0. 2. 2. 1. 2. 2. 0. 0. 1. 2. 1. 2. 2. 0. 2. 1. 1.]\\n\",\n      \" [0. 2. 1. 1. 2. 1. 1. 2. 2. 2. 0. 2. 1. 0. 2. 0. 2. 2.]\\n\",\n      \" [2. 1. 2. 2. 0. 2. 2. 1. 1. 0. 2. 0. 2. 2. 1. 2. 0. 0.]\\n\",\n      \" [2. 1. 2. 2. 0. 2. 2. 1. 1. 0. 2. 0. 2. 2. 1. 2. 0. 0.]]\\n\",\n      \"[[ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]\\n\",\n      \" [ 1  0  1  1  0  1  1  0  0  0  1  0  1  1  0  1  0  0]\\n\",\n      \" [ 3  2  1  1  0  1  1  2  2  0  3  0  1  3  2  3  0  0]\\n\",\n      \" [ 3  5  6  6  4  1  1  5  2  0  7  0  1  7  2  3  4  0]\\n\",\n      \" [ 3 11  6 13  9  1  1  5 10  0  7  0 12 15  2 14  4  8]\\n\",\n      \" [ 3 11  6 13  9  1 17  5 10 16  7  0 12 15  2 14  4  8]]\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"# n_agent = 16\\n\",\n    \"tree = get_division_tree(18)\\n\",\n    \"# current_level = 3\\n\",\n    \"# blood_distance = np.ones(shape=(n_agent,n_agent), dtype=np.float64) * np.nan\\n\",\n    \"# for i in range(n_agent):\\n\",\n    \"#     for j in range(n_agent):\\n\",\n    \"#         if i==j:blood_distance[i,j] = 0\\n\",\n    \"#         if not np.isnan(blood_distance[i,j]):\\n\",\n    \"#             continue\\n\",\n    \"#         for t in range(current_level+1):\\n\",\n    \"#             investigate_level = (current_level) - t\\n\",\n    \"#             if tree[investigate_level, i] == tree[investigate_level, j]:\\n\",\n    \"#                 blood_distance[i,j] = t\\n\",\n    \"#                 blood_distance[j,i] = t\\n\",\n    \"#                 break\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"blood_distance = get_blood_distance(tree, 2)\\n\",\n    \"print(blood_distance)\\n\",\n    \"print(tree)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 72,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"array([[ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],\\n\",\n       \"       [ 1,  0,  1,  0,  1,  1,  1,  1,  0,  1,  0,  0,  0,  1,  0,  0],\\n\",\n       \"       [ 1,  0,  3,  2,  3,  1,  1,  1,  2,  3,  0,  2,  2,  3,  0,  0],\\n\",\n       \"       [ 1,  0,  3,  5,  7,  6,  1,  6,  2,  7,  4,  5,  2,  3,  4,  0],\\n\",\n       \"       [12,  0,  3, 11, 15,  6,  1, 13, 10,  7,  9,  5,  2, 14,  4,  8]])\"\n      ]\n     },\n     \"execution_count\": 72,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"\\n\",\n    \"[[ 0.  2.  2. nan  2. nan  2. nan nan  2. nan  2. nan nan  2. nan]\\n\",\n    \" [ 2.  0.  2. nan  2. nan  2. nan nan  2. nan  2. nan nan  2. nan]\\n\",\n    \" [ 2.  2.  0. nan  2. nan  2. nan nan  2. nan  2. nan nan  2. nan]\\n\",\n    \" [nan nan nan  0. nan  2. nan  2.  2. nan  2. nan  2.  2. nan  2.]\\n\",\n    \" [ 2.  2.  2. nan  0. nan  2. nan nan  2. nan  2. nan nan  2. nan]\\n\",\n    \" [nan nan nan  2. nan  0. nan  2.  2. nan  2. nan  2.  2. nan  2.]\\n\",\n    \" [ 2.  2.  2. nan  2. nan  0. nan nan  2. nan  2. nan nan  2. nan]\\n\",\n    \" [nan nan nan  2. nan  2. nan  0.  2. nan  2. nan  2.  2. nan  2.]\\n\",\n    \" [nan nan nan  2. nan  2. nan  2.  0. nan  2. nan  2.  2. nan  2.]\\n\",\n    \" [ 2.  2.  2. nan  2. nan  2. nan nan  0. nan  2. nan nan  2. nan]\\n\",\n    \" [nan nan nan  2. nan  2. nan  2.  2. nan  0. nan  2.  2. nan  2.]\\n\",\n    \" [ 2.  2.  2. nan  2. nan  2. nan nan  2. nan  0. nan nan  2. nan]\\n\",\n    \" [nan nan nan  2. nan  2. nan  2.  2. nan  2. nan  0.  2. nan  2.]\\n\",\n    \" [nan nan nan  2. nan  2. nan  2.  2. nan  2. nan  2.  0. nan  2.]\\n\",\n    \" [ 2.  2.  2. nan  2. nan  2. nan nan  2. nan  2. nan nan  0. nan]\\n\",\n    \" [nan nan nan  2. nan  2. nan  2.  2. nan  2. nan  2.  2. nan  0.]]\\n\",\n    \"\\n\",\n    \" \"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"{'d1': tensor([1.1618e-13, 4.3868e-16, 3.4678e-18, 0.0000e+00, 1.0000e+00, 8.3701e-14,\\n\",\n       \"         1.8273e-12], device='cuda:0', requires_grad=True),\\n\",\n       \" 'd2': tensor([0.0756, 0.2109, 0.0628, 0.0104, 0.3506, 0.1273, 0.1624],\\n\",\n       \"        device='cuda:0', requires_grad=True)}\"\n      ]\n     },\n     \"execution_count\": 2,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"import pickle\\n\",\n    \"with open('TEMP/wifi', 'rb') as f:\\n\",\n    \"    p_list = pickle.load(f)\\n\",\n    \"\\n\",\n    \"p_list\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 32,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"tensor([5.0909e-02, 1.9223e-04, 1.5197e-06, 8.2279e-11, 1.1147e-01, 3.6681e-02,\\n\",\n      \"        8.0075e-01], dtype=torch.float64)\\n\",\n      \"tensor([0.0756, 0.2109, 0.0628, 0.0104, 0.3506, 0.1273, 0.1624],\\n\",\n      \"       dtype=torch.float64)\\n\",\n      \"tensor(1.0829, dtype=torch.float64)\\n\",\n      \"tensor(2.6698, dtype=torch.float64)\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import torch\\n\",\n    \"import torch.nn.functional as F\\n\",\n    \"from torch.distributions.categorical import Categorical\\n\",\n    \"from torch.nn.functional import kl_div\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"logits1 = torch.Tensor([  4.4786,  -1.1005,  -5.9407, -15.7646,  5.2623,   4.1508,   7.2341]).type(torch.DoubleTensor)\\n\",\n    \"logits2 = torch.Tensor([ -0.5480,  0.4779, -0.7330, -2.5290,  0.9862, -0.0273,  0.2163]).type(torch.DoubleTensor)\\n\",\n    \"\\n\",\n    \"dist1 = Categorical(logits = logits1)\\n\",\n    \"dist2 = Categorical(logits = logits2)\\n\",\n    \"print(dist1.probs)\\n\",\n    \"print(dist2.probs)\\n\",\n    \"from torch.distributions import kl_divergence\\n\",\n    \"\\n\",\n    \"print(kl_divergence(dist1, dist2))\\n\",\n    \"print(kl_divergence(dist2, dist1))\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/plain\": [\n       \"tensor([1.1618e-13, 4.3868e-16, 3.4678e-18, 0.0000e+00, 1.0000e+00, 8.3701e-14,\\n\",\n       \"        1.8273e-12], device='cuda:0', requires_grad=True)\"\n      ]\n     },\n     \"execution_count\": 7,\n     \"metadata\": {},\n     \"output_type\": \"execute_result\"\n    }\n   ],\n   \"source\": [\n    \"# probs = torch.Tensor(\\n\",\n    \"#     [0, 1])\\n\",\n    \"# probs2 = torch.Tensor(\\n\",\n    \"#     [0.3, 0.7])\\n\",\n    \"\\n\",\n    \"# probs = p_list['d1']\\n\",\n    \"# probs2 = p_list['d2']\\n\",\n    \"\\n\",\n    \"# print(kl_div(probs.log(), probs2))\\n\",\n    \"# print(kl_div(probs2.log(), probs))\\n\",\n    \"\\n\",\n    \"# probs = torch.Tensor(\\n\",\n    \"#     [0, 1])\\n\",\n    \"# probs2 = torch.Tensor(\\n\",\n    \"#     [0.3, 0.7])\\n\",\n    \"\\n\",\n    \"# probs = p_list['d1']\\n\",\n    \"# probs2 = p_list['d2']\\n\",\n    \"\\n\",\n    \"# print(kl_div(probs.log(), probs2))\\n\",\n    \"# print(kl_div(probs2.log(), probs))\\n\",\n    \"\\n\",\n    \"# probs = torch.Tensor(\\n\",\n    \"#     [0, 1])\\n\",\n    \"# probs2 = torch.Tensor(\\n\",\n    \"#     [0.3, 0.7])\\n\",\n    \"\\n\",\n    \"# probs = p_list['d1']\\n\",\n    \"# probs2 = p_list['d2']\\n\",\n    \"\\n\",\n    \"# print(kl_div(probs.log(), probs2))\\n\",\n    \"# print(kl_div(probs2.log(), probs))\\n\",\n    \"\\n\",\n    \"# probs = torch.Tensor(\\n\",\n    \"#     [0, 1])\\n\",\n    \"# probs2 = torch.Tensor(\\n\",\n    \"#     [0.3, 0.7])\\n\",\n    \"\\n\",\n    \"# probs = p_list['d1']\\n\",\n    \"# probs2 = p_list['d2']\\n\",\n    \"\\n\",\n    \"# print(kl_div(probs.log(), probs2))\\n\",\n    \"# print(kl_div(probs2.log(), probs))\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 64,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"tensor([[[ 0.,  1.,  2.],\\n\",\n      \"         [ 3.,  4.,  5.]],\\n\",\n      \"\\n\",\n      \"        [[ 6.,  7.,  8.],\\n\",\n      \"         [ 9., 10., 11.]],\\n\",\n      \"\\n\",\n      \"        [[12., 13., 14.],\\n\",\n      \"         [15., 16., 17.]]])\\n\",\n      \"tensor([[[ 0.,  0.,  0.],\\n\",\n      \"         [ 3.,  4.,  5.]],\\n\",\n      \"\\n\",\n      \"        [[ 6.,  7.,  8.],\\n\",\n      \"         [ 0.,  0.,  0.]],\\n\",\n      \"\\n\",\n      \"        [[ 0.,  0.,  0.],\\n\",\n      \"         [15., 16., 17.]]])\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import torch\\n\",\n    \"import numpy as np\\n\",\n    \"from UTIL.tensor_ops import gather_righthand, repeat_at\\n\",\n    \"def scatter_righthand(scatter_into, src, index, check=True):\\n\",\n    \"    index = index.long()\\n\",\n    \"    i_dim = index.dim()\\n\",\n    \"    s_dim = src.dim()\\n\",\n    \"    t_dim = i_dim - 1\\n\",\n    \"    index_new_shape = list(src.shape)\\n\",\n    \"    index_new_shape[t_dim] = index.shape[t_dim]\\n\",\n    \"    for _ in range(i_dim, s_dim):\\n\",\n    \"        index = index.unsqueeze(-1)\\n\",\n    \"    index_expand = index.expand(index_new_shape)  # only this two line matters\\n\",\n    \"    return scatter_into.scatter(t_dim, index_expand, src)\\n\",\n    \"\\n\",\n    \"orig = torch.Tensor([[[ 0,  1,  2], [ 3,  4,  5]],\\n\",\n    \"                    [[ 6,  7,  8], [ 9, 10, 11]],\\n\",\n    \"                    [[12, 13, 14], [15, 16, 17]]])\\n\",\n    \"index = torch.Tensor([[0], [1], [0]])\\n\",\n    \"print(orig)\\n\",\n    \"\\n\",\n    \"res      = gather_righthand(src=orig, index=index)\\n\",\n    \"res[:] = 0\\n\",\n    \"\\n\",\n    \"orig_fix = orig.clone().detach()\\n\",\n    \"orig_fix = scatter_righthand(orig_fix, src=res, index=index)\\n\",\n    \"print(orig_fix)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# orig.scatter(dim=1,index=index.long(),src=res)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 71,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"s1\\n\",\n      \"s1\\n\",\n      \"s2\\n\",\n      \"s2\\n\",\n      \"torch.Size([64, 16, 8, 88, 888])\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"index = torch.randint(high=16,size=(64,5))\\n\",\n    \"print('s1')\\n\",\n    \"res = gather_righthand(src,index)\\n\",\n    \"print('s1')\\n\",\n    \"res[:] = 0\\n\",\n    \"print('s2')\\n\",\n    \"resX = scatter_righthand(scatter_into=src, src=res, index=index)\\n\",\n    \"print('s2')\\n\",\n    \"print(resX.S)\\n\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"x2 tensor([[101., 101., 101., 101., 100., 100., 100.],\\n\",\n      \"        [202., 202., 202., 200., 201., 200., 200.],\\n\",\n      \"        [303., 303., 303., 300., 300., 301., 300.],\\n\",\n      \"        [404., 404., 404., 400., 400., 400., 401.]])\\n\",\n      \"perm_index tensor([2, 1, 3, 0])\\n\",\n      \"confact_x2 tensor([[401., 401., 401., 400., 400., 400., 401.],\\n\",\n      \"        [202., 202., 202., 200., 201., 200., 200.],\\n\",\n      \"        [103., 103., 103., 101., 100., 100., 100.],\\n\",\n      \"        [304., 304., 304., 300., 300., 301., 300.]])\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import torch\\n\",\n    \"import numpy as np\\n\",\n    \"from UTIL.tensor_ops import gather_righthand, repeat_at\\n\",\n    \"from UTIL.tensor_ops import add_onehot_id_at_last_dim, repeat_at, _2tensor, gather_righthand, scatter_righthand\\n\",\n    \"x_in = torch.Tensor([\\n\",\n    \"    [1,1,1],\\n\",\n    \"    [2,2,2],\\n\",\n    \"    [3,3,3],\\n\",\n    \"    [4,4,4],\\n\",\n    \"])\\n\",\n    \"n_agent = 4\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"nets = [\\n\",\n    \"    lambda x: x+100,\\n\",\n    \"    lambda x: x+200,\\n\",\n    \"    lambda x: x+300,\\n\",\n    \"    lambda x: x+400\\n\",\n    \"]\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"x0 = add_onehot_id_at_last_dim(x_in)\\n\",\n    \"# x1 = self.shared_net(x0)\\n\",\n    \"res = []\\n\",\n    \"for i in range(n_agent):\\n\",\n    \"    res.append(nets[i](x0[..., i, :]))\\n\",\n    \"x2 = torch.stack(res, -2)\\n\",\n    \"# x22 = self.nets[0](x1)\\n\",\n    \"print('x2',x2)\\n\",\n    \"\\n\",\n    \"######### forward twice: shuffle forward\\n\",\n    \"perm_index = torch.randperm(n_agent, device=x_in.device)   # shape = (n_agent)\\n\",\n    \"print('perm_index',perm_index)\\n\",\n    \"\\n\",\n    \"perm_index = perm_index.expand(x_in.shape[:-1]) # shape = (...?, n_agent)\\n\",\n    \"# x_in shape = (...?, n_agent, coredim)\\n\",\n    \"perm_x_in = gather_righthand(src=x_in, index=perm_index, check=False)\\n\",\n    \"perm_x0 = add_onehot_id_at_last_dim(perm_x_in)\\n\",\n    \"perm_res = []\\n\",\n    \"for i in range(n_agent):\\n\",\n    \"    perm_res.append(nets[i](perm_x0[..., i, :]))\\n\",\n    \"perm_x2 = torch.stack(perm_res, -2)\\n\",\n    \"\\n\",\n    \"# 103\\n\",\n    \"# 202\\n\",\n    \"# 304\\n\",\n    \"# 401\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"# 401 vs 101\\n\",\n    \"# 202 vs 202\\n\",\n    \"# 103 vs 303\\n\",\n    \"# 304 vs 404\\n\",\n    \"\\n\",\n    \"confact_x2 = torch.zeros_like(perm_x2) + np.nan\\n\",\n    \"confact_x2 = scatter_righthand(scatter_into=confact_x2, src=perm_x2, index=perm_index)\\n\",\n    \"print('confact_x2',confact_x2)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"tensor([False, False, False, False,  True])\\n\",\n      \"tensor([ True, False,  True, False, False])\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"import torch\\n\",\n    \"\\n\",\n    \"res=torch.isnan(torch.tensor([1, float('inf'), 2, float('-inf'), float('nan')]))\\n\",\n    \"print(res)\\n\",\n    \"\\n\",\n    \"res=torch.isfinite(torch.tensor([1, float('inf'), 2, float('-inf'), float('nan')]))\\n\",\n    \"print(res)\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"\\n\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"interpreter\": {\n   \"hash\": \"916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1\"\n  },\n  \"kernelspec\": {\n   \"display_name\": \"Python 3.8.10 64-bit\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.8.10\"\n  },\n  \"orig_nbformat\": 4,\n  \"vscode\": {\n   \"interpreter\": {\n    \"hash\": \"916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1\"\n   }\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/cradle.py",
    "content": "from UTIL.network import UnixTcpClientP2P, UnixTcpServerP2P\nimport time\n\n\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/escape.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"random-escape\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 8, 4 ],\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": true, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": true,\n      \"HeteAgents\": false,\n      \"UnrealLevel\": \"UhmapAttackPost\",\n      \"SubTaskSelection\": \"UhmapEscape\",\n      \"UhmapVersion\":\"3.8\",\n      \"UhmapRenderExe\": \"../../Build/WindowsNoEditor/UHMP.exe\",\n      \"UhmapServerExe\": \"../../Build/WindowsServer/UHMPServer.exe\",\n      // \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxNoEditor/UHMP.sh\",\n      // \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.script_ai.a_escape->EscapeGreenPreprogramBaseline\",\n        // \"ALGORITHM.random.foundation->RandomControllerWithActionSetV2\",\n        \"ALGORITHM.script_ai.a_escape->EscapeRedPreprogramBaseline\",\n      ]\n    },\n\n\n    \"ALGORITHM.script_ai.a_escape.py->AlgorithmConfig\": {\n    },\n\n\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/formation.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"random-attackpost\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 20, 20 ], // 10 ships, 2 waterdrops\n      \"MaxEpisodeStep\": 100,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": false, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": true,\n      \"HeteAgents\": false,\n      \"UnrealLevel\": \"UhmapFormation\",\n      \"SubTaskSelection\": \"UhmapFormation\",\n      \"UhmapVersion\":\"3.8\",\n      \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxNoEditor/UHMP.sh\",\n      \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 64, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.random.foundation->RandomControllerWithMomentumAgent\",\n        // \"ALGORITHM.random.foundation->RandomControllerWithActionSetV2\",\n        \"TEMP.TEAM2.ALGORITHM.random.foundation->RandomControllerWithMomentumAgent\"\n      ]\n    },\n\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n    },\n\n    // --- Part3: config ALGORITHM 2/2 --- \n    \"TEMP.TEAM2.ALGORITHM.random.foundation.py->AlgorithmConfig\": {\n    }\n\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/main.py",
    "content": "# This Python file uses the following encoding: utf-8\n\"\"\"\n    Author: Fu Qingxu,CASIA\n    Description: Enterance for everything in HMP\n    In this file you can find:\n        1.Config-Parsing; 2.Multiprocess-Initilization\n        3.GPU-Selection;  4.Seed-Setting\n    If you are interested in something, you may continue to read:\n        Handling parallel environment             -->   task_runner.py & shm_env.py\n        Link between teams and diverse algorithms -->   multi_team.py\n        Adding new env                            -->   MISSION.env_router.py\n        Adding algorithm                          -->   ALGORITHM.example_foundation.py\n        Configuring by writing py files           -->   config.py\n        Configuring by json                       -->   xx.json\n        colorful printing                         -->   colorful.py\n        auto pip deployer                         -->   pip_find_missing.py\n        efficient parallel execting               -->   shm_pool.pyx\n        auto gpu selection                        -->   auto_gpu.py\n        hmap logging/plotting bridge            -->   mcom.py & mcom_rec.py\n        experiment batch executor                 -->   mprofile.py\n\"\"\"\nimport os, atexit, platform\n\ndef SET_NUM_THREADS(internal_threads):\n    os.environ['NUM_THREADS'] = str(internal_threads)\n    os.environ['OPENBLAS_NUM_THREADS'] = str(internal_threads)\n    os.environ['MKL_NUM_THREADS'] = str(internal_threads)\n    os.environ['OMP_NUM_THREADS'] = str(internal_threads)\nSET_NUM_THREADS(1)\n\n# do NOT edit this func\ndef pytorch_gpu_init(cfg):\n    import torch\n    from UTIL.auto_gpu import sel_gpu\n    torch.set_num_threads(int(os.environ['NUM_THREADS']))\n    seed = cfg.seed; device = cfg.device\n    torch.manual_seed(seed)\n    torch.set_printoptions(precision=4, sci_mode=False)\n    # e.g. device='cpu'\n    os.environ[\"CUDA_DEVICE_ORDER\"]=\"PCI_BUS_ID\"\n    if not 'cuda' in device: return\n    if device == 'cuda': gpu_index = sel_gpu().auto_choice()\n    else: # e.g. device='cuda:0'\n        gpu_index = int(device.split(':')[-1])\n        cfg.manual_gpu_ctl = True\n        if cfg.gpu_fraction!=1: torch.cuda.set_per_process_memory_fraction(cfg.gpu_fraction, gpu_index)\n    os.environ['CUDA_VISIBLE_DEVICES'] = str(gpu_index)\n    cfg.device = 'cuda' # remove ':x', the selected gpu is cuda:0 from now on\n    torch.cuda.manual_seed(seed)\n    if cfg.use_float64:\n        torch.set_default_dtype(torch.float64)\n\n\ndef register_daemon(cfg):\n    from UTIL.hmp_daemon import start_periodic_daemon\n    start_periodic_daemon(cfg)\n\n\nif __name__ == '__main__':\n    import numpy\n    import pyximport; pyximport.install(build_dir='./TEMP/build/', inplace=True, language_level=3, setup_args={'include_dirs': numpy.get_include()})\n    from UTIL.colorful import *\n    from UTIL.config_args import prepare_args\n    from UTIL.shm_pool import SmartPool\n    cfg = prepare_args()\n    register_daemon(cfg)\n\n    # Set numpy seed\n    numpy.random.seed(cfg.seed)\n    numpy.set_printoptions(3, suppress=True)\n\n    # Get mem-sharing process pool\n    assert cfg.num_threads % cfg.fold == 0, ('Use n process to run n*m parallel threads!')\n    smart_pool = SmartPool(fold=cfg.fold, proc_num=cfg.num_threads // cfg.fold, base_seed=cfg.seed)\n    atexit.register(smart_pool.party_over)  # failsafe, handles shm leak\n\n    # Pytorch has to be init AFTER the process pool starts, set pytorch seed\n    pytorch_gpu_init(cfg=cfg)\n\n    # Prepare everything else\n    from task_runner import Runner\n    runner = Runner(process_pool=smart_pool)\n    # GO! GO! GO!\n    runner.run() \n\n    # DONE!\n    print绿('--- All jobs finished ---')\n    smart_pool.party_over()\n\nelif platform.system()!=\"Linux\":\n    # Linux uses fork for multi-processing, but Windows does not, reload config for Windows\n    from UTIL.config_args import prepare_args\n    cfg = prepare_args(vb=False)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/multi_server.py",
    "content": "base = \"\"\"\n{\n    \"config.py->GlobalConfig\": {\n        \"note\": \"sc-MMM2-conc-db1\",               // experiment note, also means the log saving directory\n        // \"train_time_testing\": \"False\",                      // do not manage train time testing, pymarl env manage the testing itself\n        \"env_name\":\"sc2\",                                   // starcraft 2\n        \"env_path\":\"MISSION.starcraft.sc2_env_wrapper\",    // starcraft 2\n        // \"interested_agent_num\":100,                         // only for reward logging, **not needed because sc2 use uniform team reward\n        \"draw_mode\": \"Img\",                                 // plot curlves as image\n        \"num_threads\": \"16\",                                 // number of parallel envs\n        \"report_reward_interval\": \"128\",                      // report the reward averaging x episodes\n        \"test_interval\": \"512\",                             // begin a test run every x episodes, test run is managed by pymarl side\n        \"test_epoch\": \"64\",                                 // begin a test run every x episodes, test run is managed by pymarl side\n        \"device\": \"cuda\",\n        \"max_n_episode\": 1500000,\n        \"fold\": \"1\",                                        // each linux process handle x parallel envs\n        \"backup_files\":[\n        ]\n    },\n\n    \"MISSION.starcraft.sc2_env_wrapper.py->ScenarioConfig\": {\n        \"map_\": \"MMM2\",\n        \"sc_version\": \"2.4.6\",\n        // \"map_\": \"5m_vs_6m\",\n        // \"SINGLE_TEAM_N_AGENT\": 5,\n        // \"episode_limit\": 60,\n        // \"reward_vec\": true,\n        \"TEAM_NAMES\": [\n            \"ALGORITHM.conc_4hist_scdb.foundation->ReinforceAlgorithmFoundation\"\n        ]\n    },\n\n    \"ALGORITHM.conc_4hist_scdb.foundation.py->AlgorithmConfig\": {\n        \"train_traj_needed\": \"128\",\n        \"n_focus_on\": 3,\n        \"actor_attn_mod\": \"False\",\n        \"lr\": 0.0001,\n        \"ppo_epoch\": 24,\n        \"load_checkpoint\": \"False\"\n    }\n}\n\"\"\"\n\n\nimport commentjson as json\nimport numpy as np\nbase_conf = json.loads(base)\nn_run = 4\nn_run_mode = [\n    {\n        \"addr\": \"localhost:2266\",\n        \"usr\": \"hmp\",\n        \"pwd\": \"hmp\"\n    },\n]*n_run\nassert len(n_run_mode)==n_run\n\nsum_note = \"MMM2-conc4hist\"\nconf_override = {\n\n    \"config.py->GlobalConfig-->seed\":       \n        [\n            np.random.randint(0, 10000) for _ in range(n_run)\n        ],\n\n    \"config.py->GlobalConfig-->device\":\n        [\n            'cuda',\n            'cuda',\n            'cuda',\n            'cuda',\n        ],\n\n    \"config.py->GlobalConfig-->note\":\n        [\n            \"n_focus_on_run1_3focus\",\n            \"n_focus_on_run2_3focus\",\n            \"n_focus_on_run1_5focus\",\n            \"n_focus_on_run2_5focus\",\n        ],\n\n    \"ALGORITHM.conc_4hist_scdb.foundation.py->AlgorithmConfig-->n_focus_on\":\n        [\n            3,\n            3,\n            5,\n            5,\n        ],\n}\n\nif __name__ == '__main__':\n    # copy the experiments\n    import shutil, os\n    shutil.copyfile(__file__, os.path.join(os.path.dirname(__file__), 'batch_experiment_backup.py'))\n    # run experiments remotely\n    from UTIL.batch_exp import run_batch_exp\n    run_batch_exp(sum_note, n_run, n_run_mode, base_conf, conf_override, __file__)\n"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/multi_team.py",
    "content": "import numpy as np\nimport importlib\nfrom UTIL.data_struct import UniqueList\n\nclass MMPlatform(object):\n\n    def __init__(self, mcv, envs):\n        from config import GlobalConfig\n        self.n_t =           GlobalConfig.ScenarioConfig.N_TEAM                # n_t => n_teams\n        n_agents_each_t =    GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM     # n_agents_each_t => n_agents_each_team\n        self.t_member_list = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM\n        self.t_name =        GlobalConfig.ScenarioConfig.TEAM_NAMES\n        assert self.n_t == len(self.t_name), 'Team does not match agent id'    # check N_TEAM\n        assert self.n_t == len(UniqueList(self.t_name)), 'Team name must not repeat' # please duplicate algorithm if needed\n        self.align_episode = GlobalConfig.align_episode\n        self.n_thread =      GlobalConfig.num_threads\n        self.legacy_act_order = True\n        if GlobalConfig.mt_act_order == 'new_method':\n            self.legacy_act_order = False\n        self.RewardAsUnity = False # env give reward of each team instead of agent\n        if hasattr(GlobalConfig.ScenarioConfig, 'RewardAsUnity'):\n            self.RewardAsUnity = GlobalConfig.ScenarioConfig.RewardAsUnity \n        self.ActAsUnity = False\n        if hasattr(GlobalConfig.ScenarioConfig, 'ActAsUnity'):\n            self.ActAsUnity = GlobalConfig.ScenarioConfig.ActAsUnity\n        self.ObsAsUnity = False\n        if hasattr(GlobalConfig.ScenarioConfig, 'ObsAsUnity'):\n            self.ObsAsUnity = GlobalConfig.ScenarioConfig.ObsAsUnity\n\n        space = envs.get_space()    # get observation space and action space\n\n        self.algo_foundations = []  # import and initialize algorithms\n        for t in range(self.n_t):\n            assert len(self.t_member_list[t]) == n_agents_each_t[t]\n            assert '->' in self.t_name[t]\n            module_, class_ = self.t_name[t].split('->')\n            init_f = getattr(importlib.import_module(module_), class_)\n            self.algo_foundations.append(\n                init_f(n_agent=n_agents_each_t[t], n_thread=self.n_thread, space=space, mcv=mcv, team=t)\n            )\n        pass\n\n\n    def act(self, runner_info):\n        actions_list = []\n        for t_name, t_members, algo_fdn, t_index in zip(self.t_name, self.t_member_list, self.algo_foundations, range(self.n_t)):\n            # split intel such as reward and observation into different teams\n            _t_intel_ = self._split_intel(runner_info, t_members, t_name, t_index)\n            # each team (controlled by different algorithms) interacts with env and act\n            _act_, _t_intel_ = algo_fdn.interact_with_env(_t_intel_)\n            # concat actions of each agent ('_act_' --> 'actions_list')\n            actions_list = self._append_act_to_list(_act_, actions_list, t_members)\n            # loop back internal states registered in _t_intel_  (e.g._division_obs_)\n            if _t_intel_ is None: continue\n            # process internal states loop back, featured with keys that startswith and endswith '_'\n            for key in _t_intel_:\n                if key.startswith('_') and key.endswith('_'): \n                    self._update_runner(runner_info, runner_info['ENV-PAUSE'], t_name, key, _t_intel_[key])\n        pass\n\n        # swapaxes:  [n_agent(n_teams if ActAsUnity), n_thread] --> [n_thread, $n_agent(n_teams if ActAsUnity)]\n        actions_list = np.swapaxes(np.array(actions_list, dtype=np.double), 0, 1) \n\n        # in align_episode mod, threads that are paused are forced to give NaN action\n        ENV_PAUSE = runner_info['ENV-PAUSE']\n        if ENV_PAUSE.any() and self.align_episode: actions_list[ENV_PAUSE,:] = np.nan\n        return actions_list, runner_info\n\n    def before_terminate(self, runner_info):\n        for t_name, t_members, t_index in zip(self.t_name, self.t_member_list, range(self.n_t)):\n            # split info such as reward and observation\n            self._split_intel(runner_info, t_members, t_name, t_index)\n\n\n    def _update_runner(self, runner_info, ENV_PAUSE, t_name, key, content):\n        u_key = t_name+key\n        if (u_key in runner_info) and hasattr(content, '__len__') and \\\n                len(content)==self.n_thread and ENV_PAUSE.any():\n            runner_info[u_key][~ENV_PAUSE] = content[~ENV_PAUSE]\n            return\n        runner_info[u_key] = content\n        return\n\n    # seperate observation between teams\n    def _split_intel(self, runner_info, t_members, t_name, t_index):\n        # RUNNING = ~runner_info['ENV-PAUSE']\n        # Team_Info and ter_obs_echo are None when runner_info['Latest-Team-Info'] is absent\n        Team_Info = None\n        ter_obs_echo = None\n        # load Team_Info and ter_obs_echo\n        if runner_info['Latest-Team-Info'] is not None:\n            assert isinstance(runner_info['Latest-Team-Info'][0], dict)\n            Team_Info = runner_info['Latest-Team-Info']\n            # if a env just ended ('Env-Suffered-Reset'), the final step obs can be acquired here\n            ter_obs_echo = np.array([self.__split_obs_thread(Team_Info[thread_idx]['obs-echo'], t_index)\n                            if done and ('obs-echo' in Team_Info[thread_idx]) else None \n                            for thread_idx, done in enumerate(runner_info['Env-Suffered-Reset'])], dtype=object)\n\n        o = self.__split_obs(runner_info['Latest-Obs'], t_index)\n        reward = runner_info['Latest-Reward']\n\n        # summary\n        t_intel_basic = {\n            'Team_Name':            t_name,\n            'Latest-Obs':           o, \n            'Latest-Team-Info':     Team_Info,\n            'Env-Suffered-Reset':   runner_info['Env-Suffered-Reset'],\n            'Terminal-Obs-Echo':    ter_obs_echo, \n            'ENV-PAUSE':            runner_info['ENV-PAUSE'],\n            'Test-Flag':            runner_info['Test-Flag'],\n            'Latest-Reward':        reward[:, t_members] if not self.RewardAsUnity else reward[:, t_index],\n            'Current-Obs-Step':     runner_info['Current-Obs-Step']\n        }\n\n        for key in runner_info:\n            if not (t_name in key): continue\n            # otherwise t_name in key\n            s_key = key.replace(t_name, '')\n            t_intel_basic[s_key] = runner_info[key]\n            if (s_key != '_hook_'): continue\n            # otherwise deal with _hook_\n            if t_intel_basic['_hook_'] is not None:\n                self.deal_with_hook(t_intel_basic['_hook_'], t_intel_basic)\n                runner_info[key] = None\n                t_intel_basic['_hook_'] = None\n            # remove _hook_ key\n            t_intel_basic.pop('_hook_')\n            \n        # t_intel_basic = self.filter_running(t_intel_basic, RUNNING)\n        return t_intel_basic\n\n    def _append_act_to_list(self, _act_, actions_list, t_members):\n        if not self.legacy_act_order: _act_ = np.swapaxes(_act_, 0, 1) \n        assert _act_.shape[0]==len(t_members), ('number of actions differs number of agents!')\n        append_op = actions_list.append if self.ActAsUnity else actions_list.extend\n        append_op(_act_)\n        return actions_list\n\n    def deal_with_hook(self, hook, t_intel_basic):\n        # use the hook left by algorithm to callback some function \n        # to deliver reward and reset signals\n        # assert self.L_RUNNING is not None\n        # t_intel_basic = self.filter_running(t_intel_basic, self.L_RUNNING)\n        hook({  'reward':t_intel_basic['Latest-Reward'], \n                'done': t_intel_basic['Env-Suffered-Reset'],\n                'info': t_intel_basic['Latest-Team-Info'],\n                'Latest-Obs':t_intel_basic['Latest-Obs'],\n                'Terminal-Obs-Echo': t_intel_basic['Terminal-Obs-Echo'],\n                })\n\n    def notify_teams(self, message, **kargs):\n        for t_index, algo_fdn in enumerate(self.algo_foundations):\n            if (not hasattr(algo_fdn, 'on_notify')) or (not callable(algo_fdn.on_notify)): continue\n            team_kargs = {k:v[t_index] for k,v in kargs.items()}\n            algo_fdn.on_notify(message, **team_kargs)\n            \n    def __split_obs(self, obs, t_index):\n        # obs [n_thread, n_team/n_agent, coredim]\n        if obs[0] is None:\n            o = None\n        elif self.ObsAsUnity:\n            o = obs[:, t_index]\n        else:   # in most cases\n            o = obs[:, self.t_member_list[t_index]]\n        return o\n\n    def __split_obs_thread(self, obs, t_index):\n        # obs [n_thread, n_team/n_agent, coredim]\n        if self.ObsAsUnity:\n            o = obs[t_index]\n        else:   # in most cases\n            o = obs[self.t_member_list[t_index]]\n        return o"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/multi_team_parallel.py",
    "content": "import numpy as np\nimport importlib, copy\nfrom UTIL.data_struct import UniqueList\nfrom UTIL.shm_pool import SmartPool\n\nclass alg_parallel_wrapper(object):\n    def __init__(self, t_name, n_agent, n_thread, space, mcv, team) -> None:\n        self.team = team\n        if mcv is None: mcv = self.init_alg_logger()\n        module_, class_ = t_name.split('->')\n        init_f = getattr(importlib.import_module(module_), class_)\n        self.alg = init_f(n_agent, n_thread, space, mcv, team)\n        self._hook_deligate_ = None\n\n    def interact_with_env(self, _input_):\n        _act_, _t_intel_ = self.alg.interact_with_env(_input_)\n        for k in list(_t_intel_.keys()):\n            if not k.startswith('_'): _t_intel_.pop(k)\n        # _act_.shape=(n_thread, n_agent, action_dim)\n        if '_hook_' in _t_intel_ and _t_intel_['_hook_'] is not None:\n            self._hook_deligate_ = _t_intel_.pop('_hook_')\n            _t_intel_['_hook_'] = 'call_hook_deligate'\n        return _act_, _t_intel_\n\n    def call_hook_deligate(self, callback_arg):\n        assert self._hook_deligate_ is not None\n        self._hook_deligate_(callback_arg)\n        self._hook_deligate_ = None\n\n    # -- you may delete it or replace it with Tensorboard --\n    def init_alg_logger(self):\n        from config import GlobalConfig as cfg\n        from VISUALIZE.mcom import mcom\n        logdir = cfg.logdir\n        if cfg.activate_logger:\n            mcv = mcom( path=f'{logdir}/logger/{self.team}/',\n                        image_path=f'{logdir}/team-{self.team}.jpg',\n                        rapid_flush=True,\n                        draw_mode=cfg.draw_mode,\n                        tag='[multi_team_parallel.py]',\n                        resume_mod=cfg.resume_mod)\n        mcv.rec_init(color='k')\n        return mcv\n\nclass MMPlatform(object):\n\n    def __init__(self, mcv, envs):\n        from config import GlobalConfig\n        self.n_t =           GlobalConfig.ScenarioConfig.N_TEAM                # n_t => n_teams\n        n_agents_each_t =    GlobalConfig.ScenarioConfig.N_AGENT_EACH_TEAM     # n_agents_each_t => n_agents_each_team\n        self.t_member_list = GlobalConfig.ScenarioConfig.AGENT_ID_EACH_TEAM\n        self.t_name =        GlobalConfig.ScenarioConfig.TEAM_NAMES\n        assert self.n_t == len(self.t_name), 'Team does not match agent id'    # check N_TEAM\n        assert self.n_t == len(UniqueList(self.t_name)), 'Team name must not repeat' # please duplicate algorithm if needed\n        self.align_episode = GlobalConfig.align_episode\n        self.n_thread =      GlobalConfig.num_threads\n        self.legacy_act_order = True\n        if GlobalConfig.mt_act_order == 'new_method':\n            self.legacy_act_order = False\n        self.RewardAsUnity = False # env give reward of each team instead of agent\n        if hasattr(GlobalConfig.ScenarioConfig, 'RewardAsUnity'):\n            self.RewardAsUnity = GlobalConfig.ScenarioConfig.RewardAsUnity \n        self.ActAsUnity = False\n        if hasattr(GlobalConfig.ScenarioConfig, 'ActAsUnity'):\n            self.ActAsUnity = GlobalConfig.ScenarioConfig.ActAsUnity\n        self.ObsAsUnity = False\n        if hasattr(GlobalConfig.ScenarioConfig, 'ObsAsUnity'):\n            self.ObsAsUnity = GlobalConfig.ScenarioConfig.ObsAsUnity\n\n        space = envs.get_space()    # get observation space and action space\n        arg_list = []\n        self.algo_foundations = []  # import and initialize algorithms\n        for t in range(self.n_t):\n            assert len(self.t_member_list[t]) == n_agents_each_t[t]\n            assert '->' in self.t_name[t]\n            arg_list.append((\n                self.t_name[t], # 't_name'\n                n_agents_each_t[t], # 'n_agent'\n                self.n_thread,      # 'n_thread'\n                space,              # 'space'\n                None,               # 'mcv'\n                t,                  # 'team'\n            ))\n\n        print('[multi_team_parallel] distributing algorithm to independent process')\n        self.alg_parallel_exe = SmartPool(fold=1, proc_num=self.n_t, base_seed=GlobalConfig.seed)\n        self.alg_parallel_exe.add_target(\n            name='alg_parallel_exe', \n            lam=alg_parallel_wrapper, \n            args_list=arg_list\n        )\n        print('[multi_team_parallel] distribution is done')\n        pass\n\n\n    def act(self, runner_info):\n        actions_list = []\n\n        _t_intel_feed_list_ = []\n        for t_name, t_members, t_index in zip(self.t_name, self.t_member_list, range(self.n_t)):\n            # split intel such as reward and observation into different teams\n            _t_intel_ = self._split_intel(runner_info, t_members, t_name, t_index)\n            _t_intel_feed_list_.append(_t_intel_)\n        \n        results = self.alg_parallel_exe.exec_target(name='alg_parallel_exe', dowhat='interact_with_env', args_list=_t_intel_feed_list_, ensure_safe=True)\n        # each team (controlled by different algorithms) interacts with env and act\n        # _act_, _t_intel_ = algo_fdn.interact_with_env(_t_intel_)\n        _act_mt_, _t_intel_mt_ = zip(*results)\n\n        for t_name, t_members, _act_, _t_intel_, t_index in zip(self.t_name, self.t_member_list, _act_mt_, _t_intel_mt_, range(self.n_t)):\n            # concat actions of each agent ('_act_' --> 'actions_list')\n            actions_list = self._append_act_to_list(_act_, actions_list, t_members)\n            # loop back internal states registered in _t_intel_  (e.g._division_obs_)\n            if _t_intel_ is None: continue\n            # process internal states loop back, featured with keys that startswith and endswith '_'\n            for key in _t_intel_:\n                if key.startswith('_') and key.endswith('_'): \n                    self._update_runner(runner_info, runner_info['ENV-PAUSE'], t_name, key, _t_intel_[key])\n        pass\n\n        # swapaxes:  [n_agent(n_teams if ActAsUnity), n_thread] --> [n_thread, $n_agent(n_teams if ActAsUnity)]\n        actions_list = np.swapaxes(np.array(actions_list, dtype=np.double), 0, 1) \n\n        # in align_episode mod, threads that are paused are forced to give NaN action\n        ENV_PAUSE = runner_info['ENV-PAUSE']\n        if ENV_PAUSE.any() and self.align_episode: actions_list[ENV_PAUSE,:] = np.nan\n        return actions_list, runner_info\n\n    def before_terminate(self, runner_info):\n        for t_name, t_members, t_index in zip(self.t_name, self.t_member_list, range(self.n_t)):\n            # split info such as reward and observation\n            self._split_intel(runner_info, t_members, t_name, t_index)\n\n\n    def _update_runner(self, runner_info, ENV_PAUSE, t_name, key, content):\n        u_key = t_name+key\n        if (u_key in runner_info) and hasattr(content, '__len__') and \\\n                len(content)==self.n_thread and ENV_PAUSE.any():\n            runner_info[u_key][~ENV_PAUSE] = content[~ENV_PAUSE]\n            return\n        runner_info[u_key] = content\n        return\n\n    # seperate observation between teams\n    def _split_intel(self, runner_info, t_members, t_name, t_index):\n        # RUNNING = ~runner_info['ENV-PAUSE']\n        # Team_Info and ter_obs_echo are None when runner_info['Latest-Team-Info'] is absent\n        Team_Info = None\n        ter_obs_echo = None\n        # load Team_Info and ter_obs_echo\n        if runner_info['Latest-Team-Info'] is not None:\n            assert isinstance(runner_info['Latest-Team-Info'][0], dict)\n            Team_Info = runner_info['Latest-Team-Info']\n            # if a env just ended ('Env-Suffered-Reset'), the final step obs can be acquired here\n            ter_obs_echo = np.array([None for _ in range(self.n_thread)], dtype=object)\n            for thread_idx, done in enumerate(runner_info['Env-Suffered-Reset']):\n                if done and ('obs-echo' in Team_Info[thread_idx]):\n                    ter_obs_echo[thread_idx] = self.__split_obs_thread(Team_Info[thread_idx]['obs-echo'], t_index)\n\n        Team_Info_Downstream = copy.deepcopy(Team_Info)\n        for i in range(len(Team_Info_Downstream)):\n            if 'obs-echo' in Team_Info_Downstream[i]:\n                Team_Info_Downstream[i].pop('obs-echo')\n\n        o = self.__split_obs(runner_info['Latest-Obs'], t_index)\n        reward = runner_info['Latest-Reward']\n\n        # summary\n        t_intel_basic = {\n            'Team_Name':            t_name,\n            'Latest-Obs':           o, \n            'Latest-Team-Info':     Team_Info_Downstream,\n            'Env-Suffered-Reset':   runner_info['Env-Suffered-Reset'],\n            'Terminal-Obs-Echo':    ter_obs_echo, \n            'ENV-PAUSE':            runner_info['ENV-PAUSE'],\n            'Test-Flag':            runner_info['Test-Flag'],\n            'Latest-Reward':        reward[:, t_members] if not self.RewardAsUnity else reward[:, t_index],\n            'Current-Obs-Step':     runner_info['Current-Obs-Step']\n        }\n\n        # deal with algorithm callback\n        key = f'{t_name}_hook_'\n        if (key in runner_info) and (runner_info[key] is not None):\n            t_intel_basic['_hook_'] = runner_info[key]\n            self.deal_with_hook(t_intel_basic['_hook_'], t_intel_basic, t_index)\n            runner_info[key] = None\n            t_intel_basic['_hook_'] = None\n            # remove _hook_ key\n            t_intel_basic.pop('_hook_')\n \n        # t_intel_basic = self.filter_running(t_intel_basic, RUNNING)\n        return t_intel_basic\n\n    def _append_act_to_list(self, _act_, actions_list, t_members):\n        if not self.legacy_act_order: _act_ = np.swapaxes(_act_, 0, 1) \n        assert _act_.shape[0]==len(t_members), ('number of actions differs number of agents!')\n        append_op = actions_list.append if self.ActAsUnity else actions_list.extend\n        append_op(_act_)\n        return actions_list\n\n    def deal_with_hook(self, hook, t_intel_basic, t_index):\n        # use the hook left by algorithm to callback some function \n        # to deliver reward and reset signals\n        # assert self.L_RUNNING is not None\n        # t_intel_basic = self.filter_running(t_intel_basic, self.L_RUNNING)\n        arg = { 'reward':t_intel_basic['Latest-Reward'], \n                'done': t_intel_basic['Env-Suffered-Reset'],\n                'info': t_intel_basic['Latest-Team-Info'],\n                'Latest-Obs':t_intel_basic['Latest-Obs'],\n                'Terminal-Obs-Echo': t_intel_basic['Terminal-Obs-Echo'],\n        }\n        if hook == 'call_hook_deligate':\n            # name, dowhat, args_list index_list\n            self.alg_parallel_exe.exec_target(\n                name='alg_parallel_exe', \n                dowhat='call_hook_deligate', \n                args_list=[arg],\n                index_list=[t_index],\n                ensure_safe=True\n            )\n        else:\n            hook(arg)\n\n    def notify_teams(self, message, **kargs):\n        for algo_fdn in self.algo_foundations:\n            if (not hasattr(algo_fdn, 'on_notify')) or (not callable(algo_fdn.on_notify)): continue\n            algo_fdn.on_notify(message, **kargs)\n\n    def __split_obs(self, obs, t_index):\n        # obs [n_thread, n_team/n_agent, coredim]\n        if obs[0] is None:\n            o = None\n        elif self.ObsAsUnity:\n            o = obs[:, t_index]\n        else:   # in most cases\n            o = obs[:, self.t_member_list[t_index]]\n        return o\n\n    def __split_obs_thread(self, obs, t_index):\n        # obs [n_thread, n_team/n_agent, coredim]\n        if self.ObsAsUnity:\n            o = obs[t_index]\n        else:   # in most cases\n            o = obs[self.t_member_list[t_index]]\n        return o"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/reproduce.jsonc",
    "content": "{\n    // --- Part1: config HMP core --- \n    \"config.py->GlobalConfig\": {\n        \"note\": \"random-reproduce\",// http://localhost:59547\n        \"env_name\": \"uhmap\",\n        \"env_path\": \"MISSION.uhmap\",\n        // \"heartbeat_on\": \"False\",\n        \"draw_mode\": \"Img\",\n        \"num_threads\": 1,  // 请预留 num_threads * 1 GB 的内存空间\n        \"report_reward_interval\": 128,\n        \"test_interval\": 1280,\n        \"mt_act_order\": \"new_method\",\n        \"test_epoch\": 512,\n        \"interested_team\": 0,\n        \"seed\": 10098,\n        \"device\": \"cpu\",\n        \"max_n_episode\": 5000000,\n        \"fold\": 1,\n        \"backup_files\": [\n            \"MISSION/uhmap\"\n        ]\n    },\n\n\n      // --- Part2: config MISSION ---\n    \"MISSION.uhmap.uhmap_env_wrapper.py->ScenarioConfig\": {\n      \"N_AGENT_EACH_TEAM\": [ 200 ], // 8 agent, single team\n      \"MaxEpisodeStep\": 50,\n      \"StepGameTime\": 0.5,\n      \"StateProvided\": false,\n      \"render\": false, // note: random seed has different impact on renderer and server\n      \"UElink2editor\": false,\n      \"HeteAgents\": false,\n      \"UnrealLevel\": \"UhmapReproduce\",\n      \"SubTaskSelection\": \"UhmapReproduce\",\n      \"UhmapVersion\":\"3.8\",\n      \"UhmapRenderExe\": \"../../Build/WindowsNoEditor/UHMP.exe\",\n      \"UhmapServerExe\": \"../../Build/WindowsServer/UHMPServer.exe\",\n      // \"UhmapRenderExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxNoEditor/UHMP.sh\",\n      // \"UhmapServerExe\": \"/home/hmp/UnrealHmapBinary/Version3.8/LinuxServer/UHMPServer.sh\",\n      \"TimeDilation\": 1, // simulation time speed up, larger is faster\n      \"TEAM_NAMES\": [\n        \"ALGORITHM.script_ai.a_test_reproduce->TestReproduce\",\n      ]\n    },\n\n    // --- Part3: config ALGORITHM 1/2 --- \n    \"ALGORITHM.script_ai.a_test_reproduce.py->AlgorithmConfig\": {\n    },\n\n    \n}"
  },
  {
    "path": "PythonExample/hmp_minimal_modules/task_runner.py",
    "content": "\"\"\"\n    Author: Fu Qingxu,CASIA\n    Description: HMP task runner, coordinates environments and algorithms\n    Notes before you read code: \n    In general, HMP task runner can operate two ways:\n        self.align_episode = False: threads immediately restart at terminal state, threads do not wait each other\n        self.align_episode = True: threads pause at terminal state, waiting until all threads terminate, then reset\n\"\"\"\n\nimport time, os\nimport numpy as np\nfrom UTIL.colorful import *\nfrom UTIL.exp_helper import upload_exp\nfrom config import GlobalConfig as cfg\nfrom MISSION.env_router import make_parallel_envs\nclass Runner(object):\n    def __init__(self, process_pool):\n        self.process_pool = process_pool\n        self.envs = make_parallel_envs(process_pool)          # parallel environments start\n        self.mcv = self.get_a_logger(cfg.note)                # multiagent silent logging bridge active\n        if cfg.mt_parallel: from multi_team_parallel import MMPlatform  # parallel the decision process\n        else:               from multi_team          import MMPlatform\n        self.platform_controller = MMPlatform(self.mcv, self.envs)  # block infomation access between teams\n        self.info_runner = {}                                       # dict of realtime obs, reward, reward, info et.al.\n        self.n_agent  =  sum(cfg.ScenarioConfig.N_AGENT_EACH_TEAM)\n        self.n_team  =   len(cfg.ScenarioConfig.N_AGENT_EACH_TEAM)\n        # please specify: env gives reward of each team instead of agent ?\n        self.RewardAsUnity = False  \n        if hasattr(cfg.ScenarioConfig, 'RewardAsUnity'):\n            self.RewardAsUnity = cfg.ScenarioConfig.RewardAsUnity\n        # let test env sleep (when not used) to save memory ?\n        self.test_env_sleepy = False\n        if hasattr(cfg.ScenarioConfig, 'CanTurnOff'):\n            self.test_env_sleepy = cfg.ScenarioConfig.CanTurnOff\n        self.n_thread = cfg.num_threads\n        self.n_frame =  cfg.n_parallel_frame\n        self.note =     cfg.note   # experiment note\n        self.hb_on =    cfg.heartbeat_on and stdout.isatty()    # show the environment stepping heartbeat\n        self.current_n_frame = 0\n        self.current_n_episode = 0\n        self.max_n_episode = cfg.max_n_episode\n        # Reward monitoring for agents of your interest\n        self.train_time_testing = cfg.train_time_testing\n        self.test_interval = cfg.test_interval\n        self.test_only = cfg.test_only\n        self.align_episode = cfg.align_episode\n        self._exit_early_ = False\n        self._init_interested_agent_logging() \n\n    # -------------------------------------------------------------------------\n    # ------------------------------ Major Loop -------------------------------\n    # -------------------------------------------------------------------------\n    def run(self):\n        # all item in self.info_runner: shape =(n_thread, n_agent/n_team, ...)\n        self.init_runner()\n        # test machine performance\n        tic = time.time()\n        # start simulation\n        for cnt in range(self.n_frame):\n            # line 1: get action, block infomation access between teams (LINK to ARGORITHM)\n            # (The controller can also handle algorithm internal state loopback by following simple rules)\n            actions_list, self.info_runner = self.platform_controller.act(self.info_runner)\n            # line 2: multi-thread environment step (LINK to MISSION)\n            # (When thread align is needed, NaN actions will be used to make envs freeze for a step)\n            obs, reward, done, info = self.envs.step(actions_list)\n            # line 3: prepare obs and reward for next round \n            # (If required, a test run will be started at proper time)\n            self.info_runner = self.update_runner(done, obs, reward, info)\n            toc=time.time(); dt = toc-tic; tic = toc\n            if self.hb_on: print('\\r [task runner]: FPS %d, episode steping %s       '%(\n                self.get_fps(dt), self.heartbeat()), end='', flush=True)\n            if self._exit_early_: print('exit_early'); break\n        # All task done! Time to shut down\n        return\n\n    def init_runner(self):\n        self.info_runner['Test-Flag'] = self.test_only  # not testing mode for rl methods\n        self.info_runner['Recent-Reward-Sum'] = []\n        self.info_runner['Recent-Win'] = []\n        self.info_runner['Recent-Team-Ranking'] = []\n        obs_info = self.envs.reset() # assumes only the first time reset is manual\n        self.info_runner['Latest-Obs'], self.info_runner['Latest-Team-Info'] = obs_info if isinstance(obs_info, tuple) else (obs_info, None)\n        self.info_runner['Env-Suffered-Reset'] = np.array([True for _ in range(self.n_thread)])\n        self.info_runner['ENV-PAUSE']          = np.array([False for _ in range(self.n_thread)])\n        self.info_runner['Current-Obs-Step']   = np.array([0 for _ in range(self.n_thread)])\n        self.info_runner['Latest-Reward']      = np.zeros(shape=(self.n_thread, self.n_agent))\n        self.info_runner['Latest-Reward-Sum']  = np.zeros(shape=(self.n_thread, self.n_agent))\n        self.info_runner['Thread-Episode-Cnt'] = np.array([0 for _ in range(self.n_thread)])\n        if self.RewardAsUnity:\n            self.info_runner['Latest-Reward']  = np.zeros(shape=(self.n_thread, self.n_team))\n            self.info_runner['Latest-Reward-Sum'] = np.zeros(shape=(self.n_thread, self.n_team))\n        return\n\n    def update_runner(self, done, obs, reward, info):\n        P = self.info_runner['ENV-PAUSE']\n        R = ~P\n        assert info is not None\n        if self.info_runner['Latest-Team-Info'] is None: self.info_runner['Latest-Team-Info'] = info\n        self.info_runner['Latest-Obs'][R] = obs[R]\n        self.info_runner['Latest-Team-Info'][R] = info[R]\n        self.info_runner['Latest-Reward'][R] = reward[R]    # note, reward shape: (thread, n-team\\n-agent)\n        self.info_runner['Latest-Reward-Sum'][R] += reward[R]\n        self.info_runner['Current-Obs-Step'][R] += 1\n        for i in range(self.n_thread):\n            self.info_runner['Env-Suffered-Reset'][i] = done[i].all()\n            # if the environment has not been reset, do nothing\n            if P[i] or (not self.info_runner['Env-Suffered-Reset'][i]): continue\n            # otherwise, the environment just been reset\n            self.current_n_frame += self.info_runner['Current-Obs-Step'][i]\n            self.current_n_episode += 1\n            self.info_runner['Recent-Reward-Sum'].append(self.info_runner['Latest-Reward-Sum'][i].copy())\n            term_info = self.info_runner['Latest-Team-Info'][i]\n            # update win/lose (single-team), or team ranking (multi-team)\n            win = 1 if 'win' in term_info and term_info['win']==True else 0\n            self.info_runner['Recent-Win'].append(win)\n            if 'team_ranking' in term_info: \n                self.info_runner['Recent-Team-Ranking'].append(term_info['team_ranking'].copy())\n            self.info_runner['Latest-Reward-Sum'][i] = 0\n            self.info_runner['Current-Obs-Step'][i] = 0\n            self.info_runner['Thread-Episode-Cnt'][i] += 1\n            # hault finished threads to wait unfinished ones\n            if self.align_episode: self.info_runner['ENV-PAUSE'][i] = True\n            # monitoring agents/team of interest\n            if self.current_n_episode % self.report_interval == 0: \n                self._checkout_interested_agents(self.info_runner)  # monitor rewards for some specific agents\n                self.info_runner['Recent-Reward-Sum'] = []\n                self.info_runner['Recent-Win'] = []\n                self.info_runner['Recent-Team-Ranking'] = []\n            # begin a testing session?\n            if self.train_time_testing and (not self.test_only) and (self.current_n_episode % self.test_interval == 0): \n                self.platform_controller.before_terminate(self.info_runner)\n                self.start_a_test_run()\n        # all threads haulted, finished and Aligned, then restart all thread\n        if self.align_episode and self.info_runner['ENV-PAUSE'].all():  self.info_runner['ENV-PAUSE'][:] = False\n        # when too many episode is done, Terminate flag on.\n        if self.current_n_episode >= self.max_n_episode: self._exit_early_ = True\n        return self.info_runner\n\n\n\n\n    # ------------------------------------------------------------------------------------------------------------------------------------------\n    # ------------------------------------------ About TEST RUN routine, almost a Mirror of above ----------------------------------------------\n    # ------------------------------------------------------------------------------------------------------------------------------------------\n    # -- I know these code below might merge with above for simplicity --\n    # -- But I decide not, in order to make it easier to read and debug --\n    if cfg.train_time_testing:\n        def start_a_test_run(self):\n            print靛('\\r[task runner]: test run started!')\n            self.init_test_runner()\n            # loop until all env is done\n            assert cfg.test_epoch%self.n_thread == 0, ('please set test_epoch as (n_thread * N)!')\n            num_runs = cfg.test_epoch // self.n_thread\n            print靛('\\r[task runner]: test run is going to run %d episode'%cfg.test_epoch)\n            while True:\n                actions_list, self.test_info_runner = self.platform_controller.act(self.test_info_runner)\n                obs, reward, done, info = self.test_envs.step(actions_list)\n                self.test_info_runner = self.update_test_runner(done, obs, reward, info)\n                if self.hb_on: print('\\r [task runner]: testing %s  '%self.heartbeat(\n                    style=3, beat=self.test_info_runner['Current-Obs-Step']), end='', flush=True)\n                # If the test run reach its end, record the reward and win-rate:\n                if (self.test_info_runner['Thread-Episode-Cnt']>=num_runs).all():\n                    # get the reward average\n                    t_win_rates, t_rewards = self._checkout_interested_agents(self.test_info_runner, testing=True)\n                    self.platform_controller.before_terminate(self.test_info_runner)\n                    self.platform_controller.notify_teams('test done', win_rate=t_win_rates, mean_reward=t_rewards)\n                    # close all\n                    if self.test_env_sleepy: self.test_envs.sleep()\n                    return\n        def init_test_runner(self):\n            if not hasattr(self, 'test_envs'):\n                self.test_envs = make_parallel_envs(self.process_pool, marker='test') # 平行环境\n            self.test_info_runner = {}  # dict of realtime obs, reward, reward, info et.al.\n            self.test_info_runner['ENV-PAUSE'] = np.array([False for _ in range(self.n_thread)])\n            self.test_info_runner['Test-Flag'] = True\n            self.test_info_runner['Recent-Win'] = []\n            self.test_info_runner['Recent-Reward-Sum'] = []\n            self.test_info_runner['Recent-Team-Ranking'] = []\n            test_obs_info = self.test_envs.reset() # assume only the first time reset is manual\n            self.test_info_runner['Latest-Obs'], self.test_info_runner['Latest-Team-Info'] = test_obs_info if isinstance(test_obs_info, tuple) else (test_obs_info, None)\n            self.test_info_runner['Env-Suffered-Reset'] = np.array([True for _ in range(self.n_thread)])\n            self.test_info_runner['Latest-Reward'] = np.zeros(shape=(self.n_thread, self.n_agent))\n            self.test_info_runner['Latest-Reward-Sum'] = np.zeros(shape=(self.n_thread, self.n_agent))\n            self.test_info_runner['Current-Obs-Step'] = np.array([0 for _ in range(self.n_thread)])\n            self.test_info_runner['Thread-Episode-Cnt'] = np.array([0 for _ in range(self.n_thread)])\n            if self.RewardAsUnity:\n                self.test_info_runner['Latest-Reward'] = np.zeros(shape=(self.n_thread, self.n_team))\n                self.test_info_runner['Latest-Reward-Sum'] = np.zeros(shape=(self.n_thread, self.n_team))\n            return\n        def update_test_runner(self, done, obs, reward, info):\n            P = self.test_info_runner['ENV-PAUSE']\n            R = ~P\n            assert info is not None\n            if self.test_info_runner['Latest-Team-Info'] is None: self.test_info_runner['Latest-Team-Info'] = info\n            self.test_info_runner['Latest-Obs'][R] = obs[R]\n            self.test_info_runner['Latest-Team-Info'][R] = info[R]\n            self.test_info_runner['Latest-Reward'][R] = reward[R]\n            self.test_info_runner['Latest-Reward-Sum'][R] += reward[R]\n            self.test_info_runner['Current-Obs-Step'][R] += 1\n            for i in range(self.n_thread):\n                self.test_info_runner['Env-Suffered-Reset'][i] = done[i].all()\n                # if the environment has not been reset, do nothing\n                if P[i] or (not self.test_info_runner['Env-Suffered-Reset'][i]): continue\n                # otherwise, the environment just been reset\n                self.test_info_runner['Recent-Reward-Sum'].append(self.test_info_runner['Latest-Reward-Sum'][i].copy())\n                self.test_info_runner['Latest-Reward-Sum'][i] = 0\n                self.test_info_runner['Current-Obs-Step'][i] = 0\n                self.test_info_runner['Thread-Episode-Cnt'][i] += 1\n                term_info = self.test_info_runner['Latest-Team-Info'][i]\n                win = 1 if 'win' in term_info and term_info['win']==True else 0\n                self.test_info_runner['Recent-Win'].append(win)\n                if 'team_ranking' in term_info: \n                    self.test_info_runner['Recent-Team-Ranking'].append(term_info['team_ranking'].copy())\n                if self.align_episode: self.test_info_runner['ENV-PAUSE'][i] = True\n            if self.align_episode and self.test_info_runner['ENV-PAUSE'].all(): self.test_info_runner['ENV-PAUSE'][:] = False\n            return self.test_info_runner\n\n\n\n\n    # -- If you care much about the agents running your algorthm... --\n    # -- you may delete them if monitering is established in ALGORITHM level --\n    def _init_interested_agent_logging(self):\n        self.report_interval = cfg.report_reward_interval\n        self.interested_agents_uid = cfg.interested_agent_uid\n        self.interested_team = cfg.interested_team\n        self.top_rewards = None\n        self.test_top_rewards = None\n        return\n    def _checkout_interested_agents(self, info_runner, testing=False):\n        # (1). record mean reward\n        if not testing: self.mcv.rec(self.current_n_episode, 'time')\n        prefix = 'test' if testing else ''\n        recent_rewards = np.stack(info_runner['Recent-Reward-Sum'])\n        mean_reward_each_team = []\n        if self.RewardAsUnity:\n            for interested_team in range(self.n_team):\n                mean_reward_each_team.append(recent_rewards[:, interested_team].mean().copy())\n        else:\n            for interested_team in range(self.n_team):\n                tean_agent_uid = cfg.ScenarioConfig.AGENT_ID_EACH_TEAM[interested_team]\n                mean_reward_each_team.append(recent_rewards[:, tean_agent_uid].mean().copy())\n\n        for team in range(self.n_team):\n            self.mcv.rec(mean_reward_each_team[team], f'{prefix} reward of=team-{team}')\n\n        # (2).reflesh historical top reward\n        if not testing: \n            if self.top_rewards is None: self.top_rewards = mean_reward_each_team\n            top_rewards_list_pointer = self.top_rewards\n        else:\n            if self.test_top_rewards is None: self.test_top_rewards = mean_reward_each_team\n            top_rewards_list_pointer = self.test_top_rewards\n        for team in range(self.n_team):\n            if mean_reward_each_team[team] > top_rewards_list_pointer[team]:\n                top_rewards_list_pointer[team] = mean_reward_each_team[team]\n            self.mcv.rec(top_rewards_list_pointer[team], f'{prefix} top reward of=team-{team}')\n\n        # (3).record winning rate (single-team) or record winning rate (multi-team)\n        # for team in range(self.n_team):\n        teams_ranking = info_runner['Recent-Team-Ranking']\n        win_rate_each_team = [0]*self.n_team\n        if len(teams_ranking)>0:\n            for team in range(self.n_team):\n                rank_itr_team = np.array(teams_ranking)[:, team]\n                win_rate = (rank_itr_team==0).mean()  # 0 means rank first\n                win_rate_each_team[team] = win_rate\n                self.mcv.rec(win_rate, f'{prefix} top-rank ratio of=team-{team}')\n        else:\n            team = 0; assert self.n_team == 1, \"There is only one team\"\n            win_rate_each_team[team] = np.array(info_runner['Recent-Win']).mean()\n            win_rate = np.array(info_runner['Recent-Win']).mean()\n            self.mcv.rec(win_rate, f'{prefix} win rate of=team-{team}')\n\n        # plot the figure\n        self.mcv.rec_show()\n        if testing: \n            print_info = ['\\r[task runner]: Test result at episode %d.'%(self.current_n_episode)]\n        else:\n            print_info = ['\\r[task runner]: (%s) Finished episode %d, frame %d.'%(self.note, self.current_n_episode, self.current_n_frame)]\n\n        for team in range(self.n_team): \n            print_info.append(' | team-%d: win rate: %.3f, recent reward %.3f'%(team, win_rate_each_team[team], mean_reward_each_team[team]))\n        print靛(''.join(print_info))\n            \n        return win_rate_each_team, mean_reward_each_team\n\n\n    # -- below is nothing of importance --\n    # -- you may delete it or replace it with Tensorboard --\n    @staticmethod\n    def get_a_logger(note):\n        from VISUALIZE.mcom import mcom\n        logdir = cfg.logdir\n        if cfg.activate_logger:\n            mcv = mcom( path='%s/logger/'%logdir,\n                        digit=16,\n                        rapid_flush=True,\n                        draw_mode=cfg.draw_mode,\n                        tag='[task_runner.py]',\n                        resume_mod=cfg.resume_mod)\n            cfg.data_logger = mcv\n        mcv.rec_init(color='b')\n        return mcv\n\n    def heartbeat(self, style=0, beat=None):\n        # default ⠁⠈⠐⠠⢀⡀⠄⠂\n        width = os.get_terminal_size().columns\n        if style==0: sym = ['⠁','⠈','⠐','⠠','⢀','⡀','⠄','⠂',]\n        elif style==1: sym = ['◐ ','◓ ','◑ ','◒ ']\n        elif style==2: sym = ['▁','▂','▃','▄','▅','▆','▇','█']\n        elif style==3: sym = ['💐','🌷','🌸','🌹','🌺','🌻','🌼',]\n        if beat is None: beat = self.info_runner['Current-Obs-Step']\n        beat = beat % len(sym)\n        beat = beat[:int(width*0.2)]\n        beat.astype(int)\n        beat = [sym[t] for t in beat]\n        return ''.join(beat)\n\n    \n    def get_fps(self, dt):\n        new_fps = int(self.n_thread/dt)\n        if not hasattr(self, 'fps_smooth'):\n            self.fps_smooth = new_fps\n        else:\n            self.fps_smooth = self.fps_smooth*0.98 + new_fps*0.02\n        return int(self.fps_smooth)"
  },
  {
    "path": "README.md",
    "content": "# Unreal-MAP \n\n[English](README.md) | [中文](README_CN.md)\n\n[![Version](https://img.shields.io/badge/version-3.14-blue)](https://github.com/binary-husky/unreal-map)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.7+-blue)](https://www.python.org/)\n[![Unreal Engine](https://img.shields.io/badge/Unreal%20Engine-4.27-blue)](https://www.unrealengine.com/)\n[![stars](https://img.shields.io/github/stars/binary-husky/unreal-map)](https://github.com/binary-husky/unreal-map)\n[![Documentation](https://img.shields.io/badge/docs-English-blue)](README.md)\n\n\nThis is **Unreal Multi-Agent Playground** (Unreal-Map), a multi-agent general platform based on [Unreal Engine](https://www.unrealengine.com/).\nHere you can use all the capabilities of Unreal Engine (Blueprints, Behavior tree, Physics engine, AI navigation, 3D models/animations and Plugin resources, etc) to build elegant (but also computational efficient) and magnificent (but also experimentally reproducible) multi-agent environments.\n\nUnreal-MAP can not only be used to develop conventional multi-agent simulation environments, but has also been optimized for Multi-Agent Reinforcement Learning (MARL) simulation. You can use it to develop various realistic and complex MARL scenarios. You can also use Unreal-MAP together with our developed [HMAP](https://github.com/binary-husky/hmp2g) (a powerful MARL-specific experimental framework) to easily develop MARL scenarios and quickly deploy cutting-edge algorithms.\n\n\n> The present study aims to identify potential collaboration partners. If interested in this research project, please feel free to contact our office at CASIA: tenghai.qiu@ia.ac.cn, hutianyi2021@ia.ac.cn.\n> \n\n\n**Please ```star``` the Github project. Your encouragement is extremely important to us as researchers: ```https://github.com/binary-husky/unreal-map```** !\n\n\n\n<div align=\"center\">\n<img src=\"Docs/Imgs/Overall.png\"/ width=\"550\"> \n</div>\n\n# 1. Introduction\n### 1.1 Basic Introduction\nUnreal-based Multi-Agent Playground (Unreal-MAP) is a new generation of multi-agent general platform based on the Unreal Engine.\nThis platform supports adversial training between swarms & algorithms, and it is the first (and currently the only) extensible RL/MARL environment based on the Unreal Engine to support multi-team training.\n\n### 1.2 Architecture\n<div align=\"center\">\n<img src=\"Docs/Imgs/Architecture.png\"/ width=\"800\"> \n</div>\n\nUnreal-MAP employs a hierarchical five-layer architecture,\nwhere each layer builds upon the previous one. From bottom\nto top,the five layers are: *native layer*, *specification layer*, *base class layer*, ***advanced module layer***, and ***interface layer***.\nlayer. **You only need to focus on the *advanced module layer* (Blueprint) and the *interface layer* (Python).** \nFrom the perspective of creating a standard MARL scenario, using these two layers is sufficient to modify all elements in the task (e.g., POMDP) such as states, actions, observations, transitions, etc.\n\n### 1.3 Features\n\nUnreal-MAP can be used to develop various multi-agent simulation scenarios. Our case studies have already included scenarios with large-scale, heterogeneous, and multi-team characteristics.\n**Compared to other RL general platforms** such as [Unity ML-Agents](https://unity-technologies.github.io/ml-agents/), Unreal-MAP has the following advantages in terms of scientific research and experiment:\n\n**(1) Fully Open-Source and Easily Modifiable**: Unreal-MAP utilizes a layered design, and all components from the bottom-level engine to the top-level interfaces are open-sourced.\n\n**(2) Optimized Specifically for MARL**: The underlying engine of Unreal-MAP has been optimized to enhance efficiency in large-scale agent simulations and data transmission. \n\n**(3) Parallel Multi-Process Execution and Controllable Single-Process Time Flow**: Unreal-MAP supports the parallel execution of multiple simulation processes as well as the adjustment of the simulation time flow speed in a single process. You can accelerate simulations to speed up training or decelerate simulations for detailed slow-motion analysis.\n\n\n**Compared to all current MARL simulation environments**, Unreal-MAP has advantages in terms of scientific research and experiment:\n\n- **Freely build realistic tasks** using the massive resources available in the [Unreal Engine Marketplace](https://www.fab.com/).\n- Simultaneously supports **large-scale, heterogeneous, multi-team** simulations.\n- **Highly efficient training** with TPS (Timesteps per second) up to 10k+ and FPS (Frames per second) up to 10M+.\n- **Controllable simulation time**: you can accelerate simulation to speed up training (until CPU is fully utilized, acceleration doesn't consume extra memory or VRAM), or decelerate for slow-motion analysis.\n- **Strong reproducibility**: eliminated various butterfly effect factors in Unreal Engine that could cause experimental irreproducibility.\n- **Multi-platform support**: compile both Headless mode and rendering mode clients on Windows, Linux, and MacOS. \n- **Rich rendering mechanisms**: supports a) rendering in the UE editor, b) on a compiled pure rendering client, c) cross-platform real-time rendering. You can train on a Linux server and render on Windows host at the same time!\n\n<div align=\"center\">\n<img src=\"Docs/unreal-island.jpg\" height=\"200\" width=\"320\"/> <img src=\"https://github.com/binary-husky/unreal-map/assets/96192199/985c2c27-bc0a-4c90-a036-ec676d7aec1d\" height=\"200\" width=\"320\"/> \n</div>\n\n<div align=\"center\">\n<img src=\"Docs/Demo/uhmap-bbad.jpg\" height=\"200\" width=\"320\"/> <img src=\"Docs/Demo/uhmap-hete.jpg\" height=\"200\" width=\"320\"/> \n</div>\n<div align=\"center\">\n<img src=\"Docs/Demo/2023-02-12 155956.jpg\" height=\"200\" width=\"320\"/> <img src=\"Docs/Demo/2023-02-12 151938.jpg\" height=\"200\" width=\"320\"/> \n</div>\n\n\n### 1.4 Some Future Works\n\nUnreal-MAP introduces modern game engines into the MARL field with tremendous potential. This potential is mainly reflected in two dimensions: **Scalability** and **Realism**. In terms of scalability, users can not only ***freely*** construct environments using the extremely rich resources from the Unreal Engine community, but can also ***quickly*** build environments according to their ideas using Unreal Engine's future generative AI plugins (such as [ACE](https://developer.nvidia.com/ace.)).\n\nIn terms of realism, users can leverage Unreal-MAP to build ***highly realistic*** MARL environments and even develop ***digital twins*** of real-world scenarios. We attempted a sim2real demo using Unreal-MAP. In this demo, we first deployed a multi-UAV-UGV gaming scenario in the experimental field, then recreated the scenario using Unreal-MAP (including model proportions, agent kinematics and dynamics, etc.). We conducted training in the sim environment and then validated it in the real-world scenario, achieving preliminary positive results. In the current solution, Unreal-MAP not only serves as a simulation environment creator, but also acts as a data transmission intermediary, connecting data from the real-world scenario with the algorithmic side.\n\n<div align=\"center\">\n<img src=\"Docs/Imgs/Sim2RealEXP.png\" width=\"800\"/> \n</div>\n\n<div align=\"center\">\n<img src=\"Docs/Imgs/Sim2RealFra.png\" width=\"700\"/> \n</div>\n\n\n\n# 2. How to Install \n\n## 2.1 Professional version\n\n- Step 1, you must install the Unreal Engine from the source code. For details, see the official document of the Unreal Engine: ```https://docs.unrealengine.com/4.27/zh-CN/ProductionPipelines/DevelopmentSetup/BuildingUnrealEngine/```\n- Step 2: Clone the git resp ```git clone https://github.com/binary-husky/unreal-hmp.git```\n- Step 3: Download large files that github cannot manage. Run ```python Please_ Run_ This_ First_ To_ Fetch_ Big_ Files.py```\n- Step 4: Right click the ```UHMP.upproject``` downloaded in step 3, select ```switch unreal engine version```, and then select ```source build at xxxxx``` to confirm. Then open the generated ```UHMP. sln``` and compile it\n- Finally, double-click ```UHMP. upproject``` to enter the Unreal Engine Editor.\n\nNote that steps 1 and 4 are difficult. It is recommended to refer to the following video (the 0:00->1:46 in the video is the steps 1, and 1:46->end is steps 4): ```https://ageasga-my.sharepoint.com/:v:/g/personal/fuqingxu_yiteam_tech/EawfqsV2jF5Nsv3KF7X1-woBH-VTvELL6FSRX4cIgUboLg?e=Vmp67E```\n\n\n\n## 2.2 Only compiled binary version \n\n```https://github.com/binary-husky/hmp2g/blob/master/ZDOCS/use_unreal_hmap.md```\n\n# 3. Tutorial \nThe document is being improved. For the video tutorial of simple demo, see ```EnvDesignTutorial.pptx``` (you need to complete step 3 of installation to download this pptx file)\n\nDirectory:\n- Chapter I. Unreal Engine\n- - Build a map (Level) ```https://www.bilibili.com/video/BV1U24y1D7i4/?spm_id_from=333.999.0.0&vd_source=e3bc3eddd1d2414cb64ae72b6a64df55```\n- - Establish Agent Actor\n- - Design agent blueprint program logic\n- - Episode key event notification mechanism\n- - Define Custom actions (Unreal Engine side)\n- - The Python side controls the custom parameters of the agent\n- Chapter II. Python Interface\n- - Create a task file (SubTask)\n- - Modify agent initialization code\n- - Modify the agent reward code\n- - Select the control algorithm of each team\n- - Full closed loop debugging method\n- Chapter III. Appendix\n- - Headless acceleration and cross-compiling Linux package\n- - Define Custom actions (Need to be familiar with the full closed-loop debugging method first)\n- - - Draft a list of actions\n- - - Python side action generation\n- - - UE-side action parse and execution\n- - - Action discretization\n- - Installation guide for cross compilation tool chain\n\n\n\n# 4. How to Build Binary Client\nRun following scripts.\n```\npython BuildlinuxRender.py\npython BuildLinuxServer.py\npython BuildWinRender.py\npython BuildWinServer.py\n```\n- Among them, ```Render/Server``` represents ```including graphic rendering / only computing```, the later is generally used for RL training.\n- Among them, ```Windows/linux``` represents the target operating system. Note that you need to install ```Unreal Engine Cross Compilation Tool``` to compile Linux programs on Windows.\n\n\n\n\n- After adding new ActionSets in ```Content/Assets/DefAction/ParseAction.uasset```, you may encounter ```Ensure condition failed: !FindPin(FFunctionEntryHelper::GetWorldContextPinName())``` error during packaging. If so, find and remove an extra blueprint function parameter named ```__WorldContext``` that you created by accident in ```ParseAction.uasset```. For more details: ```https://forums.unrealengine.com/t/ensure-condition-failed-on-project-start/469587```\n\n- If you encounter BuildCMakeLib.Automation.cs(45,54): error CS1002 after project migration, please **Rebuild** (not Build!) the AutomationTool in Visual Studio. For more details: ```https://forums.unrealengine.com/t/unreal-engine-version-4-27-2-i-get-an-error-when-trying-to-package-any-project/270627```\n\n# Cite this project !\n\n```\n@article{unrealmap,\n  title={Unreal-MAP: Unreal-Engine-Based General Platform for Multi-Agent Reinforcement Learning},\n  author={Hu, Tianyi and Fu, Qingxu and Pu, Zhiqiang and Wang, Yuan and Qiu, Tenghai},\n  journal={arXiv preprint arXiv:2503.15947},\n  year={2025}\n}\n```\n\n\n"
  },
  {
    "path": "README_CN.md",
    "content": "# Unreal-MAP \n\n[English](README.md) | [中文](README_CN.md)\n\n[![Version](https://img.shields.io/badge/version-3.14-blue)](https://github.com/binary-husky/unreal-map)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.7+-blue)](https://www.python.org/)\n[![Unreal Engine](https://img.shields.io/badge/Unreal%20Engine-4.27-blue)](https://www.unrealengine.com/)\n[![stars](https://img.shields.io/github/stars/binary-husky/unreal-map)](https://github.com/binary-husky/unreal-map)\n[![Documentation](https://img.shields.io/badge/docs-中文-blue)](README_CN.md)\n\n\n这是**虚幻多智能体游乐场**（Unreal-MAP），一个基于[虚幻引擎](https://www.unrealengine.com/)的多智能体通用平台。\n在这里，您可以使用虚幻引擎的所有功能（蓝图、行为树、物理引擎、AI导航、3D模型/动画和插件资源等）来构建优雅（但也计算高效）和宏伟（但也实验可重现）的多智能体环境。\n\nUnreal-MAP不仅可以用于开发常规的多智能体仿真环境，还针对多智能体强化学习（MARL）仿真进行了特殊优化。您可以使用它来开发各种真实和复杂的MARL场景。您还可以将Unreal-MAP与我们开发的[HMAP](https://github.com/binary-husky/hmp2g)（一个强大的MARL专用实验框架）一起使用，轻松开发MARL场景并快速部署前沿算法。\n\n\n> 本研究旨在寻找潜在的合作伙伴。如果对这个研究项目感兴趣，请随时联系我们中科院自动化研究所的办公室：tenghai.qiu@ia.ac.cn, hutianyi2021@ia.ac.cn。\n> \n\n\n**请为Github项目点亮```star```。作为研究人员，您的鼓励对我们来说极其重要：```https://github.com/binary-husky/unreal-hmp```** ！\n\n\n\n<div align=\"center\">\n<img src=\"Docs/Imgs/Overall.png\"/ width=\"600\"> \n</div>\n\n# 简介\n基于虚幻引擎的多智能体游乐场（Unreal-MAP）是一个基于虚幻引擎的新一代多智能体通用平台。\n该平台支持群体与算法之间的对抗训练，是第一个（也是目前唯一一个）支持多团队训练的基于虚幻引擎的可扩展RL/MARL环境。\n\n<div align=\"center\">\n<img src=\"Docs/Imgs/Architecture.png\"/ width=\"800\"> \n</div>\n\nUnreal-MAP采用分层五层架构，每一层都建立在前一层之上。从底层到顶层，这五层分别是：*原生层*、*规范层*、*基类层*、***高级模块层***和***接口层***。\n**您只需要关注*高级模块层*（蓝图）和*接口层*（Python）**。\n从创建标准MARL环境的角度来看，使用这两层就足以修改任务中的所有元素（例如POMDP），如状态、动作、观察、转换等。\n\nUnreal-MAP可用于开发各种多智能体仿真场景。我们的案例研究已经包括了具有大规模、异构和多团队特征的场景。\n**与其他RL通用平台**相比，如[Unity ML-Agents](https://unity-technologies.github.io/ml-agents/)，Unreal-MAP在科研和实验方面具有以下优势：\n\n**(1) 完全开源且易于修改**：Unreal-MAP采用分层设计，从底层引擎到顶层接口的所有组件都是开源的。\n\n**(2) 专门针对MARL优化**：Unreal-MAP的底层引擎已经过优化，以提高大规模智能体仿真和数据传输的效率。\n\n**(3) 并行多进程执行和可控的单进程时间流**：Unreal-MAP支持多个仿真进程的并行执行以及单个进程中仿真时间流速度的调整。您可以加速仿真以加快训练速度，或减速仿真以进行详细的慢动作分析。\n\n\n**与目前所有的MARL仿真环境相比**，Unreal-MAP在科研和实验方面具有优势：\n\n- 使用[虚幻引擎市场](https://www.fab.com/)中的海量资源**自由构建真实任务**。\n- 同时支持**大规模、异构、多团队**仿真。\n- **高效训练**，TPS（每秒时间步数）高达10k+，FPS（每秒帧数）高达10M+。\n- **可控的仿真时间**：您可以加速仿真以加快训练速度（直到CPU完全利用，加速不会消耗额外的内存或显存），或减速以进行慢动作分析。\n- **强大的可重现性**：消除了虚幻引擎中可能导致实验不可重现的各种蝴蝶效应因素。\n- **多平台支持**：在Windows、Linux和MacOS上编译无头模式和渲染模式客户端。\n- **丰富的渲染机制**：支持a）在UE编辑器中渲染，b）在编译的纯渲染客户端上渲染，c）跨平台实时渲染。\n\n<div align=\"center\">\n<img src=\"Docs/unreal-island.jpg\" height=\"250\" width=\"400\"/> <img src=\"https://github.com/binary-husky/unreal-map/assets/96192199/985c2c27-bc0a-4c90-a036-ec676d7aec1d\" height=\"250\" width=\"400\"/> \n</div>\n\n<div align=\"center\">\n<img src=\"Docs/Demo/uhmap-bbad.jpg\" height=\"250\" width=\"400\"/> <img src=\"Docs/Demo/uhmap-hete.jpg\" height=\"250\" width=\"400\"/> \n</div>\n<div align=\"center\">\n<img src=\"Docs/Demo/2023-02-12 155956.jpg\" height=\"250\" width=\"400\"/> <img src=\"Docs/Demo/2023-02-12 151938.jpg\" height=\"250\" width=\"400\"/> \n</div>\n\n\n# 如何安装 \n\n## 完整版本\n\n- 步骤1，您必须从源代码安装虚幻引擎。详情请参见虚幻引擎官方文档：```https://docs.unrealengine.com/4.27/zh-CN/ProductionPipelines/DevelopmentSetup/BuildingUnrealEngine/```\n- 步骤2：克隆git仓库 ```git clone https://github.com/binary-husky/unreal-hmp.git```\n- 步骤3：下载github无法管理的大文件。运行 ```python Please_ Run_ This_ First_ To_ Fetch_ Big_ Files.py```\n- 步骤4：右键点击步骤3中下载的```UHMP.upproject```，选择```切换虚幻引擎版本```，然后选择```source build at xxxxx```确认。然后打开生成的```UHMP.sln```并编译\n- 最后，双击```UHMP.upproject```进入虚幻引擎编辑器。\n\n注意，步骤1和4比较困难。建议参考以下视频（视频中0:00->1:46是步骤1，1:46->结束是步骤4）：```https://ageasga-my.sharepoint.com/:v:/g/personal/fuqingxu_yiteam_tech/EawfqsV2jF5Nsv3KF7X1-woBH-VTvELL6FSRX4cIgUboLg?e=Vmp67E```\n\n\n\n## 仅编译二进制版本 \n\n```https://github.com/binary-husky/hmp2g/blob/master/ZDOCS/use_unreal_hmap.md```\n\n# 教程 \n文档正在完善中。关于简单演示的视频教程，请参见```EnvDesignTutorial.pptx```（您需要完成安装步骤3才能下载此pptx文件）\n\n目录：\n- 第一章 虚幻引擎\n- - 构建地图（Level）```https://www.bilibili.com/video/BV1U24y1D7i4/?spm_id_from=333.999.0.0&vd_source=e3bc3eddd1d2414cb64ae72b6a64df55```\n- - 建立智能体Actor\n- - 设计智能体蓝图程序逻辑\n- - Episode关键事件通知机制\n- - 定义自定义动作（虚幻引擎端）\n- - Python端控制智能体的自定义参数\n- 第二章 Python接口\n- - 创建任务文件（SubTask）\n- - 修改智能体初始化代码\n- - 修改智能体奖励代码\n- - 选择每个团队的控制算法\n- - 完整闭环调试方法\n- 第三章 附录\n- - 无头加速和交叉编译Linux包\n- - 定义自定义动作（需要先熟悉完整闭环调试方法）\n- - - 起草动作列表\n- - - Python端动作生成\n- - - UE端动作解析和执行\n- - - 动作离散化\n- - 交叉编译工具链安装指南\n\n\n\n# 如何构建二进制客户端\n运行以下脚本。\n```\npython BuildlinuxRender.py\npython BuildLinuxServer.py\npython BuildWinRender.py\npython BuildWinServer.py\n```\n- 其中，```Render/Server```代表```包含图形渲染/仅计算```，后者通常用于RL训练。\n- 其中，```Windows/linux```代表目标操作系统。注意，您需要安装```虚幻引擎交叉编译工具```才能在Windows上编译Linux程序。\n\n\n\n\n# 常见问题\n- 在```Content/Assets/DefAction/ParseAction.uasset```中添加新的ActionSets后，打包时可能遇到```Ensure condition failed: !FindPin(FFunctionEntryHelper::GetWorldContextPinName())```错误。如果出现这种情况，请在```ParseAction.uasset```中找到并删除您不小心创建的名为```__WorldContext```的额外蓝图函数参数。更多详情：```https://forums.unrealengine.com/t/ensure-condition-failed-on-project-start/469587```\n\n- 如果在项目迁移后遇到BuildCMakeLib.Automation.cs(45,54): error CS1002，请在Visual Studio中**重新构建**（不是构建！）AutomationTool。更多详情：```https://forums.unrealengine.com/t/unreal-engine-version-4-27-2-i-get-an-error-when-trying-to-package-any-project/270627```\n\n# 引用\n```\n@article{unrealmap,\n  title={Unreal-MAP: Unreal-Engine-Based General Platform for Multi-Agent Reinforcement Learning},\n  author={Hu, Tianyi and Fu, Qingxu and Pu, Zhiqiang and Wang, Yuan and Qiu, Tenghai},\n  journal={arXiv preprint arXiv:2503.15947},\n  year={2025}\n}\n```\n\n# Dev log 项目开发日志\n- 2023-10-18 版本3.14\n- 2023-4-30 版本3.8，引入标准化的高效感知模块\n- 2023-3-9 正在尝试用共享内存通讯替换tcp通讯，以提高IO效率，待上传到4.0版本\n- 2023-3-1 实现高效感知模块，待上传到4.0版本\n- 2023-2-15 版本3.7融入master分支\n- 2023-2-14 3.7上传中\n- 2023-2-14 ```EnvDesignTutorial.pptx```中更新了自定义动作的文档\n- 2023-2-14 上传了一个微缩版的hmp代码，作为入门用的U-MAP驱动，文档待写\n- 2023-2-1 将读起来蹩脚的UHMAP缩写名称改为U-Map\n- 2023-1-8 update readme\n- 2023-12-25 covid is not a flu /(ㄒoㄒ)/\n- 2022-12-22 版本3.6融入master分支\n- 2022-12-21 解决智能体scale!=1的情况下，飞行智能体高度越来越低的问题\n- 2022-12-21 修复超大规模智能体数量情况下缓存区溢出的问题\n- 2022-12-18 优化大文件下载脚本\n- 2022-12-17 版本3.5融入master分支\n\n"
  },
  {
    "path": "Source/Jsonx/Jsonx.Build.cs",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\nnamespace UnrealBuildTool.Rules\n{\n\tpublic class Jsonx : ModuleRules\n\t{\n\t\tpublic Jsonx(ReadOnlyTargetRules Target) : base(Target)\n\t\t{\n\t\t\tPublicDependencyModuleNames.AddRange(\n\t\t\t\tnew string[]\n\t\t\t\t{\n\t\t\t\t\t\"Core\",\n\t\t\t\t}\n\t\t\t); \n\n\t\t\tPrivateIncludePaths.AddRange(\n\t\t\t\tnew string[] {\n\t\t\t\t\t\"Jsonx/Private\",\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Source/Jsonx/Private/Dom/JsonxObject.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"Dom/JsonxObject.h\"\n\n\nvoid FJsonxObject::SetField( const FString& FieldName, const TSharedPtr<FJsonxValue>& Value )\n{\n\tthis->Values.Add(FieldName, Value);\n}\n\n\nvoid FJsonxObject::RemoveField( const FString& FieldName )\n{\n\tthis->Values.Remove(FieldName);\n}\n\n\ndouble FJsonxObject::GetNumberField( const FString& FieldName ) const\n{\n\treturn GetField<EJsonx::None>(FieldName)->AsNumber();\n}\n\n\nbool FJsonxObject::TryGetNumberField( const FString& FieldName, double& OutNumber ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetNumber(OutNumber);\n}\n\n\nbool FJsonxObject::TryGetNumberField( const FString& FieldName, int32& OutNumber ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetNumber(OutNumber);\n}\n\n\nbool FJsonxObject::TryGetNumberField( const FString& FieldName, uint32& OutNumber ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetNumber(OutNumber);\n}\n\nbool FJsonxObject::TryGetNumberField(const FString& FieldName, int64& OutNumber) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetNumber(OutNumber);\n}\n\nvoid FJsonxObject::SetNumberField( const FString& FieldName, double Number )\n{\n\tthis->Values.Add(FieldName, MakeShared<FJsonxValueNumber>(Number));\n}\n\n\nFString FJsonxObject::GetStringField( const FString& FieldName ) const\n{\n\treturn GetField<EJsonx::None>(FieldName)->AsString();\n}\n\n\nbool FJsonxObject::TryGetStringField( const FString& FieldName, FString& OutString ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetString(OutString);\n}\n\n\nbool FJsonxObject::TryGetStringArrayField( const FString& FieldName, TArray<FString>& OutArray ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\n\tif (!Field.IsValid())\n\t{\n\t\treturn false;\n\t}\n\t \n\tconst TArray< TSharedPtr<FJsonxValue> > *Array;\n\n\tif (!Field->TryGetArray(Array))\n\t{\n\t\treturn false;\n\t}\n\n\tfor (int Idx = 0; Idx < Array->Num(); Idx++)\n\t{\n\t\tFString Element;\n\n\t\tif (!(*Array)[Idx]->TryGetString(Element))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tOutArray.Add(Element);\n\t}\n\n\treturn true;\n}\n\n\nvoid FJsonxObject::SetStringField( const FString& FieldName, const FString& StringValue )\n{\n\tthis->Values.Add(FieldName, MakeShared<FJsonxValueString>(StringValue));\n}\n\n\nbool FJsonxObject::GetBoolField( const FString& FieldName ) const\n{\n\treturn GetField<EJsonx::None>(FieldName)->AsBool();\n}\n\n\nbool FJsonxObject::TryGetBoolField( const FString& FieldName, bool& OutBool ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetBool(OutBool);\n}\n\n\nvoid FJsonxObject::SetBoolField( const FString& FieldName, bool InValue )\n{\n\tthis->Values.Add(FieldName, MakeShared<FJsonxValueBoolean>(InValue));\n}\n\n\nconst TArray<TSharedPtr<FJsonxValue>>& FJsonxObject::GetArrayField( const FString& FieldName ) const\n{\n\treturn GetField<EJsonx::Array>(FieldName)->AsArray();\n}\n\n\nbool FJsonxObject::TryGetArrayField(const FString& FieldName, const TArray< TSharedPtr<FJsonxValue> >*& OutArray) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetArray(OutArray);\n}\n\n\nvoid FJsonxObject::SetArrayField( const FString& FieldName, const TArray< TSharedPtr<FJsonxValue> >& Array )\n{\n\tthis->Values.Add(FieldName, MakeShared<FJsonxValueArray>(Array));\n}\n\n\nconst TSharedPtr<FJsonxObject>& FJsonxObject::GetObjectField( const FString& FieldName ) const\n{\n\treturn GetField<EJsonx::Object>(FieldName)->AsObject();\n}\n\n\nbool FJsonxObject::TryGetObjectField( const FString& FieldName, const TSharedPtr<FJsonxObject>*& OutObject ) const\n{\n\tTSharedPtr<FJsonxValue> Field = TryGetField(FieldName);\n\treturn Field.IsValid() && Field->TryGetObject(OutObject);\n}\n\n\nvoid FJsonxObject::SetObjectField( const FString& FieldName, const TSharedPtr<FJsonxObject>& JsonxObject )\n{\n\tif (JsonxObject.IsValid())\n\t{\n\t\tthis->Values.Add(FieldName, MakeShared<FJsonxValueObject>(JsonxObject.ToSharedRef()));\n\t}\n\telse\n\t{\n\t\tthis->Values.Add(FieldName, MakeShared<FJsonxValueNull>());\n\t}\n}\n"
  },
  {
    "path": "Source/Jsonx/Private/Dom/JsonxValue.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"Dom/JsonxValue.h\"\n#include \"Dom/JsonxObject.h\"\n\n\n\n\ndouble FJsonxValue::AsNumber() const\n{\n\tdouble Number = 0.0;\n\n\tif (!TryGetNumber(Number))\n\t{\n\t\tErrorMessage(TEXT(\"Number\"));\n\t}\n\n\treturn Number;\n}\n\n\nFString FJsonxValue::AsString() const \n{\n\tFString String;\n\n\tif (!TryGetString(String))\n\t{\n\t\tErrorMessage(TEXT(\"String\"));\n\t}\n\n\treturn String;\n}\n\n\nbool FJsonxValue::AsBool() const \n{\n\tbool Bool = false;\n\n\tif (!TryGetBool(Bool))\n\t{\n\t\tErrorMessage(TEXT(\"Boolean\")); \n\t}\n\n\treturn Bool;\n}\n\n\nconst TArray< TSharedPtr<FJsonxValue> >& FJsonxValue::AsArray() const\n{\n\tconst TArray<TSharedPtr<FJsonxValue>>* Array = nullptr;\n\n\tif (!TryGetArray(Array))\n\t{\n\t\tstatic const TArray< TSharedPtr<FJsonxValue> > EmptyArray;\n\t\tArray = &EmptyArray;\n\t\tErrorMessage(TEXT(\"Array\"));\n\t}\n\n\treturn *Array;\n}\n\n\nconst TSharedPtr<FJsonxObject>& FJsonxValue::AsObject() const\n{\n\tconst TSharedPtr<FJsonxObject>* Object = nullptr;\n\n\tif (!TryGetObject(Object))\n\t{\n\t\tstatic const TSharedPtr<FJsonxObject> EmptyObject = MakeShared<FJsonxObject>();\n\t\tObject = &EmptyObject;\n\t\tErrorMessage(TEXT(\"Object\"));\n\t}\n\n\treturn *Object;\n}\n\n// -----------------------------------\n\ntemplate <typename T>\nbool TryConvertNumber(const FJsonxValue& InValue, T& OutNumber)\n{\n\tdouble Double;\n\n\tif (InValue.TryGetNumber(Double) && (Double >= TNumericLimits<T>::Min()) && (Double <= static_cast<double>(TNumericLimits<T>::Max())))\n\t{\n\t\tOutNumber = static_cast<T>(FMath::RoundHalfFromZero(Double));\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n// Need special handling for int64/uint64, due to overflow in the numeric limits.\n// 2^63-1 and 2^64-1 cannot be exactly represented as a double, so TNumericLimits<>::Max() gets rounded up to exactly 2^63 or 2^64 by the compiler's implicit cast to double.\n// This breaks the overflow check in TryConvertNumber. We use \"<\" rather than \"<=\" along with the exact power-of-two double literal to fix this.\ntemplate <> bool TryConvertNumber<uint64>(const FJsonxValue& InValue, uint64& OutNumber)\n{\n\tdouble Double;\n\tif (InValue.TryGetNumber(Double) && Double >= 0.0 && Double < 18446744073709551616.0)\n\t{\n\t\tOutNumber = static_cast<uint64>(FMath::RoundHalfFromZero(Double));\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\ntemplate <> bool TryConvertNumber<int64>(const FJsonxValue& InValue, int64& OutNumber)\n{\n\tdouble Double;\n\tif (InValue.TryGetNumber(Double) && Double >= -9223372036854775808.0 && Double < 9223372036854775808.0)\n\t{\n\t\tOutNumber = static_cast<int64>(FMath::RoundHalfFromZero(Double));\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n// -----------------------------------\n\nbool FJsonxValue::TryGetNumber(float& OutNumber) const\n{\n\tdouble Double;\n\n\tif (TryGetNumber(Double))\n\t{\n\t\tOutNumber = static_cast<float>(Double);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool FJsonxValue::TryGetNumber(uint8& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(uint16& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(uint32& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(uint64& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(int8& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(int16& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(int32& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\nbool FJsonxValue::TryGetNumber(int64& OutNumber) const\n{\n\treturn TryConvertNumber(*this, OutNumber);\n}\n\n//static \nbool FJsonxValue::CompareEqual( const FJsonxValue& Lhs, const FJsonxValue& Rhs )\n{\n\tif (Lhs.Type != Rhs.Type)\n\t{\n\t\treturn false;\n\t}\n\n\tswitch (Lhs.Type)\n\t{\n\tcase EJsonx::None:\n\tcase EJsonx::Null:\n\t\treturn true;\n\n\tcase EJsonx::String:\n\t\treturn Lhs.AsString() == Rhs.AsString();\n\n\tcase EJsonx::Number:\n\t\treturn Lhs.AsNumber() == Rhs.AsNumber();\n\n\tcase EJsonx::Boolean:\n\t\treturn Lhs.AsBool() == Rhs.AsBool();\n\n\tcase EJsonx::Array:\n\t\t{\n\t\t\tconst TArray< TSharedPtr<FJsonxValue> >& LhsArray = Lhs.AsArray();\n\t\t\tconst TArray< TSharedPtr<FJsonxValue> >& RhsArray = Rhs.AsArray();\n\n\t\t\tif (LhsArray.Num() != RhsArray.Num())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// compare each element\n\t\t\tfor (int32 i = 0; i < LhsArray.Num(); ++i)\n\t\t\t{\n\t\t\t\tif (!CompareEqual(*LhsArray[i], *RhsArray[i]))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\n\tcase EJsonx::Object:\n\t\t{\n\t\t\tconst TSharedPtr<FJsonxObject>& LhsObject = Lhs.AsObject();\n\t\t\tconst TSharedPtr<FJsonxObject>& RhsObject = Rhs.AsObject();\n\n\t\t\tif (LhsObject.IsValid() != RhsObject.IsValid())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (LhsObject.IsValid())\n\t\t\t{\n\t\t\t\tif (LhsObject->Values.Num() != RhsObject->Values.Num())\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// compare each element\n\t\t\t\tfor (const auto& It : LhsObject->Values)\n\t\t\t\t{\n\t\t\t\t\tconst FString& Key = It.Key;\n\t\t\t\t\tconst TSharedPtr<FJsonxValue>* RhsValue = RhsObject->Values.Find(Key);\n\t\t\t\t\tif (RhsValue == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t// not found in both objects\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst TSharedPtr<FJsonxValue>& LhsValue = It.Value;\n\n\t\t\t\t\tif (LhsValue.IsValid() != RhsValue->IsValid())\n\t\t\t\t\t{\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (LhsValue.IsValid())\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!CompareEqual(*LhsValue.Get(), *RhsValue->Get()))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nvoid FJsonxValue::ErrorMessage(const FString& InType) const\n{\n\tUE_LOG(LogJsonx, Error, TEXT(\"Jsonx Value of type '%s' used as a '%s'.\"), *GetType(), *InType);\n}\n"
  },
  {
    "path": "Source/Jsonx/Private/JsonxModule.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"CoreMinimal.h\"\n#include \"JsonxGlobals.h\"\n#include \"Modules/ModuleInterface.h\"\n#include \"Modules/ModuleManager.h\"\n\n\nDEFINE_LOG_CATEGORY(LogJsonx);\n\n\n/**\n * Implements the Jsonx module.\n */\nclass FJsonxModule\n\t: public IModuleInterface\n{\npublic:\n\n\t// IModuleInterface interface\n\n\tvirtual void StartupModule( ) override { }\n\tvirtual void ShutdownModule( ) override { }\n\n\tvirtual bool SupportsDynamicReloading( ) override\n\t{\n\t\treturn false;\n\t}\n};\n\n\nIMPLEMENT_MODULE(FJsonxModule, Jsonx);\n"
  },
  {
    "path": "Source/Jsonx/Private/Tests/JsonxTests.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"CoreMinimal.h\"\n#include \"Misc/AutomationTest.h\"\n#include \"Policies/CondensedJsonxPrintPolicy.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Serialization/JsonxReader.h\"\n#include \"Policies/PrettyJsonxPrintPolicy.h\"\n#include \"Serialization/JsonxSerializer.h\"\n\n#if WITH_DEV_AUTOMATION_TESTS\n\n/**\n * FJsonxAutomationTest\n * Simple unit test that runs Jsonx's in-built test cases\n */\nIMPLEMENT_SIMPLE_AUTOMATION_TEST(FJsonxAutomationTest, \"System.Engine.FileSystem.JSONX\", EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::SmokeFilter )\n\ntypedef TJsonxWriterFactory< TCHAR, TCondensedJsonxPrintPolicy<TCHAR> > FCondensedJsonxStringWriterFactory;\ntypedef TJsonxWriter< TCHAR, TCondensedJsonxPrintPolicy<TCHAR> > FCondensedJsonxStringWriter;\n\ntypedef TJsonxWriterFactory< TCHAR, TPrettyJsonxPrintPolicy<TCHAR> > FPrettyJsonxStringWriterFactory;\ntypedef TJsonxWriter< TCHAR, TPrettyJsonxPrintPolicy<TCHAR> > FPrettyJsonxStringWriter;\n\n/** \n * Execute the Jsonx test cases\n *\n * @return\ttrue if the test was successful, false otherwise\n */\nbool FJsonxAutomationTest::RunTest(const FString& Parameters)\n{\n\t// Null Case\n\t{\n\t\tconst FString InputString = TEXT(\"\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tcheck( FJsonxSerializer::Deserialize( Reader, Object ) == false );\n\t\tcheck( !Object.IsValid() );\n\t}\n\n\t// Empty Object Case\n\t{\n\t\tconst FString InputString = TEXT(\"{}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tcheck( FJsonxSerializer::Deserialize( Reader, Object ) );\n\t\tcheck( Object.IsValid() );\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\t\tcheck( InputString == OutputString );\n\t}\n\n\t// Empty Array Case\n\t{\n\t\tconst FString InputString = TEXT(\"[]\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\t\tcheck( FJsonxSerializer::Deserialize( Reader, Array ) );\n\t\tcheck( Array.Num() == 0 );\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Array, Writer ) );\n\t\tcheck( InputString == OutputString );\n\t}\n\n\t// Simple Array Case\n\t{\n\t\tconst FString InputString = \n\t\t\tTEXT(\"[\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"Value\\\":\\\"Some String\\\"\")\n\t\t\tTEXT(\t\"}\")\n\t\t\tTEXT(\"]\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Array);\n\t\tcheck(bSuccessful);\n\t\tcheck( Array.Num() == 1 );\n\t\tcheck( Array[0].IsValid() );\n\n\t\tTSharedPtr< FJsonxObject > Object = Array[0]->AsObject();\n\t\tcheck( Object.IsValid() );\n\t\tcheck( Object->GetStringField( TEXT(\"Value\") ) == TEXT(\"Some String\") );\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Array, Writer ) );\n\t\tcheck( InputString == OutputString );\n\t}\n\n\t// Object Array Case\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"[\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"Value\\\":\\\"Some String1\\\"\")\n\t\t\tTEXT(\t\"},\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"Value\\\":\\\"Some String2\\\"\")\n\t\t\tTEXT(\t\"},\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"Value\\\":\\\"Some String3\\\"\")\n\t\t\tTEXT(\t\"}\")\n\t\t\tTEXT(\"]\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create(InputString);\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Array);\n\t\tcheck(bSuccessful);\n\t\tcheck(Array.Num() == 3);\n\t\tcheck(Array[0].IsValid());\n\t\tcheck(Array[1].IsValid());\n\t\tcheck(Array[2].IsValid());\n\n\t\tTSharedPtr< FJsonxObject > Object = Array[0]->AsObject();\n\t\tcheck(Object.IsValid());\n\t\tcheck(Object->GetStringField(TEXT(\"Value\")) == TEXT(\"Some String1\"));\n\n\t\tObject = Array[1]->AsObject();\n\t\tcheck(Object.IsValid());\n\t\tcheck(Object->GetStringField(TEXT(\"Value\")) == TEXT(\"Some String2\"));\n\n\t\tObject = Array[2]->AsObject();\n\t\tcheck(Object.IsValid());\n\t\tcheck(Object->GetStringField(TEXT(\"Value\")) == TEXT(\"Some String3\"));\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create(&OutputString);\n\t\tcheck(FJsonxSerializer::Serialize(Array, Writer));\n\t\tcheck(InputString == OutputString);\n\t}\n\n\t// Number Array Case\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"[\")\n\t\t\tTEXT(\"10,\")\n\t\t\tTEXT(\"20,\")\n\t\t\tTEXT(\"30,\")\n\t\t\tTEXT(\"40\")\n\t\t\tTEXT(\"]\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create(InputString);\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Array);\n\t\tcheck(bSuccessful);\n\t\tcheck(Array.Num() == 4);\n\t\tcheck(Array[0].IsValid());\n\t\tcheck(Array[1].IsValid());\n\t\tcheck(Array[2].IsValid());\n\t\tcheck(Array[3].IsValid());\n\n\t\tdouble Number = Array[0]->AsNumber();\n\t\tcheck(Number == 10);\n\n\t\tNumber = Array[1]->AsNumber();\n\t\tcheck(Number == 20);\n\n\t\tNumber = Array[2]->AsNumber();\n\t\tcheck(Number == 30);\n\n\t\tNumber = Array[3]->AsNumber();\n\t\tcheck(Number == 40);\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create(&OutputString);\n\t\tcheck(FJsonxSerializer::Serialize(Array, Writer));\n\t\tcheck(InputString == OutputString);\n\t}\n\n\t// String Array Case\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"[\")\n\t\t\tTEXT(\"\\\"Some String1\\\",\")\n\t\t\tTEXT(\"\\\"Some String2\\\",\")\n\t\t\tTEXT(\"\\\"Some String3\\\",\")\n\t\t\tTEXT(\"\\\"Some String4\\\"\")\n\t\t\tTEXT(\"]\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create(InputString);\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Array);\n\t\tcheck(bSuccessful);\n\t\tcheck(Array.Num() == 4);\n\t\tcheck(Array[0].IsValid());\n\t\tcheck(Array[1].IsValid());\n\t\tcheck(Array[2].IsValid());\n\t\tcheck(Array[3].IsValid());\n\n\t\tFString Text = Array[0]->AsString();\n\t\tcheck(Text == TEXT(\"Some String1\"));\n\n\t\tText = Array[1]->AsString();\n\t\tcheck(Text == TEXT(\"Some String2\"));\n\n\t\tText = Array[2]->AsString();\n\t\tcheck(Text == TEXT(\"Some String3\"));\n\n\t\tText = Array[3]->AsString();\n\t\tcheck(Text == TEXT(\"Some String4\"));\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create(&OutputString);\n\t\tcheck(FJsonxSerializer::Serialize(Array, Writer));\n\t\tcheck(InputString == OutputString);\n\t}\n\n\t// Complex Array Case\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"[\")\n\t\t\tTEXT(\t\"\\\"Some String1\\\",\")\n\t\t\tTEXT(\t\"10,\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"Value\\\":\\\"Some String3\\\"\")\n\t\t\tTEXT(\t\"},\")\n\t\t\tTEXT(\t\"[\")\n\t\t\tTEXT(\t\t\"\\\"Some String4\\\",\")\n\t\t\tTEXT(\t\t\"\\\"Some String5\\\"\")\n\t\t\tTEXT(\t\"],\")\n\t\t\tTEXT(\t\"true,\")\n\t\t\tTEXT(\t\"null\")\n\t\t\tTEXT(\"]\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create(InputString);\n\n\t\tTArray< TSharedPtr<FJsonxValue> > Array;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Array);\n\t\tcheck(bSuccessful);\n\t\tcheck(Array.Num() == 6);\n\t\tcheck(Array[0].IsValid());\n\t\tcheck(Array[1].IsValid());\n\t\tcheck(Array[2].IsValid());\n\t\tcheck(Array[3].IsValid());\n\t\tcheck(Array[4].IsValid());\n\t\tcheck(Array[5].IsValid());\n\n\t\tFString Text = Array[0]->AsString();\n\t\tcheck(Text == TEXT(\"Some String1\"));\n\n\t\tdouble Number = Array[1]->AsNumber();\n\t\tcheck(Number == 10);\n\n\t\tTSharedPtr< FJsonxObject > Object = Array[2]->AsObject();\n\t\tcheck(Object.IsValid());\n\t\tcheck(Object->GetStringField(TEXT(\"Value\")) == TEXT(\"Some String3\"));\n\n\t\tconst TArray<TSharedPtr< FJsonxValue >>& InnerArray = Array[3]->AsArray();\n\t\tcheck(InnerArray.Num() == 2);\n\t\tcheck(Array[0].IsValid());\n\t\tcheck(Array[1].IsValid());\n\n\t\tText = InnerArray[0]->AsString();\n\t\tcheck(Text == TEXT(\"Some String4\"));\n\n\t\tText = InnerArray[1]->AsString();\n\t\tcheck(Text == TEXT(\"Some String5\"));\n\n\t\tbool Boolean = Array[4]->AsBool();\n\t\tcheck(Boolean == true);\n\n\t\tcheck(Array[5]->IsNull() == true);\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create(&OutputString);\n\t\tcheck(FJsonxSerializer::Serialize(Array, Writer));\n\t\tcheck(InputString == OutputString);\n\t}\n\n\t// String Test\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Value\\\":\\\"Some String, Escape Chars: \\\\\\\\, \\\\\\\", \\\\/, \\\\b, \\\\f, \\\\n, \\\\r, \\\\t, \\\\u002B\\\"\")\n\t\t\tTEXT(\"}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Object);\n\t\tcheck(bSuccessful);\n\t\tcheck( Object.IsValid() );\n\n\t\tconst TSharedPtr<FJsonxValue>* Value = Object->Values.Find(TEXT(\"Value\"));\n\t\tcheck(Value && (*Value)->Type == EJsonx::String);\n\t\tconst FString String = (*Value)->AsString();\n\t\tcheck(String == TEXT(\"Some String, Escape Chars: \\\\, \\\", /, \\b, \\f, \\n, \\r, \\t, +\"));\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\n\t\tconst FString TestOutput =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Value\\\":\\\"Some String, Escape Chars: \\\\\\\\, \\\\\\\", /, \\\\b, \\\\f, \\\\n, \\\\r, \\\\t, +\\\"\")\n\t\t\tTEXT(\"}\");\n\t\tcheck(OutputString == TestOutput);\n\t}\n\n\t//// Number Test\n\t//{\n\t//\tconst FString InputString =\n\t//\t\tTEXT(\"{\")\n\t//\t\tTEXT(\t\"\\\"Value1\\\":2.544e+15,\")\n\t//\t\tTEXT(\t\"\\\"Value2\\\":-0.544E-2,\")\n\t//\t\tTEXT(\t\"\\\"Value3\\\":251e3,\")\n\t//\t\tTEXT(\t\"\\\"Value4\\\":-0.0,\")\n\t//\t\tTEXT(\t\"\\\"Value5\\\":843\")\n\t//\t\tTEXT(\"}\");\n\t//\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t//\tTSharedPtr<FJsonxObject> Object;\n\t//\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Object);\n\t//\tcheck(bSuccessful);\n\t//\tcheck( Object.IsValid() );\n\n\t//\tdouble TestValues[] = {2.544e+15, -0.544e-2, 251e3, -0.0, 843};\n\t//\tfor (int32 i = 0; i < 5; ++i)\n\t//\t{\n\t//\t\tconst TSharedPtr<FJsonxValue>* Value = Object->Values.Find(FString::Printf(TEXT(\"Value%i\"), i + 1));\n\t//\t\tcheck(Value && (*Value)->Type == EJsonx::Number);\n\t//\t\tconst double Number = (*Value)->AsNumber();\n\t//\t\tcheck(Number == TestValues[i]);\n\t//\t}\n\n\t//\tFString OutputString;\n\t//\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t//\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\n\t//\t// %g isn't standardized, so we use the same %g format that is used inside PrintJsonx instead of hardcoding the values here\n\t//\tconst FString TestOutput = FString::Printf(\n\t//\t\tTEXT(\"{\")\n\t//\t\tTEXT(\t\"\\\"Value1\\\":%.17g,\")\n\t//\t\tTEXT(\t\"\\\"Value2\\\":%.17g,\")\n\t//\t\tTEXT(\t\"\\\"Value3\\\":%.17g,\")\n\t//\t\tTEXT(\t\"\\\"Value4\\\":%.17g,\")\n\t//\t\tTEXT(\t\"\\\"Value5\\\":%.17g\")\n\t//\t\tTEXT(\"}\"),\n\t//\t\tTestValues[0], TestValues[1], TestValues[2], TestValues[3], TestValues[4]);\n\t//\tcheck(OutputString == TestOutput);\n\t//}\n\n\t// Boolean/Null Test\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Value1\\\":true,\")\n\t\t\tTEXT(\t\"\\\"Value2\\\":true,\")\n\t\t\tTEXT(\t\"\\\"Value3\\\":faLsE,\")\n\t\t\tTEXT(\t\"\\\"Value4\\\":null,\")\n\t\t\tTEXT(\t\"\\\"Value5\\\":NULL\")\n\t\t\tTEXT(\"}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Object);\n\t\tcheck(bSuccessful);\n\t\tcheck( Object.IsValid() );\n\n\t\tbool TestValues[] = {true, true, false};\n\t\tfor (int32 i = 0; i < 5; ++i)\n\t\t{\n\t\t\tconst TSharedPtr<FJsonxValue>* Value = Object->Values.Find(FString::Printf(TEXT(\"Value%i\"), i + 1));\n\t\t\tcheck(Value);\n\t\t\tif (i < 3)\n\t\t\t{\n\t\t\t\tcheck((*Value)->Type == EJsonx::Boolean);\n\t\t\t\tconst bool Bool = (*Value)->AsBool();\n\t\t\t\tcheck(Bool == TestValues[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcheck((*Value)->Type == EJsonx::Null);\n\t\t\t\tcheck((*Value)->IsNull());\n\t\t\t}\n\t\t}\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\n\t\tconst FString TestOutput =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Value1\\\":true,\")\n\t\t\tTEXT(\t\"\\\"Value2\\\":true,\")\n\t\t\tTEXT(\t\"\\\"Value3\\\":false,\")\n\t\t\tTEXT(\t\"\\\"Value4\\\":null,\")\n\t\t\tTEXT(\t\"\\\"Value5\\\":null\")\n\t\t\tTEXT(\"}\");\n\t\tcheck(OutputString == TestOutput);\n\t}\n\n\t// Object Test && extra whitespace test\n\t{\n\t\tconst FString InputStringWithExtraWhitespace =\n\t\t\tTEXT(\"\t\t\\n\\r\\n\t   {\")\n\t\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\")\n\t\t\tTEXT(\t\t\"\\\"NestedObject\\\":{}\")\n\t\t\tTEXT(\t\"},\")\n\t\t\tTEXT(\t\"\\\"Value\\\":true\")\n\t\t\tTEXT(\"}\t\t\\n\\r\\n\t   \");\n\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\t\tTEXT(\t\"{\")\n\t\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\")\n\t\t\tTEXT(\t\t\"\\\"NestedObject\\\":{}\")\n\t\t\tTEXT(\t\"},\")\n\t\t\tTEXT(\t\"\\\"Value\\\":true\")\n\t\t\tTEXT(\"}\");\n\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputStringWithExtraWhitespace );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Object);\n\t\tcheck(bSuccessful);\n\t\tcheck( Object.IsValid() );\n\n\t\tconst TSharedPtr<FJsonxValue>* InnerValueFail = Object->Values.Find(TEXT(\"InnerValue\"));\n\t\tcheck(!InnerValueFail);\n\n\t\tconst TSharedPtr<FJsonxValue>* ObjectValue = Object->Values.Find(TEXT(\"Object\"));\n\t\tcheck(ObjectValue && (*ObjectValue)->Type == EJsonx::Object);\n\t\tconst TSharedPtr<FJsonxObject> InnerObject = (*ObjectValue)->AsObject();\n\t\tcheck(InnerObject.IsValid());\n\n\t\t{\n\t\t\tconst TSharedPtr<FJsonxValue>* NestedValueValue = InnerObject->Values.Find(TEXT(\"NestedValue\"));\n\t\t\tcheck(NestedValueValue && (*NestedValueValue)->Type == EJsonx::Null);\n\t\t\tcheck((*NestedValueValue)->IsNull());\n\n\t\t\tconst TSharedPtr<FJsonxValue>* NestedObjectValue = InnerObject->Values.Find(TEXT(\"NestedObject\"));\n\t\t\tcheck(NestedObjectValue && (*NestedObjectValue)->Type == EJsonx::Object);\n\t\t\tconst TSharedPtr<FJsonxObject> InnerInnerObject = (*NestedObjectValue)->AsObject();\n\t\t\tcheck(InnerInnerObject.IsValid());\n\n\t\t\t{\n\t\t\t\tconst TSharedPtr<FJsonxValue>* NestedValueValueFail = InnerInnerObject->Values.Find(TEXT(\"NestedValue\"));\n\t\t\t\tcheck(!NestedValueValueFail);\n\t\t\t}\n\t\t}\n\n\t\tconst TSharedPtr<FJsonxValue>* ValueValue = Object->Values.Find(TEXT(\"Value\"));\n\t\tcheck(ValueValue && (*ValueValue)->Type == EJsonx::Boolean);\n\t\tconst bool Bool = (*ValueValue)->AsBool();\n\t\tcheck(Bool);\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\t\tcheck(OutputString == InputString);\n\t}\n\n\t// Array Test\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\n\t\t\tTEXT(\t\"\\\"Array\\\":\")\n\t\t\tTEXT(\t\"[\")\n\t\t\tTEXT(\t\t\"[],\")\n\t\t\tTEXT(\t\t\"\\\"Some String\\\",\")\n\t\t\tTEXT(\t\t\"\\\"Another String\\\",\")\n\t\t\tTEXT(\t\t\"null,\")\n\t\t\tTEXT(\t\t\"true,\")\n\t\t\tTEXT(\t\t\"false,\")\n\t\t\tTEXT(\t\t\"45,\")\n\t\t\tTEXT(\t\t\"{}\")\n\t\t\tTEXT(\t\"]\")\n\t\t\tTEXT(\"}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tbool bSuccessful = FJsonxSerializer::Deserialize(Reader, Object);\n\t\tcheck(bSuccessful);\n\t\tcheck( Object.IsValid() );\n\n\t\tconst TSharedPtr<FJsonxValue>* InnerValueFail = Object->Values.Find(TEXT(\"InnerValue\"));\n\t\tcheck(!InnerValueFail);\n\n\t\tconst TSharedPtr<FJsonxValue>* ArrayValue = Object->Values.Find(TEXT(\"Array\"));\n\t\tcheck(ArrayValue && (*ArrayValue)->Type == EJsonx::Array);\n\t\tconst TArray< TSharedPtr<FJsonxValue> > Array = (*ArrayValue)->AsArray();\n\t\tcheck(Array.Num() == 8);\n\n\t\tEJsonx ValueTypes[] = {EJsonx::Array, EJsonx::String, EJsonx::String, EJsonx::Null,\n\t\t\tEJsonx::Boolean, EJsonx::Boolean, EJsonx::Number, EJsonx::Object};\n\t\tfor (int32 i = 0; i < Array.Num(); ++i)\n\t\t{\n\t\t\tconst TSharedPtr<FJsonxValue>& Value = Array[i];\n\t\t\tcheck(Value.IsValid());\n\t\t\tcheck(Value->Type == ValueTypes[i]);\n\t\t}\n\n\t\tconst TArray< TSharedPtr<FJsonxValue> >& InnerArray = Array[0]->AsArray();\n\t\tcheck(InnerArray.Num() == 0);\n\t\tcheck(Array[1]->AsString() == TEXT(\"Some String\"));\n\t\tcheck(Array[2]->AsString() == TEXT(\"Another String\"));\n\t\tcheck(Array[3]->IsNull());\n\t\tcheck(Array[4]->AsBool());\n\t\tcheck(!Array[5]->AsBool());\n\t\tcheck(FMath::Abs(Array[6]->AsNumber() - 45.f) < KINDA_SMALL_NUMBER);\n\t\tconst TSharedPtr<FJsonxObject> InnerObject = Array[7]->AsObject();\n\t\tcheck(InnerObject.IsValid());\n\n\t\tFString OutputString;\n\t\tTSharedRef< FCondensedJsonxStringWriter > Writer = FCondensedJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\t\tcheck(OutputString == InputString);\n\t}\n\n\t// Pretty Print Test\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Data1\\\": \\\"value\\\",\")\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Data2\\\": \\\"value\\\",\")\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Array\\\": [\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t{\")\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t\t\\\"InnerData1\\\": \\\"value\\\"\")\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t},\")\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t[],\")\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t[ 1, 2, 3, 4 ],\")\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t{\")\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t},\")\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t\\\"value\\\",\")\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t\\\"value\\\"\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t],\")\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Object\\\":\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t{\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t}\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tcheck( FJsonxSerializer::Deserialize( Reader, Object ) );\n\t\tcheck( Object.IsValid() );\n\n\t\tFString OutputString;\n\t\tTSharedRef< FPrettyJsonxStringWriter > Writer = FPrettyJsonxStringWriterFactory::Create( &OutputString );\n\t\tcheck( FJsonxSerializer::Serialize( Object.ToSharedRef(), Writer ) );\n\t\tcheck(OutputString == InputString);\n\t}\n\t  \n\t// Line and Character # test\n\t{\n\t\tconst FString InputString =\n\t\t\tTEXT(\"{\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Data1\\\": \\\"value\\\",\")\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Array\\\":\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t[\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\t12345,\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\tTrue\")\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t],\")\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t\\\"Object\\\":\")\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t{\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"\t}\")\t\t\t\t\t\t\t\t\tLINE_TERMINATOR\n\t\t\tTEXT(\"}\");\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( InputString );\n\n\t\tEJsonxNotation Notation = EJsonxNotation::Null;\n\t\tcheck( Reader->ReadNext( Notation ) && Notation == EJsonxNotation::ObjectStart );\n\t\tcheck( Reader->GetLineNumber() == 1 && Reader->GetCharacterNumber() == 1 );\n\n\t\tcheck( Reader->ReadNext( Notation ) && Notation == EJsonxNotation::String );\n\t\tcheck( Reader->GetLineNumber() == 2 && Reader->GetCharacterNumber() == 17 );\n\n\t\tcheck( Reader->ReadNext( Notation ) && Notation == EJsonxNotation::ArrayStart );\n\t\tcheck( Reader->GetLineNumber() == 4 && Reader->GetCharacterNumber() == 2 );\n\n\t\tcheck( Reader->ReadNext( Notation ) && Notation == EJsonxNotation::Number );\n\t\tcheck( Reader->GetLineNumber() == 5 && Reader->GetCharacterNumber() == 7 );\n\n\t\tcheck( Reader->ReadNext( Notation ) && Notation == EJsonxNotation::Boolean );\n\t\tcheck( Reader->GetLineNumber() == 6 && Reader->GetCharacterNumber() == 6 );\n\t}\n\n\t// Failure Cases\n\tTArray<FString> FailureInputs;\n\n\t// Unclosed Object\n\tFailureInputs.Add(\n\t\tTEXT(\"{\"));\n\n\t// Values in Object without identifiers\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Value1\\\",\")\n\t\tTEXT(\t\"\\\"Value2\\\",\")\n\t\tTEXT(\t\"43\")\n\t\tTEXT(\"}\"));\n\n\t// Unexpected End Of Input Found\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\tTEXT(\t\"{\")\n\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\"));\n\n\t// Missing first brace\n\tFailureInputs.Add(\n\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\tTEXT(\t\t\"{\")\n\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\")\n\t\tTEXT(\t\t\"\\\"NestedObject\\\":{}\")\n\t\tTEXT(\t\"},\")\n\t\tTEXT(\t\"\\\"Value\\\":true\")\n\t\tTEXT(\"}\"));\n\n\t// Missing last character\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\tTEXT(\t\"{\")\n\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\")\n\t\tTEXT(\t\t\"\\\"NestedObject\\\":{}\")\n\t\tTEXT(\t\"},\")\n\t\tTEXT(\t\"\\\"Value\\\":true\"));\n\n\t// Missing curly brace\n\tFailureInputs.Add(TEXT(\"}\"));\n\n\t// Missing bracket\n\tFailureInputs.Add(TEXT(\"]\"));\n\n\t// Extra last character\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Object\\\":\")\n\t\tTEXT(\t\"{\")\n\t\tTEXT(\t\t\"\\\"NestedValue\\\":null,\")\n\t\tTEXT(\t\t\"\\\"NestedObject\\\":{}\")\n\t\tTEXT(\t\"},\")\n\t\tTEXT(\t\"\\\"Value\\\":true\")\n\t\tTEXT(\"}0\"));\n\n\t// Missing comma\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Value1\\\":null,\")\n\t\tTEXT(\t\"\\\"Value2\\\":\\\"string\\\"\")\n\t\tTEXT(\t\"\\\"Value3\\\":65.3\")\n\t\tTEXT(\"}\"));\n\n\t// Extra comma\n\tFailureInputs.Add(\n\t\tTEXT(\"{\")\n\t\tTEXT(\t\"\\\"Value1\\\":null,\")\n\t\tTEXT(\t\"\\\"Value2\\\":\\\"string\\\",\")\n\t\tTEXT(\t\"\\\"Value3\\\":65.3,\")\n\t\tTEXT(\"}\"));\n\n\t// Badly formed true/false/null\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":tru}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":full}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":nulle}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":n%ll}\"));\n\n\t// Floating Point Failures\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":65.3e}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":65.}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":.7}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":+6}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":01}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":00.56}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":-1.e+4}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":2e+}\"));\n\n\t// Bad Escape Characters\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":\\\"Hello\\\\xThere\\\"}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":\\\"Hello\\\\u123There\\\"}\"));\n\tFailureInputs.Add(TEXT(\"{\\\"Value\\\":\\\"Hello\\\\RThere\\\"}\"));\n\n\tfor (int32 i = 0; i < FailureInputs.Num(); ++i)\n\t{\n\t\tTSharedRef< TJsonxReader<> > Reader = TJsonxReaderFactory<>::Create( FailureInputs[i] );\n\n\t\tTSharedPtr<FJsonxObject> Object;\n\t\tcheck( FJsonxSerializer::Deserialize( Reader, Object ) == false );\n\t\tcheck( !Object.IsValid() );\n\t}\n\n\t// TryGetNumber tests\n\t{\n\t\tauto JsonxNumberToInt64 = [](double Val, int64& OutVal) -> bool\n\t\t{\n\t\t\tFJsonxValueNumber JsonxVal(Val);\n\t\t\treturn ((FJsonxValue&)JsonxVal).TryGetNumber(OutVal);\n\t\t};\n\n\t\tauto JsonxNumberToInt32 = [](double Val, int32& OutVal) -> bool\n\t\t{\n\t\t\tFJsonxValueNumber JsonxVal(Val);\n\t\t\treturn ((FJsonxValue&)JsonxVal).TryGetNumber(OutVal);\n\t\t};\n\n\t\tauto JsonxNumberToUInt32 = [](double Val, uint32& OutVal) -> bool\n\t\t{\n\t\t\tFJsonxValueNumber JsonxVal(Val);\n\t\t\treturn ((FJsonxValue&)JsonxVal).TryGetNumber(OutVal);\n\t\t};\n\t\t\n\t\t// TryGetNumber-Int64 tests\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(9007199254740991.0, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Big Float64 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Big Float64\"), IntVal, 9007199254740991LL);\n\t\t}\n\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(-9007199254740991.0, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Small Float64 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Small Float64\"), IntVal, -9007199254740991LL);\n\t\t}\n\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(0.4999999999999997, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Lesser than near half succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Lesser than near half rounds to zero\"), IntVal, 0LL);\n\t\t}\n\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(-0.4999999999999997, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Greater than near negative half succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Greater than near negative half rounds to zero\"), IntVal, 0LL);\n\t\t}\n\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(0.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Half rounds to next integer succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Half rounds to next integer\"), IntVal, 1LL);\n\t\t}\n\n\t\t{\n\t\t\tint64 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt64(-0.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int64 Negative half rounds to next negative integer succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int64 Negative half rounds to next negative integer succeeds\"), IntVal, -1LL);\n\t\t}\n\n\t\t// TryGetNumber-Int32 tests\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(2147483647.000001, IntVal);\n\t\t\tTestFalse(TEXT(\"TryGetNumber-Int32 Number greater than max Int32 fails\"), bOk);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(-2147483648.000001, IntVal);\n\t\t\tTestFalse(TEXT(\"TryGetNumber-Int32 Number lesser than min Int32 fails\"), bOk);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(2147483647.0, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Max Int32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Max Int32\"), IntVal, INT_MAX);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(2147483646.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Round up to max Int32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Round up to max Int32\"), IntVal, INT_MAX);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(-2147483648.0, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Min Int32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Min Int32\"), IntVal, INT_MIN);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(-2147483647.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Round down to min Int32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Round down to min Int32\"), IntVal, INT_MIN);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(0.4999999999999997, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Lesser than near half succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Lesser than near half rounds to zero\"), IntVal, 0);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(-0.4999999999999997, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Greater than near negative half succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Greater than near negative half rounds to zero\"), IntVal, 0);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(0.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Half rounds to next integer succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Half rounds to next integer\"), IntVal, 1);\n\t\t}\n\n\t\t{\n\t\t\tint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToInt32(-0.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-Int32 Negative half rounds to next negative integer succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-Int32 Negative half rounds to next negative integer succeeds\"), IntVal, -1);\n\t\t}\n\n\t\t// TryGetNumber-UInt32 tests\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(4294967295.000001, IntVal);\n\t\t\tTestFalse(TEXT(\"TryGetNumber-UInt32 Number greater than max Uint32 fails\"), bOk);\n\t\t}\n\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(-0.000000000000001, IntVal);\n\t\t\tTestFalse(TEXT(\"TryGetNumber-UInt32 Negative number fails\"), bOk);\n\t\t}\n\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(4294967295.0, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-UInt32 Max UInt32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-UInt32  Max UInt32\"), IntVal, UINT_MAX);\n\t\t}\n\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(4294967294.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-UInt32 Round up to max UInt32 succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-UInt32 Round up to max UInt32\"), IntVal, UINT_MAX);\n\t\t}\n\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(0.4999999999999997, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-UInt32 Lesser than near half succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-UInt32 Lesser than near half rounds to zero\"), IntVal, 0U);\n\t\t}\n\n\t\t{\n\t\t\tuint32 IntVal;\n\t\t\tbool bOk = JsonxNumberToUInt32(0.5, IntVal);\n\t\t\tTestTrue(TEXT(\"TryGetNumber-UInt32 Half rounds to next integer succeeds\"), bOk);\n\t\t\tTestEqual(TEXT(\"TryGetNumber-UInt32 Half rounds to next integer\"), IntVal, 1U);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n#endif //WITH_DEV_AUTOMATION_TESTS\n"
  },
  {
    "path": "Source/Jsonx/Public/Dom/JsonxObject.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"JsonxGlobals.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Dom/JsonxValue.h\"\n\n/**\n * A Jsonx Object is a structure holding an unordered set of name/value pairs.\n * In a Jsonx file, it is represented by everything between curly braces {}.\n */\nclass JSONX_API FJsonxObject\n{\npublic:\n\n\tTMap<FString, TSharedPtr<FJsonxValue>> Values;\n\n\ttemplate<EJsonx JsonxType>\n\tTSharedPtr<FJsonxValue> GetField( const FString& FieldName ) const\n\t{\n\t\tconst TSharedPtr<FJsonxValue>* Field = Values.Find(FieldName);\n\t\tif ( Field != nullptr && Field->IsValid() )\n\t\t{\n\t\t\tif (JsonxType == EJsonx::None || (*Field)->Type == JsonxType)\n\t\t\t{\n\t\t\t\treturn (*Field);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"Field %s is of the wrong type.\"), *FieldName);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"Field %s was not found.\"), *FieldName);\n\t\t}\n\n\t\treturn MakeShared<FJsonxValueNull>();\n\t}\n\n\t/**\n\t * Attempts to get the field with the specified name.\n\t *\n\t * @param FieldName The name of the field to get.\n\t * @return A pointer to the field, or nullptr if the field doesn't exist.\n\t */\n\tTSharedPtr<FJsonxValue> TryGetField( const FString& FieldName ) const\n\t{\n\t\tconst TSharedPtr<FJsonxValue>* Field = Values.Find(FieldName);\n\t\treturn (Field != nullptr && Field->IsValid()) ? *Field : TSharedPtr<FJsonxValue>();\n\t}\n\n\t/**\n\t * Checks whether a field with the specified name exists in the object.\n\t *\n\t * @param FieldName The name of the field to check.\n\t * @return true if the field exists, false otherwise.\n\t */\n\tbool HasField( const FString& FieldName) const\n\t{\n\t\tconst TSharedPtr<FJsonxValue>* Field = Values.Find(FieldName);\n\t\tif(Field && Field->IsValid())\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * Checks whether a field with the specified name and type exists in the object.\n\t *\n\t * @param JsonxType The type of the field to check.\n\t * @param FieldName The name of the field to check.\n\t * @return true if the field exists, false otherwise.\n\t */\n\ttemplate<EJsonx JsonxType>\n\tbool HasTypedField(const FString& FieldName) const\n\t{\n\t\tconst TSharedPtr<FJsonxValue>* Field = Values.Find(FieldName);\n\t\tif(Field && Field->IsValid() && ((*Field)->Type == JsonxType))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sets the value of the field with the specified name.\n\t *\n\t * @param FieldName The name of the field to set.\n\t * @param Value The value to set.\n\t */\n\tvoid SetField( const FString& FieldName, const TSharedPtr<FJsonxValue>& Value );\n\n\t/**\n\t * Removes the field with the specified name.\n\t *\n\t * @param FieldName The name of the field to remove.\n\t */\n\tvoid RemoveField(const FString& FieldName);\n\n\t/**\n\t * Gets the field with the specified name as a number.\n\t *\n\t * Ensures that the field is present and is of type Jsonx number.\n\t *\n\t * @param FieldName The name of the field to get.\n\t * @return The field's value as a number.\n\t */\n\tdouble GetNumberField(const FString& FieldName) const;\n\n\t/**\n\t * Gets a numeric field and casts to an int32\n\t */\n\tFORCEINLINE int32 GetIntegerField(const FString& FieldName) const\n\t{\n\t\treturn (int32)GetNumberField(FieldName);\n\t}\n\n\t/** Get the field named FieldName as a number. Returns false if it doesn't exist or cannot be converted. */\n\tbool TryGetNumberField(const FString& FieldName, double& OutNumber) const;\n\n\t/** Get the field named FieldName as a number, and makes sure it's within int32 range. Returns false if it doesn't exist or cannot be converted. */\n\tbool TryGetNumberField(const FString& FieldName, int32& OutNumber) const;\n\n\t/** Get the field named FieldName as a number, and makes sure it's within uint32 range. Returns false if it doesn't exist or cannot be converted.  */\n\tbool TryGetNumberField(const FString& FieldName, uint32& OutNumber) const;\n\n\t/** Get the field named FieldName as a number. Returns false if it doesn't exist or cannot be converted. */\n\tbool TryGetNumberField(const FString& FieldName, int64& OutNumber) const;\n\n\t/** Add a field named FieldName with Number as value */\n\tvoid SetNumberField( const FString& FieldName, double Number );\n\n\t/** Get the field named FieldName as a string. */\n\tFString GetStringField(const FString& FieldName) const;\n\n\t/** Get the field named FieldName as a string. Returns false if it doesn't exist or cannot be converted. */\n\tbool TryGetStringField(const FString& FieldName, FString& OutString) const;\n\n\t/** Get the field named FieldName as an array of strings. Returns false if it doesn't exist or any member cannot be converted. */\n\tbool TryGetStringArrayField(const FString& FieldName, TArray<FString>& OutArray) const;\n\n\t/** Get the field named FieldName as an array of enums. Returns false if it doesn't exist or any member is not a string. */\n\ttemplate<typename TEnum>\n\tbool TryGetEnumArrayField(const FString& FieldName, TArray<TEnum>& OutArray) const\n\t{\n\t\tTArray<FString> Strings;\n\t\tif (!TryGetStringArrayField(FieldName, Strings))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tOutArray.Empty();\n\t\tfor (const FString& String : Strings)\n\t\t{\n\t\t\tTEnum Value;\n\t\t\tif (LexTryParseString(Value, *String))\n\t\t\t{\n\t\t\t\tOutArray.Add(Value);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** Add a field named FieldName with value of StringValue */\n\tvoid SetStringField( const FString& FieldName, const FString& StringValue );\n\n\t/**\n\t * Gets the field with the specified name as a boolean.\n\t *\n\t * Ensures that the field is present and is of type Jsonx number.\n\t *\n\t * @param FieldName The name of the field to get.\n\t * @return The field's value as a boolean.\n\t */\n\tbool GetBoolField(const FString& FieldName) const;\n\n\t/** Get the field named FieldName as a string. Returns false if it doesn't exist or cannot be converted. */\n\tbool TryGetBoolField(const FString& FieldName, bool& OutBool) const;\n\n\t/** Set a boolean field named FieldName and value of InValue */\n\tvoid SetBoolField( const FString& FieldName, bool InValue );\n\n\t/** Get the field named FieldName as an array. */\n\tconst TArray< TSharedPtr<FJsonxValue> >& GetArrayField(const FString& FieldName) const;\n\n\t/** Try to get the field named FieldName as an array, or return false if it's another type */\n\tbool TryGetArrayField(const FString& FieldName, const TArray< TSharedPtr<FJsonxValue> >*& OutArray) const;\n\n\t/** Set an array field named FieldName and value of Array */\n\tvoid SetArrayField( const FString& FieldName, const TArray< TSharedPtr<FJsonxValue> >& Array );\n\n\t/**\n\t * Gets the field with the specified name as a Jsonx object.\n\t *\n\t * Ensures that the field is present and is of type Jsonx object.\n\t *\n\t * @param FieldName The name of the field to get.\n\t * @return The field's value as a Jsonx object.\n\t */\n\tconst TSharedPtr<FJsonxObject>& GetObjectField(const FString& FieldName) const;\n\n\t/** Try to get the field named FieldName as an object, or return false if it's another type */\n\tbool TryGetObjectField(const FString& FieldName, const TSharedPtr<FJsonxObject>*& OutObject) const;\n\n\t/** Set an ObjectField named FieldName and value of JsonxObject */\n\tvoid SetObjectField( const FString& FieldName, const TSharedPtr<FJsonxObject>& JsonxObject );\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Dom/JsonxValue.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Serialization/JsonxTypes.h\"\n\nclass FJsonxObject;\n\n/**\n * A Jsonx Value is a structure that can be any of the Jsonx Types.\n * It should never be used on its, only its derived types should be used.\n */\nclass JSONX_API FJsonxValue\n{\npublic:\n\n\t/** Returns this value as a double, logging an error and returning zero if this is not an Jsonx Number */\n\tdouble AsNumber() const;\n\n\t/** Returns this value as a string, logging an error and returning an empty string if not possible */\n\tFString AsString() const;\n\n\t/** Returns this value as a boolean, logging an error and returning false if not possible */\n\tbool AsBool() const;\n\n\t/** Returns this value as an array, logging an error and returning an empty array reference if not possible */\n\tconst TArray< TSharedPtr<FJsonxValue> >& AsArray() const;\n\n\t/** Returns this value as an object, throwing an error if this is not an Jsonx Object */\n\tvirtual const TSharedPtr<FJsonxObject>& AsObject() const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(double& OutNumber) const { return false; }\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(float& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(int8& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(int16& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(int32& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(int64& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(uint8& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(uint16& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(uint32& OutNumber) const;\n\n\t/** Tries to convert this value to a number, returning false if not possible */\n\tvirtual bool TryGetNumber(uint64& OutNumber) const;\n\n\t/** Tries to convert this value to a string, returning false if not possible */\n\tvirtual bool TryGetString(FString& OutString) const { return false; }\n\n\t/** Tries to convert this value to a bool, returning false if not possible */\n\tvirtual bool TryGetBool(bool& OutBool) const { return false; }\n\n\t/** Tries to convert this value to an array, returning false if not possible */\n\tvirtual bool TryGetArray(const TArray< TSharedPtr<FJsonxValue> >*& OutArray) const { return false; }\n\n\t/** Tries to convert this value to an object, returning false if not possible */\n\tvirtual bool TryGetObject(const TSharedPtr<FJsonxObject>*& Object) const { return false; }\n\n\t/** Returns true if this value is a 'null' */\n\tbool IsNull() const { return Type == EJsonx::Null || Type == EJsonx::None; }\n\n\t/** Get a field of the same type as the argument */\n\tvoid AsArgumentType(double                          & Value) { Value = AsNumber(); }\n\tvoid AsArgumentType(FString                         & Value) { Value = AsString(); }\n\tvoid AsArgumentType(bool                            & Value) { Value = AsBool  (); }\n\tvoid AsArgumentType(TArray< TSharedPtr<FJsonxValue> >& Value) { Value = AsArray (); }\n\tvoid AsArgumentType(TSharedPtr<FJsonxObject>         & Value) { Value = AsObject(); }\n\n\tEJsonx Type;\n\n\tstatic bool CompareEqual(const FJsonxValue& Lhs, const FJsonxValue& Rhs);\n\nprotected:\n\n\tFJsonxValue() : Type(EJsonx::None) {}\n\tvirtual ~FJsonxValue() {}\n\n\tvirtual FString GetType() const = 0;\n\n\tvoid ErrorMessage(const FString& InType) const;\n};\n\ninline bool operator==(const FJsonxValue& Lhs, const FJsonxValue& Rhs)\n{\n\treturn FJsonxValue::CompareEqual(Lhs, Rhs);\n}\n\ninline bool operator!=(const FJsonxValue& Lhs, const FJsonxValue& Rhs)\n{\n\treturn !FJsonxValue::CompareEqual(Lhs, Rhs);\n}\n\n\n/** A Jsonx String Value. */\nclass JSONX_API FJsonxValueString : public FJsonxValue\n{\npublic:\n\tFJsonxValueString(const FString& InString) : Value(InString) {Type = EJsonx::String;}\n\n\tvirtual bool TryGetString(FString& OutString) const override\t{ OutString = Value; return true; }\n\tvirtual bool TryGetNumber(double& OutDouble) const override\t\t{ if (Value.IsNumeric()) { OutDouble = FCString::Atod(*Value); return true; } else { return false; } }\n\tvirtual bool TryGetNumber(int32& OutValue) const override\t\t{ LexFromString(OutValue, *Value); return true; }\n\tvirtual bool TryGetNumber(uint32& OutValue) const override\t\t{ LexFromString(OutValue, *Value); return true; }\n\tvirtual bool TryGetNumber(int64& OutValue) const override\t\t{ LexFromString(OutValue, *Value); return true; }\n\tvirtual bool TryGetNumber(uint64& OutValue) const override\t\t{ LexFromString(OutValue, *Value); return true; }\n\tvirtual bool TryGetBool(bool& OutBool) const override\t\t\t{ OutBool = Value.ToBool(); return true; }\n\n\t// Way to check if string value is empty without copying the string \n\tbool IsEmpty() const { return Value.IsEmpty(); }\n\nprotected:\n\tFString Value;\n\n\tvirtual FString GetType() const override {return TEXT(\"String\");}\n};\n\n\n/** A Jsonx Number Value. */\nclass JSONX_API FJsonxValueNumber : public FJsonxValue\n{\npublic:\n\tFJsonxValueNumber(double InNumber) : Value(InNumber) {Type = EJsonx::Number;}\n\tvirtual bool TryGetNumber(double& OutNumber) const override\t\t{ OutNumber = Value; return true; }\n\tvirtual bool TryGetBool(bool& OutBool) const override\t\t\t{ OutBool = (Value != 0.0); return true; }\n\tvirtual bool TryGetString(FString& OutString) const override\t{ OutString = FString::SanitizeFloat(Value, 0); return true; }\n\t\nprotected:\n\n\tdouble Value;\n\n\tvirtual FString GetType() const override {return TEXT(\"Number\");}\n};\n\n\n/** A Jsonx Number Value, stored internally as a string so as not to lose precision */\nclass JSONX_API FJsonxValueNumberString : public FJsonxValue\n{\npublic:\n\tFJsonxValueNumberString(const FString& InString) : Value(InString) { Type = EJsonx::Number; }\n\n\tvirtual bool TryGetString(FString& OutString) const override { OutString = Value; return true; }\n\tvirtual bool TryGetNumber(double& OutDouble) const override { return LexTryParseString(OutDouble, *Value); }\n\tvirtual bool TryGetNumber(float &OutDouble) const override { return LexTryParseString(OutDouble, *Value); }\n\tvirtual bool TryGetNumber(int8& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(int16& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(int32& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(int64& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(uint8& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(uint16& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(uint32& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetNumber(uint64& OutValue) const override { return LexTryParseString(OutValue, *Value); }\n\tvirtual bool TryGetBool(bool& OutBool) const override { OutBool = Value.ToBool(); return true; }\n\nprotected:\n\tFString Value;\n\n\tvirtual FString GetType() const override { return TEXT(\"NumberString\"); }\n};\n\n\n/** A Jsonx Boolean Value. */\nclass JSONX_API FJsonxValueBoolean : public FJsonxValue\n{\npublic:\n\tFJsonxValueBoolean(bool InBool) : Value(InBool) {Type = EJsonx::Boolean;}\n\tvirtual bool TryGetNumber(double& OutNumber) const override\t\t{ OutNumber = Value ? 1 : 0; return true; }\n\tvirtual bool TryGetBool(bool& OutBool) const override\t\t\t{ OutBool = Value; return true; }\n\tvirtual bool TryGetString(FString& OutString) const override\t{ OutString = Value ? TEXT(\"true\") : TEXT(\"false\"); return true; }\n\t\nprotected:\n\tbool Value;\n\n\tvirtual FString GetType() const override {return TEXT(\"Boolean\");}\n};\n\n\n/** A Jsonx Array Value. */\nclass JSONX_API FJsonxValueArray : public FJsonxValue\n{\npublic:\n\tFJsonxValueArray(const TArray< TSharedPtr<FJsonxValue> >& InArray) : Value(InArray) {Type = EJsonx::Array;}\n\tvirtual bool TryGetArray(const TArray< TSharedPtr<FJsonxValue> >*& OutArray) const override\t{ OutArray = &Value; return true; }\n\t\nprotected:\n\tTArray< TSharedPtr<FJsonxValue> > Value;\n\n\tvirtual FString GetType() const override {return TEXT(\"Array\");}\n};\n\n\n/** A Jsonx Object Value. */\nclass JSONX_API FJsonxValueObject : public FJsonxValue\n{\npublic:\n\tFJsonxValueObject(TSharedPtr<FJsonxObject> InObject) : Value(InObject) {Type = EJsonx::Object;}\n\tvirtual bool TryGetObject(const TSharedPtr<FJsonxObject>*& OutObject) const override\t\t\t{ OutObject = &Value; return true; }\n\t\nprotected:\n\tTSharedPtr<FJsonxObject> Value;\n\n\tvirtual FString GetType() const override {return TEXT(\"Object\");}\n};\n\n\n/** A Jsonx Null Value. */\nclass JSONX_API FJsonxValueNull : public FJsonxValue\n{\npublic:\n\tFJsonxValueNull() {Type = EJsonx::Null;}\n\nprotected:\n\tvirtual FString GetType() const override {return TEXT(\"Null\");}\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Jsonx.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n\n/* Boilerplate\n *****************************************************************************/\n\n#include \"Misc/MonolithicHeaderBoilerplate.h\"\nMONOLITHIC_HEADER_BOILERPLATE()\n\n\n/* Public dependencies\n *****************************************************************************/\n\n#include \"Core.h\"\n\n\n/* Public includes\n *****************************************************************************/\n\n#include \"JsonxGlobals.h\"\n\n#include \"Policies/JsonxPrintPolicy.h\"\n#include \"Policies/PrettyJsonxPrintPolicy.h\"\n#include \"Policies/CondensedJsonxPrintPolicy.h\"\n\n#include \"Serialization/JsonxTypes.h\"\n#include \"Dom/JsonxValue.h\"\n#include \"Dom/JsonxObject.h\"\n\n#include \"Serialization/JsonxReader.h\"\n#include \"Serialization/JsonxWriter.h\"\n#include \"Serialization/JsonxSerializer.h\"\n#include \"Serialization/JsonxSerializerMacros.h\"\n"
  },
  {
    "path": "Source/Jsonx/Public/JsonxGlobals.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n\nJSONX_API DECLARE_LOG_CATEGORY_EXTERN(LogJsonx, Log, All);\n"
  },
  {
    "path": "Source/Jsonx/Public/JsonxUtils/JsonxObjectArrayUpdater.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Dom/JsonxObject.h\"\n#include \"Dom/JsonxValue.h\"\n\n/**\n * Utility to update an array of json objects from an array of elements (of arbitrary type).\n * Elements in the source array and the destination json object array are matched based on an\n * arbitrary key (provided by the FGetElementKey and FTryGetJsonxObjectKey delegates respectively).\n * Existing elements get \"updated\" via the FUpdateJsonxObject delegate. The update scheme is entirely \n * customizable; for example, it can be non-destructive and leave some json fields unchanged.\n * Elements from the source array that are not in the json array (based on the \"key\") are added to it.\n * Elements that are not present in the source array (based on the \"key\") are removed from the json array.\n * If the source array is empty the json array field is removed.\n */\ntemplate <typename ElementType, typename KeyType>\nstruct FJsonxObjectArrayUpdater\n{\n\tDECLARE_DELEGATE_RetVal_OneParam(KeyType, FGetElementKey, const ElementType&);\n\n\tDECLARE_DELEGATE_RetVal_TwoParams(bool, FTryGetJsonxObjectKey, const FJsonxObject&, KeyType& /*OutKey*/);\n\n\tDECLARE_DELEGATE_TwoParams(FUpdateJsonxObject, const ElementType&, FJsonxObject&);\n\n\tstatic void Execute(FJsonxObject& JsonxObject, const FString& ArrayName, const TArray<ElementType>& SourceArray, FGetElementKey GetElementKey, FTryGetJsonxObjectKey TryGetJsonxObjectKey, FUpdateJsonxObject UpdateJsonxObject)\n\t{\n\t\tif (SourceArray.Num() > 0)\n\t\t{\n\t\t\tTArray<TSharedPtr<FJsonxValue>> NewJsonxValues;\n\t\t\t{\n\t\t\t\tconst TArray<TSharedPtr<FJsonxValue>>* ExistingJsonxValues;\n\t\t\t\tif (JsonxObject.TryGetArrayField(ArrayName, ExistingJsonxValues))\n\t\t\t\t{\n\t\t\t\t\t// Build a map of elements for quick access and to keep track of which ones got updated\n\t\t\t\t\tTMap<KeyType, const ElementType*> ElementsMap;\n\t\t\t\t\tfor (const ElementType& Element : SourceArray)\n\t\t\t\t\t{\n\t\t\t\t\t\tElementsMap.Add(GetElementKey.Execute(Element), &Element);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update existing json values and discard entries that no longer exist or are invalid\n\t\t\t\t\tfor (TSharedPtr<FJsonxValue> ExistingJsonxValue : *ExistingJsonxValues)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst TSharedPtr<FJsonxObject>* ExistingJsonxValueAsObject;\n\t\t\t\t\t\tif (ExistingJsonxValue->TryGetObject(ExistingJsonxValueAsObject))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tKeyType ElementKey;\n\t\t\t\t\t\t\tif (TryGetJsonxObjectKey.Execute(**ExistingJsonxValueAsObject, ElementKey))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (const ElementType** ElementPtr = ElementsMap.Find(ElementKey))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tUpdateJsonxObject.Execute(**ElementPtr, **ExistingJsonxValueAsObject);\n\t\t\t\t\t\t\t\t\tNewJsonxValues.Add(ExistingJsonxValue);\n\t\t\t\t\t\t\t\t\tElementsMap.Remove(ElementKey);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Add new elements\n\t\t\t\t\tfor (auto It = ElementsMap.CreateConstIterator(); It; ++It)\n\t\t\t\t\t{\n\t\t\t\t\t\tTSharedPtr<FJsonxObject> NewJsonxObject = MakeShareable(new FJsonxObject);\n\t\t\t\t\t\tUpdateJsonxObject.Execute(*It.Value(), *NewJsonxObject.Get());\n\t\t\t\t\t\tNewJsonxValues.Add(MakeShareable(new FJsonxValueObject(NewJsonxObject)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Array doesn't exist in the given JsonxObject, so build a new array\n\t\t\t\t\tfor (const ElementType& Element : SourceArray)\n\t\t\t\t\t{\n\t\t\t\t\t\tTSharedPtr<FJsonxObject> NewJsonxObject = MakeShareable(new FJsonxObject);\n\t\t\t\t\t\tUpdateJsonxObject.Execute(Element, *NewJsonxObject.Get());\n\t\t\t\t\t\tNewJsonxValues.Add(MakeShareable(new FJsonxValueObject(NewJsonxObject)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the new content of the json array\n\t\t\tJsonxObject.SetArrayField(ArrayName, NewJsonxValues);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Source array is empty so remove the json array\n\t\t\tJsonxObject.RemoveField(ArrayName);\n\t\t}\n\t}\n};"
  },
  {
    "path": "Source/Jsonx/Public/Policies/CondensedJsonxPrintPolicy.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Policies/JsonxPrintPolicy.h\"\n\n/**\n * Template for print policies that generate compressed output.\n *\n * @param CharType The type of characters to print, i.e. TCHAR or ANSICHAR.\n */\ntemplate <class CharType>\nstruct TCondensedJsonxPrintPolicy\n\t: public TJsonxPrintPolicy<CharType>\n{\n\tstatic inline void WriteLineTerminator(FArchive* Stream) {}\n\tstatic inline void WriteTabs(FArchive* Stream, int32 Count) {}\n\tstatic inline void WriteSpace(FArchive* Stream) {}\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Policies/JsonxPrintPolicy.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n\n/**\n * Base template for Jsonx print policies.\n *\n * @param CharType The type of characters to print, i.e. TCHAR or ANSICHAR.\n */\ntemplate <class CharType>\nstruct TJsonxPrintPolicy\n{\n\t/**\n\t * Writes a single character to the output stream.\n\t *\n\t * @param Stream The stream to write to.\n\t * @param Char The character to write.\n\t */\n\tstatic inline void WriteChar( FArchive* Stream, CharType Char )\n\t{\n\t\tStream->Serialize(&Char, sizeof(CharType));\n\t}\n\n\t/**\n\t * Writes a string to the output stream.\n\t *\n\t * @param Stream The stream to write to.\n\t * @param String The string to write.\n\t */\n\tstatic inline void WriteString( FArchive* Stream, const FString& String )\n\t{\n\t\tconst TCHAR* CharPtr = *String;\n\n\t\tfor (int32 CharIndex = 0; CharIndex < String.Len(); ++CharIndex, ++CharPtr)\n\t\t{\n\t\t\tWriteChar(Stream, *CharPtr);\n\t\t}\n\t}\n};\n\n\n/**\n * Specialization for TCHAR that allows direct copying from FString data.\n */\ntemplate <>\ninline void TJsonxPrintPolicy<TCHAR>::WriteString( FArchive* Stream, const FString& String )\n{\n\tStream->Serialize((void*)*String, String.Len() * sizeof(TCHAR));\n}\n\n\n#if !PLATFORM_TCHAR_IS_CHAR16\n\n/**\n * Specialization for UTF16CHAR that writes FString data UTF-16.\n */\ntemplate <>\ninline void TJsonxPrintPolicy<UTF16CHAR>::WriteString(FArchive* Stream, const FString& String)\n{\n\t// Note: This is a no-op on platforms that are using a 16-bit TCHAR\n\tFTCHARToUTF16 UTF16String(*String, String.Len());\n\n\tStream->Serialize((void*)UTF16String.Get(), UTF16String.Length() * sizeof(UTF16CHAR));\n}\n\n#endif\n\n"
  },
  {
    "path": "Source/Jsonx/Public/Policies/PrettyJsonxPrintPolicy.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Policies/JsonxPrintPolicy.h\"\n\n/**\n * Template for print policies that generate human readable output.\n *\n * @param CharType The type of characters to print, i.e. TCHAR or ANSICHAR.\n */\ntemplate <class CharType>\nstruct TPrettyJsonxPrintPolicy\n\t: public TJsonxPrintPolicy<CharType>\n{\n\tstatic inline void WriteLineTerminator( FArchive* Stream )\n\t{\n\t\tTJsonxPrintPolicy<CharType>::WriteString(Stream, LINE_TERMINATOR);\n\t}\n\n\tstatic inline void WriteTabs( FArchive* Stream, int32 Count )\n\t{\n\t\tCharType Tab = CharType('\\t');\n\n\t\tfor (int32 i = 0; i < Count; ++i)\n\t\t{\n\t\t\tTJsonxPrintPolicy<CharType>::WriteChar(Stream, Tab);\n\t\t}\n\t}\n\n\tstatic inline void WriteSpace( FArchive* Stream )\n\t{\n\t\tTJsonxPrintPolicy<CharType>::WriteChar(Stream, CharType(' '));\n\t}\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Serialization/JsonxReader.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Serialization/BufferReader.h\"\n\nclass Error;\n\n#define JSONX_NOTATIONMAP_DEF \\\nstatic EJsonxNotation TokenToNotationTablex[] =  \\\n{ \\\n\tEJsonxNotation::Error,\t\t\t/*EJsonxToken::None*/ \\\n\tEJsonxNotation::Error,\t\t\t/*EJsonxToken::Comma*/ \\\n\tEJsonxNotation::ObjectStart,\t\t/*EJsonxToken::CurlyOpen*/ \\\n\tEJsonxNotation::ObjectEnd,\t\t/*EJsonxToken::CurlyClose*/ \\\n\tEJsonxNotation::ArrayStart,\t\t/*EJsonxToken::SquareOpen*/ \\\n\tEJsonxNotation::ArrayEnd,\t\t/*EJsonxToken::SquareClose*/ \\\n\tEJsonxNotation::Error,\t\t\t/*EJsonxToken::Colon*/ \\\n\tEJsonxNotation::String,\t\t\t/*EJsonxToken::String*/ \\\n\tEJsonxNotation::Number,\t\t\t/*EJsonxToken::Number*/ \\\n\tEJsonxNotation::Boolean,\t\t\t/*EJsonxToken::True*/ \\\n\tEJsonxNotation::Boolean,\t\t\t/*EJsonxToken::False*/ \\\n\tEJsonxNotation::Null,\t\t\t/*EJsonxToken::Null*/ \\\n};\n\n#ifndef WITH_JSONX_INLINED_NOTATIONMAP\n#define WITH_JSONX_INLINED_NOTATIONMAP 0\n#endif // WITH_JSONX_INLINED_NOTATIONMAP\n\n#if !WITH_JSONX_INLINED_NOTATIONMAP\nJSONX_NOTATIONMAP_DEF;\n#endif // WITH_JSONX_INLINED_NOTATIONMAP\n\ntemplate <class CharType = TCHAR>\nclass TJsonxReader\n{\npublic:\n\n\tstatic TSharedRef< TJsonxReader<CharType> > Create( FArchive* const Stream )\n\t{\n\t\treturn MakeShareable( new TJsonxReader<CharType>( Stream ) );\n\t}\n\npublic:\n\n\tvirtual ~TJsonxReader() {}\n\n\tbool ReadNext( EJsonxNotation& Notation )\n\t{\n\t\tif (!ErrorMessage.IsEmpty())\n\t\t{\n\t\t\tNotation = EJsonxNotation::Error;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (Stream == nullptr)\n\t\t{\n\t\t\tNotation = EJsonxNotation::Error;\n\t\t\tSetErrorMessage(TEXT(\"Null Stream\"));\n\t\t\treturn true;\n\t\t}\n\n\t\tconst bool AtEndOfStream = Stream->AtEnd();\n\n\t\tif (AtEndOfStream && !FinishedReadingRootObject)\n\t\t{\n\t\t\tNotation = EJsonxNotation::Error;\n\t\t\tSetErrorMessage(TEXT(\"Improperly formatted.\"));\n\t\t\treturn true;\n\t\t}\n\n\t\tif (FinishedReadingRootObject && !AtEndOfStream)\n\t\t{\n\t\t\tNotation = EJsonxNotation::Error;\n\t\t\tSetErrorMessage(TEXT(\"Unexpected additional input found.\"));\n\t\t\treturn true;\n\t\t}\n\n\t\tif (AtEndOfStream)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tbool ReadWasSuccess = false;\n\t\tIdentifier.Empty();\n\n\t\tdo\n\t\t{\n\t\t\tEJsonx CurrentState = EJsonx::None;\n\n\t\t\tif (ParseState.Num() > 0)\n\t\t\t{\n\t\t\t\tCurrentState = ParseState.Top();\n\t\t\t}\n\n\t\t\tswitch (CurrentState)\n\t\t\t{\n\t\t\t\tcase EJsonx::Array:\n\t\t\t\t\tReadWasSuccess = ReadNextArrayValue( /*OUT*/ CurrentToken );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase EJsonx::Object:\n\t\t\t\t\tReadWasSuccess = ReadNextObjectValue( /*OUT*/ CurrentToken );\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tReadWasSuccess = ReadStart( /*OUT*/ CurrentToken );\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\twhile (ReadWasSuccess && (CurrentToken == EJsonxToken::None));\n\n#if WITH_JSONX_INLINED_NOTATIONMAP\n\t\tJSONX_NOTATIONMAP_DEF;\n#endif // WITH_JSONX_INLINED_NOTATIONMAP\n\n\t\tNotation = TokenToNotationTablex[(int32)CurrentToken];\n\t\tFinishedReadingRootObject = ParseState.Num() == 0;\n\n\t\tif (!ReadWasSuccess || (Notation == EJsonxNotation::Error))\n\t\t{\n\t\t\tNotation = EJsonxNotation::Error;\n\n\t\t\tif (ErrorMessage.IsEmpty())\n\t\t\t{\n\t\t\t\tSetErrorMessage(TEXT(\"Unknown Error Occurred\"));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (FinishedReadingRootObject && !Stream->AtEnd())\n\t\t{\n\t\t\tReadWasSuccess = ParseWhiteSpace();\n\t\t}\n\n\t\treturn ReadWasSuccess;\n\t}\n\n\tbool SkipObject()\n\t{\n\t\treturn ReadUntilMatching(EJsonxNotation::ObjectEnd);\n\t}\n\n\tbool SkipArray()\n\t{\n\t\treturn ReadUntilMatching(EJsonxNotation::ArrayEnd);\n\t}\n\n\tFORCEINLINE virtual const FString& GetIdentifier() const { return Identifier; }\n\n\tFORCEINLINE virtual  const FString& GetValueAsString() const \n\t{ \n\t\tcheck(CurrentToken == EJsonxToken::String);\n\t\treturn StringValue;\n\t}\n\t\n\tFORCEINLINE double GetValueAsNumber() const \n\t{ \n\t\tcheck(CurrentToken == EJsonxToken::Number);\n\t\treturn NumberValue;\n\t}\n\n\tFORCEINLINE const FString& GetValueAsNumberString() const\n\t{\n\t\tcheck(CurrentToken == EJsonxToken::Number);\n\t\treturn StringValue;\n\t}\n\t\n\tFORCEINLINE bool GetValueAsBoolean() const \n\t{ \n\t\tcheck((CurrentToken == EJsonxToken::True) || (CurrentToken == EJsonxToken::False));\n\t\treturn BoolValue; \n\t}\n\n\tFORCEINLINE const FString& GetErrorMessage() const\n\t{\n\t\treturn ErrorMessage;\n\t}\n\n\tFORCEINLINE const uint32 GetLineNumber() const\n\t{\n\t\treturn LineNumber;\n\t}\n\n\tFORCEINLINE const uint32 GetCharacterNumber() const\n\t{\n\t\treturn CharacterNumber;\n\t}\n\nprotected:\n\n\t/** Hidden default constructor. */\n\tTJsonxReader()\n\t\t: ParseState()\n\t\t, CurrentToken( EJsonxToken::None )\n\t\t, Stream( nullptr )\n\t\t, Identifier()\n\t\t, ErrorMessage()\n\t\t, StringValue()\n\t\t, NumberValue( 0.0f )\n\t\t, LineNumber( 1 )\n\t\t, CharacterNumber( 0 )\n\t\t, BoolValue( false )\n\t\t, FinishedReadingRootObject( false )\n\t{ }\n\n\t/**\n\t * Creates and initializes a new instance with the given input.\n\t *\n\t * @param InStream An archive containing the input.\n\t */\n\tTJsonxReader(FArchive* InStream)\n\t\t: ParseState()\n\t\t, CurrentToken(EJsonxToken::None)\n\t\t, Stream(InStream)\n\t\t, Identifier()\n\t\t, ErrorMessage()\n\t\t, StringValue()\n\t\t, NumberValue(0.0f)\n\t\t, LineNumber(1)\n\t\t, CharacterNumber(0)\n\t\t, BoolValue(false)\n\t\t, FinishedReadingRootObject(false)\n\t{ }\n\nprivate:\n\n\tvoid SetErrorMessage( const FString& Message )\n\t{\n\t\tErrorMessage = Message + FString::Printf(TEXT(\" Line: %u Ch: %u\"), LineNumber, CharacterNumber);\n\t}\n\n\tbool ReadUntilMatching( const EJsonxNotation ExpectedNotation )\n\t{\n\t\tuint32 ScopeCount = 0;\n\t\tEJsonxNotation Notation;\n\n\t\twhile (ReadNext(Notation))\n\t\t{\n\t\t\tif ((ScopeCount == 0) && (Notation == ExpectedNotation))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tswitch (Notation)\n\t\t\t{\n\t\t\tcase EJsonxNotation::ObjectStart:\n\t\t\tcase EJsonxNotation::ArrayStart:\n\t\t\t\t++ScopeCount;\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::ObjectEnd:\n\t\t\tcase EJsonxNotation::ArrayEnd:\n\t\t\t\t--ScopeCount;\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Boolean:\n\t\t\tcase EJsonxNotation::Null:\n\t\t\tcase EJsonxNotation::Number:\n\t\t\tcase EJsonxNotation::String:\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Error:\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn !Stream->IsError();\n\t}\n\n\tbool ReadStart( EJsonxToken& Token )\n\t{\n\t\tif (!ParseWhiteSpace())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tToken = EJsonxToken::None;\n\n\t\tif (NextToken(Token) == false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif ((Token != EJsonxToken::CurlyOpen) && (Token != EJsonxToken::SquareOpen))\n\t\t{\n\t\t\tSetErrorMessage(TEXT(\"Open Curly or Square Brace token expected, but not found.\"));\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tbool ReadNextObjectValue( EJsonxToken& Token )\n\t{\n\t\tconst bool bCommaPrepend = Token != EJsonxToken::CurlyOpen;\n\t\tToken = EJsonxToken::None;\n\n\t\tif (NextToken(Token) == false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (Token == EJsonxToken::CurlyClose)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bCommaPrepend)\n\t\t\t{\n\t\t\t\tif (Token != EJsonxToken::Comma)\n\t\t\t\t{\n\t\t\t\t\tSetErrorMessage( TEXT(\"Comma token expected, but not found.\") );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tToken = EJsonxToken::None;\n\n\t\t\t\tif (!NextToken(Token))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Token != EJsonxToken::String)\n\t\t\t{\n\t\t\t\tSetErrorMessage( TEXT(\"String token expected, but not found.\") );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tIdentifier = StringValue;\n\t\t\tToken = EJsonxToken::None;\n\n\t\t\tif (!NextToken(Token))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (Token != EJsonxToken::Colon)\n\t\t\t{\n\t\t\t\tSetErrorMessage( TEXT(\"Colon token expected, but not found.\") );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tToken = EJsonxToken::None;\n\n\t\t\tif (!NextToken(Token))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tbool ReadNextArrayValue( EJsonxToken& Token )\n\t{\n\t\tconst bool bCommaPrepend = Token != EJsonxToken::SquareOpen;\n\n\t\tToken = EJsonxToken::None;\n\n\t\tif (!NextToken(Token))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (Token == EJsonxToken::SquareClose)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bCommaPrepend)\n\t\t\t{\n\t\t\t\tif (Token != EJsonxToken::Comma)\n\t\t\t\t{\n\t\t\t\t\tSetErrorMessage( TEXT(\"Comma token expected, but not found.\") );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tToken = EJsonxToken::None;\n\n\t\t\t\tif (!NextToken(Token))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tbool NextToken( EJsonxToken& OutToken )\n\t{\n\t\twhile (!Stream->AtEnd())\n\t\t{\n\t\t\tCharType Char;\n\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t++CharacterNumber;\n\n\t\t\tif (Char == CharType('\\0'))\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (IsLineBreak(Char))\n\t\t\t{\n\t\t\t\t++LineNumber;\n\t\t\t\tCharacterNumber = 0;\n\t\t\t}\n\n\t\t\tif (!IsWhitespace(Char))\n\t\t\t{\n\t\t\t\tif (IsJsonxNumber(Char))\n\t\t\t\t{\n\t\t\t\t\tif (!ParseNumberToken(Char))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tOutToken = EJsonxToken::Number;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tswitch (Char)\n\t\t\t\t{\n\t\t\t\tcase CharType('{'):\n\t\t\t\t\tOutToken = EJsonxToken::CurlyOpen; ParseState.Push( EJsonx::Object );\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase CharType('}'):\n\t\t\t\t\t{\n\t\t\t\t\t\tOutToken = EJsonxToken::CurlyClose;\n\t\t\t\t\t\tif (ParseState.Num())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tParseState.Pop();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSetErrorMessage(TEXT(\"Unknown state reached while parsing Jsonx token.\"));\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\tcase CharType('['):\n\t\t\t\t\tOutToken = EJsonxToken::SquareOpen; ParseState.Push( EJsonx::Array );\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase CharType(']'):\n\t\t\t\t\t{\n\t\t\t\t\t\tOutToken = EJsonxToken::SquareClose;\n\t\t\t\t\t\tif (ParseState.Num())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tParseState.Pop();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSetErrorMessage(TEXT(\"Unknown state reached while parsing Jsonx token.\"));\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\tcase CharType(':'):\n\t\t\t\t\tOutToken = EJsonxToken::Colon;\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase CharType(','):\n\t\t\t\t\tOutToken = EJsonxToken::Comma;\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase CharType('\\\"'):\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!ParseStringToken())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tOutToken = EJsonxToken::String;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase CharType('t'): case CharType('T'):\n\t\t\t\tcase CharType('f'): case CharType('F'):\n\t\t\t\tcase CharType('n'): case CharType('N'):\n\t\t\t\t\t{\n\t\t\t\t\t\tFString Test;\n\t\t\t\t\t\tTest += Char;\n\n\t\t\t\t\t\twhile (!Stream->AtEnd())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (IsAlphaNumber(Char))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t++CharacterNumber;\n\t\t\t\t\t\t\t\tTest += Char;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// backtrack and break\n\t\t\t\t\t\t\t\tStream->Seek(Stream->Tell() - sizeof(CharType));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (Test == TEXT(\"False\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBoolValue = false;\n\t\t\t\t\t\t\tOutToken = EJsonxToken::False;\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (Test == TEXT(\"True\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBoolValue = true;\n\t\t\t\t\t\t\tOutToken = EJsonxToken::True;\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (Test == TEXT(\"Null\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tOutToken = EJsonxToken::Null;\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tSetErrorMessage( TEXT(\"Invalid Jsonx Token. Check that your member names have quotes around them!\") );\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\tdefault: \n\t\t\t\t\tSetErrorMessage( TEXT(\"Invalid Jsonx Token.\") );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tSetErrorMessage( TEXT(\"Invalid Jsonx Token.\") );\n\t\treturn false;\n\t}\n\n\tbool ParseStringToken()\n\t{\n\t\tFString String;\n\n\t\twhile (true)\n\t\t{\n\t\t\tif (Stream->AtEnd())\n\t\t\t{\n\t\t\t\tSetErrorMessage( TEXT(\"String Token Abruptly Ended.\") );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCharType Char;\n\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t++CharacterNumber;\n\n\t\t\tif (Char == CharType('\\\"'))\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (Char == CharType('\\\\'))\n\t\t\t{\n\t\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t++CharacterNumber;\n\n\t\t\t\tswitch (Char)\n\t\t\t\t{\n\t\t\t\tcase CharType('\\\"'): case CharType('\\\\'): case CharType('/'): String += Char; break;\n\t\t\t\tcase CharType('f'): String += CharType('\\f'); break;\n\t\t\t\tcase CharType('r'): String += CharType('\\r'); break;\n\t\t\t\tcase CharType('n'): String += CharType('\\n'); break;\n\t\t\t\tcase CharType('b'): String += CharType('\\b'); break;\n\t\t\t\tcase CharType('t'): String += CharType('\\t'); break;\n\t\t\t\tcase CharType('u'):\n\t\t\t\t\t// 4 hex digits, like \\uAB23, which is a 16 bit number that we would usually see as 0xAB23\n\t\t\t\t\t{\n\t\t\t\t\t\tint32 HexNum = 0;\n\n\t\t\t\t\t\tfor (int32 Radix = 3; Radix >= 0; --Radix)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (Stream->AtEnd())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSetErrorMessage( TEXT(\"String Token Abruptly Ended.\") );\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t++CharacterNumber;\n\n\t\t\t\t\t\t\tint32 HexDigit = FParse::HexDigit(Char);\n\n\t\t\t\t\t\t\tif ((HexDigit == 0) && (Char != CharType('0')))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSetErrorMessage( TEXT(\"Invalid Hexadecimal digit parsed.\") );\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t//@TODO: FLOATPRECISION: this is gross\n\t\t\t\t\t\t\tHexNum += HexDigit * (int32)FMath::Pow(16, (float)Radix);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString += (FString::ElementType)HexNum;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tSetErrorMessage( TEXT(\"Bad Jsonx escaped char.\") );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tString += Char;\n\t\t\t}\n\t\t}\n\n\t\tStringValue = MoveTemp(String);\n\n\t\t// Inline combine any surrogate pairs in the data when loading into a UTF-32 string\n\t\tStringConv::InlineCombineSurrogates(StringValue);\n\n\t\treturn true;\n\t}\n\n\tbool ParseNumberToken( CharType FirstChar )\n\t{\n\t\tFString String;\n\t\tint32 State = 0;\n\t\tbool UseFirstChar = true;\n\t\tbool StateError = false;\n\n\t\twhile (true)\n\t\t{\n\t\t\tif (Stream->AtEnd())\n\t\t\t{\n\t\t\t\tSetErrorMessage( TEXT(\"Number Token Abruptly Ended.\") );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCharType Char;\n\t\t\tif (UseFirstChar)\n\t\t\t{\n\t\t\t\tChar = FirstChar;\n\t\t\t\tUseFirstChar = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t++CharacterNumber;\n\t\t\t}\n\n\t\t\t// The following code doesn't actually derive the Jsonx Number: that is handled\n\t\t\t// by the function FCString::Atof below. This code only ensures the Jsonx Number is\n\t\t\t// EXACTLY to specification\n\t\t\tif (IsJsonxNumber(Char))\n\t\t\t{\n\t\t\t\t// ensure number follows Jsonx format before converting\n\t\t\t\t// This switch statement is derived from a finite state automata\n\t\t\t\t// derived from the Jsonx spec. A table was not used for simplicity.\n\t\t\t\tswitch (State)\n\t\t\t\t{\n\t\t\t\tcase 0:\n\t\t\t\t\tif (Char == CharType('-')) { State = 1; }\n\t\t\t\t\telse if (Char == CharType('0')) { State = 2; }\n\t\t\t\t\telse if (IsNonZeroDigit(Char)) { State = 3; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 1:\n\t\t\t\t\tif (Char == CharType('0')) { State = 2; }\n\t\t\t\t\telse if (IsNonZeroDigit(Char)) { State = 3; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 2:\n\t\t\t\t\tif (Char == CharType('.')) { State = 4; }\n\t\t\t\t\telse if (Char == CharType('e') || Char == CharType('E')) { State = 5; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 3:\n\t\t\t\t\tif (IsDigit(Char)) { State = 3; }\n\t\t\t\t\telse if (Char == CharType('.')) { State = 4; }\n\t\t\t\t\telse if (Char == CharType('e') || Char == CharType('E')) { State = 5; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 4:\n\t\t\t\t\tif (IsDigit(Char)) { State = 6; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 5:\n\t\t\t\t\tif (Char == CharType('-') ||Char == CharType('+')) { State = 7; }\n\t\t\t\t\telse if (IsDigit(Char)) { State = 8; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 6:\n\t\t\t\t\tif (IsDigit(Char)) { State = 6; }\n\t\t\t\t\telse if (Char == CharType('e') || Char == CharType('E')) { State = 5; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 7:\n\t\t\t\t\tif (IsDigit(Char)) { State = 8; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 8:\n\t\t\t\t\tif (IsDigit(Char)) { State = 8; }\n\t\t\t\t\telse { StateError = true; }\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tSetErrorMessage( TEXT(\"Unknown state reached in Jsonx Number Token.\") );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (StateError)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tString += Char;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// backtrack once because we read a non-number character\n\t\t\t\tStream->Seek(Stream->Tell() - sizeof(CharType));\n\t\t\t\t--CharacterNumber;\n\t\t\t\t// and now the number is fully tokenized\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// ensure the number has followed valid Jsonx format\n\t\tif (!StateError && ((State == 2) || (State == 3) || (State == 6) || (State == 8)))\n\t\t{\n\t\t\tStringValue = String;\n\t\t\tNumberValue = FCString::Atod(*String);\n\t\t\treturn true;\n\t\t}\n\n\t\tSetErrorMessage( TEXT(\"Poorly formed Jsonx Number Token.\") );\n\t\treturn false;\n\t}\n\n\tbool ParseWhiteSpace()\n\t{\n\t\twhile (!Stream->AtEnd())\n\t\t{\n\t\t\tCharType Char;\n\t\t\tif (!Serialize(&Char, sizeof(CharType)))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t++CharacterNumber;\n\n\t\t\tif (IsLineBreak(Char))\n\t\t\t{\n\t\t\t\t++LineNumber;\n\t\t\t\tCharacterNumber = 0;\n\t\t\t}\n\n\t\t\tif (!IsWhitespace(Char))\n\t\t\t{\n\t\t\t\t// backtrack and break\n\t\t\t\tStream->Seek(Stream->Tell() - sizeof(CharType));\n\t\t\t\t--CharacterNumber;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool IsLineBreak( const CharType& Char )\n\t{\n\t\treturn Char == CharType('\\n');\n\t}\n\n\t/** Can't use FChar::IsWhitespace because it is TCHAR specific, and it doesn't handle newlines */\n\tbool IsWhitespace( const CharType& Char )\n\t{\n\t\treturn Char == CharType(' ') || Char == CharType('\\t') || Char == CharType('\\n') || Char == CharType('\\r');\n\t}\n\n\t/** Can't use FChar::IsDigit because it is TCHAR specific, and it doesn't handle all the other Jsonx number characters */\n\tbool IsJsonxNumber( const CharType& Char )\n\t{\n\t\treturn ((Char >= CharType('0') && Char <= CharType('9')) ||\n\t\t\tChar == CharType('-') || Char == CharType('.') || Char == CharType('+') || Char == CharType('e') || Char == CharType('E'));\n\t}\n\n\t/** Can't use FChar::IsDigit because it is TCHAR specific */\n\tbool IsDigit( const CharType& Char )\n\t{\n\t\treturn (Char >= CharType('0') && Char <= CharType('9'));\n\t}\n\n\tbool IsNonZeroDigit( const CharType& Char )\n\t{\n\t\treturn (Char >= CharType('1') && Char <= CharType('9'));\n\t}\n\n\t/** Can't use FChar::IsAlpha because it is TCHAR specific. Also, this only checks A through Z (no underscores or other characters). */\n\tbool IsAlphaNumber( const CharType& Char )\n\t{\n\t\treturn (Char >= CharType('a') && Char <= CharType('z')) || (Char >= CharType('A') && Char <= CharType('Z'));\n\t}\n\nprotected:\n\tbool Serialize(void* V, int64 Length)\n\t{\n\t\tStream->Serialize(V, Length);\n\t\tif (Stream->IsError())\n\t\t{\n\t\t\tSetErrorMessage(TEXT(\"Stream I/O Error\"));\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\nprotected:\n\n\tTArray<EJsonx> ParseState;\n\tEJsonxToken CurrentToken;\n\n\tFArchive* Stream;\n\tFString Identifier;\n\tFString ErrorMessage;\n\tFString StringValue;\n\tdouble NumberValue;\n\tuint32 LineNumber;\n\tuint32 CharacterNumber;\n\tbool BoolValue;\n\tbool FinishedReadingRootObject;\n};\n\n\nclass FJsonxStringReader\n\t: public TJsonxReader<TCHAR>\n{\npublic:\n\n\tstatic TSharedRef<FJsonxStringReader> Create(const FString& JsonxString)\n\t{\n\t\treturn MakeShareable(new FJsonxStringReader(JsonxString));\n\t}\n\n\tstatic TSharedRef<FJsonxStringReader> Create(FString&& JsonxString)\n\t{\n\t\treturn MakeShareable(new FJsonxStringReader(MoveTemp(JsonxString)));\n\t}\n\n\tconst FString& GetSourceString() const\n\t{\n\t\treturn Content;\n\t}\npublic:\n\n\tvirtual ~FJsonxStringReader() = default;\n\nprotected:\n\n\t/**\n\t * Parses a string containing Jsonx information.\n\t *\n\t * @param JsonxString The Jsonx string to parse.\n\t */\n\tFJsonxStringReader(const FString& JsonxString)\n\t\t: Content(JsonxString)\n\t\t, Reader(nullptr)\n\t{\n\t\tInitReader();\n\t}\n\n\t/**\n\t * Parses a string containing Jsonx information.\n\t *\n\t * @param JsonxString The Jsonx string to parse.\n\t */\n\tFJsonxStringReader(FString&& JsonxString)\n\t\t: Content(MoveTemp(JsonxString))\n\t\t, Reader(nullptr)\n\t{\n\t\tInitReader();\n\t}\n\n\tFORCEINLINE void InitReader()\n\t{\n\t\tif (Content.IsEmpty())\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tReader = MakeUnique<FBufferReader>((void*)*Content, Content.Len() * sizeof(TCHAR), false);\n\t\tcheck(Reader.IsValid());\n\n\t\tStream = Reader.Get();\n\t}\n\nprotected:\n\tconst FString Content;\n\tTUniquePtr<FBufferReader> Reader;\n};\n\n\ntemplate <class CharType = TCHAR>\nclass TJsonxReaderFactory\n{\npublic:\n\n\tstatic TSharedRef<TJsonxReader<TCHAR>> Create(const FString& JsonxString)\n\t{\n\t\treturn FJsonxStringReader::Create(JsonxString);\n\t}\n\n\tstatic TSharedRef<TJsonxReader<TCHAR>> Create(FString&& JsonxString)\n\t{\n\t\treturn FJsonxStringReader::Create(MoveTemp(JsonxString));\n\t}\n\n\tstatic TSharedRef<TJsonxReader<CharType>> Create(FArchive* const Stream)\n\t{\n\t\treturn TJsonxReader<CharType>::Create(Stream);\n\t}\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Serialization/JsonxSerializer.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Serialization/JsonxReader.h\"\n#include \"Dom/JsonxValue.h\"\n#include \"Dom/JsonxObject.h\"\n#include \"Serialization/JsonxWriter.h\"\n\nclass Error;\n\nclass FJsonxSerializer\n{\npublic:\n\n\tenum class EFlags\n\t{\n\t\tNone = 0,\n\t\tStoreNumbersAsStrings = 1,\n\t};\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(const TSharedRef<TJsonxReader<CharType>>& Reader, TArray<TSharedPtr<FJsonxValue>>& OutArray, EFlags InOptions = EFlags::None)\n\t{\n\t\treturn Deserialize(*Reader, OutArray, InOptions);\n\t}\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(TJsonxReader<CharType>& Reader, TArray<TSharedPtr<FJsonxValue>>& OutArray, EFlags InOptions = EFlags::None)\n\t{\n\t\tStackState State;\n\t\tif (!Deserialize(Reader, /*OUT*/State, InOptions))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t// Empty array is ok.\n\t\tif (State.Type != EJsonx::Array)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tOutArray = State.Array;\n\n\t\treturn true;\n\t}\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(const TSharedRef<TJsonxReader<CharType>>& Reader, TSharedPtr<FJsonxObject>& OutObject, EFlags InOptions = EFlags::None)\n\t{\n\t\treturn Deserialize(*Reader, OutObject, InOptions);\n\t}\n\ttemplate <class CharType>\n\tstatic bool Deserialize(TJsonxReader<CharType>& Reader, TSharedPtr<FJsonxObject>& OutObject, EFlags InOptions = EFlags::None)\n\t{\n\t\tStackState State;\n\t\tif (!Deserialize(Reader, /*OUT*/State, InOptions))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!State.Object.IsValid())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tOutObject = State.Object;\n\n\t\treturn true;\n\t}\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(const TSharedRef<TJsonxReader<CharType>>& Reader, TSharedPtr<FJsonxValue>& OutValue, EFlags InOptions = EFlags::None)\n\t{\n\t\treturn Deserialize(*Reader, OutValue, InOptions);\n\t}\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(TJsonxReader<CharType>& Reader, TSharedPtr<FJsonxValue>& OutValue, EFlags InOptions = EFlags::None)\n\t{\n\t\tStackState State;\n\t\tif (!Deserialize(Reader, /*OUT*/State, InOptions))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (State.Type)\n\t\t{\n\t\tcase EJsonx::Object:\n\t\t\tif (!State.Object.IsValid())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tOutValue = MakeShared<FJsonxValueObject>(State.Object);\n\t\t\tbreak;\n\t\tcase EJsonx::Array:\n\t\t\tOutValue = MakeShared<FJsonxValueArray>(State.Array);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// FIXME: would be nice to handle non-composite root values but StackState Deserialize just drops them on the floor\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TArray<TSharedPtr<FJsonxValue>>& Array, const TSharedRef<TJsonxWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true)\n\t{\n\t\treturn Serialize(Array, *Writer, bCloseWriter);\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TArray<TSharedPtr<FJsonxValue>>& Array, TJsonxWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true )\n\t{\n\t\tconst TSharedRef<FElement> StartingElement = MakeShared<FElement>(Array);\n\t\treturn FJsonxSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TSharedRef<FJsonxObject>& Object, const TSharedRef<TJsonxWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true )\n\t{\n\t\treturn Serialize(Object, *Writer, bCloseWriter);\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TSharedRef<FJsonxObject>& Object, TJsonxWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true)\n\t{\n\t\tconst TSharedRef<FElement> StartingElement = MakeShared<FElement>(Object);\n\t\treturn FJsonxSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TSharedPtr<FJsonxValue>& Value, const FString& Identifier, const TSharedRef<TJsonxWriter<CharType, PrintPolicy>>& Writer, bool bCloseWriter = true)\n\t{\n\t\treturn Serialize(Value, Identifier, *Writer, bCloseWriter);\n\t}\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TSharedPtr<FJsonxValue>& Value, const FString& Identifier, TJsonxWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter = true)\n\t{\n\t\tconst TSharedRef<FElement> StartingElement = MakeShared<FElement>(Identifier, Value);\n\t\treturn FJsonxSerializer::Serialize<CharType, PrintPolicy>(StartingElement, Writer, bCloseWriter);\n\t}\n\nprivate:\n\n\tstruct StackState\n\t{\n\t\tEJsonx Type;\n\t\tFString Identifier;\n\t\tTArray<TSharedPtr<FJsonxValue>> Array;\n\t\tTSharedPtr<FJsonxObject> Object;\n\t};\n\n\tstruct FElement\n\t{\n\t\tFElement( const TSharedPtr<FJsonxValue>& InValue )\n\t\t\t: Identifier()\n\t\t\t, Value(InValue)\n\t\t\t, HasBeenProcessed(false)\n\t\t{ }\n\n\t\tFElement( const TSharedRef<FJsonxObject>& Object )\n\t\t\t: Identifier()\n\t\t\t, Value(MakeShared<FJsonxValueObject>(Object))\n\t\t\t, HasBeenProcessed( false )\n\t\t{ }\n\n\t\tFElement( const TArray<TSharedPtr<FJsonxValue>>& Array )\n\t\t\t: Identifier()\n\t\t\t, Value(MakeShared<FJsonxValueArray>(Array))\n\t\t\t, HasBeenProcessed(false)\n\t\t{ }\n\n\t\tFElement( const FString& InIdentifier, const TSharedPtr< FJsonxValue >& InValue )\n\t\t\t: Identifier( InIdentifier )\n\t\t\t, Value( InValue )\n\t\t\t, HasBeenProcessed( false )\n\t\t{\n\n\t\t}\n\n\t\tFString Identifier;\n\t\tTSharedPtr< FJsonxValue > Value;\n\t\tbool HasBeenProcessed;\n\t};\n\nprivate:\n\n\ttemplate <class CharType>\n\tstatic bool Deserialize(TJsonxReader<CharType>& Reader, StackState& OutStackState, EFlags InOptions)\n\t{\n\t\tTArray<TSharedRef<StackState>> ScopeStack; \n\t\tTSharedPtr<StackState> CurrentState;\n\n\t\tTSharedPtr<FJsonxValue> NewValue;\n\t\tEJsonxNotation Notation;\n\n\t\twhile (Reader.ReadNext(Notation))\n\t\t{\n\t\t\tFString Identifier = Reader.GetIdentifier();\n\t\t\tNewValue.Reset();\n\n\t\t\tswitch( Notation )\n\t\t\t{\n\t\t\tcase EJsonxNotation::ObjectStart:\n\t\t\t\t{\n\t\t\t\t\tif (CurrentState.IsValid())\n\t\t\t\t\t{\n\t\t\t\t\t\tScopeStack.Push(CurrentState.ToSharedRef());\n\t\t\t\t\t}\n\n\t\t\t\t\tCurrentState = MakeShared<StackState>();\n\t\t\t\t\tCurrentState->Type = EJsonx::Object;\n\t\t\t\t\tCurrentState->Identifier = Identifier;\n\t\t\t\t\tCurrentState->Object = MakeShared<FJsonxObject>();\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::ObjectEnd:\n\t\t\t\t{\n\t\t\t\t\tif (ScopeStack.Num() > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tIdentifier = CurrentState->Identifier;\n\t\t\t\t\t\tNewValue = MakeShared<FJsonxValueObject>(CurrentState->Object);\n\t\t\t\t\t\tCurrentState = ScopeStack.Pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::ArrayStart:\n\t\t\t\t{\n\t\t\t\t\tif (CurrentState.IsValid())\n\t\t\t\t\t{\n\t\t\t\t\t\tScopeStack.Push(CurrentState.ToSharedRef());\n\t\t\t\t\t}\n\n\t\t\t\t\tCurrentState = MakeShared<StackState>();\n\t\t\t\t\tCurrentState->Type = EJsonx::Array;\n\t\t\t\t\tCurrentState->Identifier = Identifier;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::ArrayEnd:\n\t\t\t\t{\n\t\t\t\t\tif (ScopeStack.Num() > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tIdentifier = CurrentState->Identifier;\n\t\t\t\t\t\tNewValue = MakeShared<FJsonxValueArray>(CurrentState->Array);\n\t\t\t\t\t\tCurrentState = ScopeStack.Pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Boolean:\n\t\t\t\tNewValue = MakeShared<FJsonxValueBoolean>(Reader.GetValueAsBoolean());\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::String:\n\t\t\t\tNewValue = MakeShared<FJsonxValueString>(Reader.GetValueAsString());\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Number:\n\t\t\t\tif (EnumHasAnyFlags(InOptions, EFlags::StoreNumbersAsStrings))\n\t\t\t\t{\n\t\t\t\t\tNewValue = MakeShared<FJsonxValueNumberString>(Reader.GetValueAsNumberString());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tNewValue = MakeShared<FJsonxValueNumber>(Reader.GetValueAsNumber());\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Null:\n\t\t\t\tNewValue = MakeShared<FJsonxValueNull>();\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonxNotation::Error:\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (NewValue.IsValid() && CurrentState.IsValid())\n\t\t\t{\n\t\t\t\tif (CurrentState->Type == EJsonx::Object)\n\t\t\t\t{\n\t\t\t\t\tCurrentState->Object->SetField(Identifier, NewValue);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCurrentState->Array.Add(NewValue);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!CurrentState.IsValid() || !Reader.GetErrorMessage().IsEmpty())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tOutStackState = *CurrentState.Get();\n\n\t\treturn true;\n\t}\n\n\ttemplate <class CharType, class PrintPolicy>\n\tstatic bool Serialize(const TSharedRef<FElement>& StartingElement, TJsonxWriter<CharType, PrintPolicy>& Writer, bool bCloseWriter)\n\t{\n\t\tTArray<TSharedRef<FElement>> ElementStack;\n\t\tElementStack.Push(StartingElement);\n\n\t\twhile (ElementStack.Num() > 0)\n\t\t{\n\t\t\tTSharedRef<FElement> Element = ElementStack.Pop();\n\t\t\tcheck(Element->Value->Type != EJsonx::None);\n\n\t\t\tswitch (Element->Value->Type)\n\t\t\t{\n\t\t\tcase EJsonx::Number:\t\n\t\t\t\t{\n\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Value->AsNumber());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Identifier, Element->Value->AsNumber());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonx::Boolean:\t\t\t\t\t\n\t\t\t\t{\n\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Value->AsBool());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Identifier, Element->Value->AsBool());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonx::String:\n\t\t\t\t{\n\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Value->AsString());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteValue(Element->Identifier, Element->Value->AsString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonx::Null:\n\t\t\t\t{\n\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteNull();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteNull(Element->Identifier);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonx::Array:\n\t\t\t\t{\n\t\t\t\t\tif (Element->HasBeenProcessed)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteArrayEnd();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tElement->HasBeenProcessed = true;\n\t\t\t\t\t\tElementStack.Push(Element);\n\n\t\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tWriter.WriteArrayStart();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tWriter.WriteArrayStart(Element->Identifier);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tTArray<TSharedPtr<FJsonxValue>> Values = Element->Value->AsArray();\n\n\t\t\t\t\t\tfor (int Index = Values.Num() - 1; Index >= 0; --Index)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tElementStack.Push(MakeShared<FElement>(Values[Index]));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase EJsonx::Object:\n\t\t\t\t{\n\t\t\t\t\tif (Element->HasBeenProcessed)\n\t\t\t\t\t{\n\t\t\t\t\t\tWriter.WriteObjectEnd();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tElement->HasBeenProcessed = true;\n\t\t\t\t\t\tElementStack.Push(Element);\n\n\t\t\t\t\t\tif (Element->Identifier.IsEmpty())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tWriter.WriteObjectStart();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tWriter.WriteObjectStart(Element->Identifier);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tTArray<FString> Keys; \n\t\t\t\t\t\tTArray<TSharedPtr<FJsonxValue>> Values;\n\t\t\t\t\t\tTSharedPtr<FJsonxObject> ElementObject = Element->Value->AsObject();\n\t\t\t\t\t\tElementObject->Values.GenerateKeyArray(Keys);\n\t\t\t\t\t\tElementObject->Values.GenerateValueArray(Values);\n\n\t\t\t\t\t\tcheck(Keys.Num() == Values.Num());\n\n\t\t\t\t\t\tfor (int Index = Values.Num() - 1; Index >= 0; --Index)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tElementStack.Push(MakeShared<FElement>(Keys[Index], Values[Index]));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault: \n\t\t\t\tUE_LOG(LogJsonx, Fatal,TEXT(\"Could not print Jsonx Value, unrecognized type.\"));\n\t\t\t}\n\t\t}\n\n\t\tif (bCloseWriter)\n\t\t{\n\t\t\treturn Writer.Close();\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Serialization/JsonxSerializerMacros.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Policies/PrettyJsonxPrintPolicy.h\"\n#include \"Policies/CondensedJsonxPrintPolicy.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Serialization/JsonxReader.h\"\n#include \"Serialization/JsonxSerializer.h\"\n\n/**\n * Macros used to generate a serialization function for a class derived from FJsonxSerializable\n */\n#define BEGIN_JSONX_SERIALIZER \\\n\tvirtual void Serialize(FJsonxSerializerBase& Serializer, bool bFlatObject) override \\\n\t{ \\\n\t\tif (!bFlatObject) { Serializer.StartObject(); }\n\n#define END_JSONX_SERIALIZER \\\n\t\tif (!bFlatObject) { Serializer.EndObject(); } \\\n\t}\n\n#define JSONX_SERIALIZE(JsonxName, JsonxValue) \\\n\t\tSerializer.Serialize(TEXT(JsonxName), JsonxValue)\n\n#define JSONX_SERIALIZE_OPTIONAL(JsonxName, OptionalJsonxValue) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tif (Serializer.GetObject()->HasField(TEXT(JsonxName))) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.Serialize(TEXT(JsonxName), OptionalJsonxValue.Emplace()); \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tif (OptionalJsonxValue.IsSet()) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.Serialize(TEXT(JsonxName), OptionalJsonxValue.GetValue()); \\\n\t\t\t} \\\n\t\t}\n\n#define JSONX_SERIALIZE_ARRAY(JsonxName, JsonxArray) \\\n\t\tSerializer.SerializeArray(TEXT(JsonxName), JsonxArray)\n\n#define JSONX_SERIALIZE_MAP(JsonxName, JsonxMap) \\\n\t\tSerializer.SerializeMap(TEXT(JsonxName), JsonxMap)\n\n#define JSONX_SERIALIZE_SIMPLECOPY(JsonxMap) \\\n\t\tSerializer.SerializeSimpleMap(JsonxMap)\n\n#define JSONX_SERIALIZE_MAP_SAFE(JsonxName, JsonxMap) \\\n\t\tSerializer.SerializeMapSafe(TEXT(JsonxName), JsonxMap)\n\n#define JSONX_SERIALIZE_SERIALIZABLE(JsonxName, JsonxValue) \\\n\t\tJsonxValue.Serialize(Serializer, false)\n\n#define JSONX_SERIALIZE_RAW_JSONX_STRING(JsonxName, JsonxValue) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Object>(TEXT(JsonxName))) \\\n\t\t\t{ \\\n\t\t\t\tTSharedPtr<FJsonxObject> JsonxObject = Serializer.GetObject()->GetObjectField(TEXT(JsonxName)); \\\n\t\t\t\tif (JsonxObject.IsValid()) \\\n\t\t\t\t{ \\\n\t\t\t\t\tauto Writer = TJsonxWriterFactory<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>::Create(&JsonxValue); \\\n\t\t\t\t\tFJsonxSerializer::Serialize(JsonxObject.ToSharedRef(), Writer); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t\telse \\\n\t\t\t{ \\\n\t\t\t\tJsonxValue = FString(); \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tif (!JsonxValue.IsEmpty()) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.WriteIdentifierPrefix(TEXT(JsonxName)); \\\n\t\t\t\tSerializer.WriteRawJSONXValue(*JsonxValue); \\\n\t\t\t} \\\n\t\t}\n\n#define JSONX_SERIALIZE_ARRAY_SERIALIZABLE(JsonxName, JsonxArray, ElementType) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Array>(JsonxName)) \\\n\t\t\t{ \\\n\t\t\t\tfor (auto It = Serializer.GetObject()->GetArrayField(JsonxName).CreateConstIterator(); It; ++It) \\\n\t\t\t\t{ \\\n\t\t\t\t\tElementType* Obj = new(JsonxArray) ElementType(); \\\n\t\t\t\t\tObj->FromJsonx((*It)->AsObject()); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tSerializer.StartArray(JsonxName); \\\n\t\t\tfor (auto It = JsonxArray.CreateIterator(); It; ++It) \\\n\t\t\t{ \\\n\t\t\t\tIt->Serialize(Serializer, false); \\\n\t\t\t} \\\n\t\t\tSerializer.EndArray(); \\\n\t\t}\n\n#define JSONX_SERIALIZE_OPTIONAL_ARRAY_SERIALIZABLE(JsonxName, OptionalJsonxArray, ElementType) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Array>(JsonxName)) \\\n\t\t\t{ \\\n\t\t\t\tTArray<ElementType>& JsonxArray = OptionalJsonxArray.Emplace(); \\\n\t\t\t\tfor (auto It = Serializer.GetObject()->GetArrayField(JsonxName).CreateConstIterator(); It; ++It) \\\n\t\t\t\t{ \\\n\t\t\t\t\tElementType* Obj = new(JsonxArray) ElementType(); \\\n\t\t\t\t\tObj->FromJsonx((*It)->AsObject()); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tif (OptionalJsonxArray.IsSet()) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.StartArray(JsonxName); \\\n\t\t\t\tfor (auto It = OptionalJsonxArray->CreateIterator(); It; ++It) \\\n\t\t\t\t{ \\\n\t\t\t\t\tIt->Serialize(Serializer, false); \\\n\t\t\t\t} \\\n\t\t\t\tSerializer.EndArray(); \\\n\t\t\t} \\\n\t\t}\n\n#define JSONX_SERIALIZE_MAP_SERIALIZABLE(JsonxName, JsonxMap, ElementType) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Object>(JsonxName)) \\\n\t\t\t{ \\\n\t\t\t\tTSharedPtr<FJsonxObject> JsonxObj = Serializer.GetObject()->GetObjectField(JsonxName); \\\n\t\t\t\tfor (auto MapIt = JsonxObj->Values.CreateConstIterator(); MapIt; ++MapIt) \\\n\t\t\t\t{ \\\n\t\t\t\t\tElementType NewEntry; \\\n\t\t\t\t\tNewEntry.FromJsonx(MapIt.Value()->AsObject()); \\\n\t\t\t\t\tJsonxMap.Add(MapIt.Key(), NewEntry); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tSerializer.StartObject(JsonxName); \\\n\t\t\tfor (auto It = JsonxMap.CreateIterator(); It; ++It) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.StartObject(It.Key()); \\\n\t\t\t\tIt.Value().Serialize(Serializer, true); \\\n\t\t\t\tSerializer.EndObject(); \\\n\t\t\t} \\\n\t\t\tSerializer.EndObject(); \\\n\t\t}\n\n#define JSONX_SERIALIZE_OBJECT_SERIALIZABLE(JsonxName, JsonxSerializableObject) \\\n\t\t/* Process the JsonxName field differently because it is an object */ \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\t/* Read in the value from the JsonxName field */ \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Object>(JsonxName)) \\\n\t\t\t{ \\\n\t\t\t\tTSharedPtr<FJsonxObject> JsonxObj = Serializer.GetObject()->GetObjectField(JsonxName); \\\n\t\t\t\tif (JsonxObj.IsValid()) \\\n\t\t\t\t{ \\\n\t\t\t\t\t(JsonxSerializableObject).FromJsonx(JsonxObj); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\t/* Write the value to the Name field */ \\\n\t\t\tSerializer.StartObject(JsonxName); \\\n\t\t\t(JsonxSerializableObject).Serialize(Serializer, true); \\\n\t\t\tSerializer.EndObject(); \\\n\t\t}\n\n#define JSONX_SERIALIZE_OPTIONAL_OBJECT_SERIALIZABLE(JsonxName, JsonxSerializableObject) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tusing ObjectType = TRemoveReference<decltype(JsonxSerializableObject.GetValue())>::Type; \\\n\t\t\tif (Serializer.GetObject()->HasTypedField<EJsonx::Object>(JsonxName)) \\\n\t\t\t{ \\\n\t\t\t\tTSharedPtr<FJsonxObject> JsonxObj = Serializer.GetObject()->GetObjectField(JsonxName); \\\n\t\t\t\tif (JsonxObj.IsValid()) \\\n\t\t\t\t{ \\\n\t\t\t\t\tJsonxSerializableObject = ObjectType{}; \\\n\t\t\t\t\tJsonxSerializableObject.GetValue().FromJsonx(JsonxObj); \\\n\t\t\t\t} \\\n\t\t\t} \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tif (JsonxSerializableObject.IsSet()) \\\n\t\t\t{ \\\n\t\t\t\tSerializer.StartObject(JsonxName); \\\n\t\t\t\t(JsonxSerializableObject.GetValue()).Serialize(Serializer, true); \\\n\t\t\t\tSerializer.EndObject(); \\\n\t\t\t} \\\n\t\t}\n\n#define JSONX_SERIALIZE_DATETIME_UNIX_TIMESTAMP(JsonxName, JsonxDateTime) \\\n\t\tif (Serializer.IsLoading()) \\\n\t\t{ \\\n\t\t\tint64 UnixTimestampValue; \\\n\t\t\tSerializer.Serialize(TEXT(JsonxName), UnixTimestampValue); \\\n\t\t\tJsonxDateTime = FDateTime::FromUnixTimestamp(UnixTimestampValue); \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tint64 UnixTimestampValue = JsonxDateTime.ToUnixTimestamp(); \\\n\t\t\tSerializer.Serialize(TEXT(JsonxName), UnixTimestampValue); \\\n\t\t}\n\n#define JSONX_SERIALIZE_DATETIME_UNIX_TIMESTAMP_MILLISECONDS(JsonxName, JsonxDateTime) \\\nif (Serializer.IsLoading()) \\\n{ \\\n\tint64 UnixTimestampValueInMilliseconds; \\\n\tSerializer.Serialize(TEXT(JsonxName), UnixTimestampValueInMilliseconds); \\\n\tJsonxDateTime = FDateTime::FromUnixTimestamp(UnixTimestampValueInMilliseconds / 1000); \\\n} \\\nelse \\\n{ \\\n\tint64 UnixTimestampValueInMilliseconds = JsonxDateTime.ToUnixTimestamp() * 1000; \\\n\tSerializer.Serialize(TEXT(JsonxName), UnixTimestampValueInMilliseconds); \\\n}\n\n#define JSONX_SERIALIZE_ENUM(JsonxName, JsonxEnum) \\\nif (Serializer.IsLoading()) \\\n{ \\\n\tFString JsonxTextValue; \\\n\tSerializer.Serialize(TEXT(JsonxName), JsonxTextValue); \\\n\tLexFromString(JsonxEnum, *JsonxTextValue); \\\n} \\\nelse \\\n{ \\\n\tFString JsonxTextValue = LexToString(JsonxEnum); \\\n\tSerializer.Serialize(TEXT(JsonxName), JsonxTextValue); \\\n}\n\n\nstruct FJsonxSerializerBase;\n\n/** Array of data */\ntypedef TArray<FString> FJsonxSerializableArray;\ntypedef TArray<int32> FJsonxSerializableArrayInt;\n\n/** Maps a key to a value */\ntypedef TMap<FString, FString> FJsonxSerializableKeyValueMap;\ntypedef TMap<FString, int32> FJsonxSerializableKeyValueMapInt;\ntypedef TMap<FString, int64> FJsonxSerializableKeyValueMapInt64;\ntypedef TMap<FString, float> FJsonxSerializableKeyValueMapFloat;\n\n/**\n * Base interface used to serialize to/from JSONX. Hides the fact there are separate read/write classes\n */\nstruct FJsonxSerializerBase\n{\n\tvirtual bool IsLoading() const = 0;\n\tvirtual bool IsSaving() const = 0;\n\tvirtual void StartObject() = 0;\n\tvirtual void StartObject(const FString& Name) = 0;\n\tvirtual void EndObject() = 0;\n\tvirtual void StartArray() = 0;\n\tvirtual void StartArray(const FString& Name) = 0;\n\tvirtual void EndArray() = 0;\n\tvirtual void Serialize(const TCHAR* Name, int32& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, uint32& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, int64& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, bool& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, FString& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, FText& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, float& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, double& Value) = 0;\n\tvirtual void Serialize(const TCHAR* Name, FDateTime& Value) = 0;\n\tvirtual void SerializeArray(FJsonxSerializableArray& Array) = 0;\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArray& Value) = 0;\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArrayInt& Value) = 0;\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map) = 0;\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt& Map) = 0;\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt64& Map) = 0;\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapFloat& Map) = 0;\n\tvirtual void SerializeSimpleMap(FJsonxSerializableKeyValueMap& Map) = 0;\n\tvirtual void SerializeMapSafe(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map) = 0;\n\tvirtual TSharedPtr<FJsonxObject> GetObject() = 0;\n\tvirtual void WriteIdentifierPrefix(const TCHAR* Name) = 0;\n\tvirtual void WriteRawJSONXValue(const TCHAR* Value) = 0;\n};\n\n/**\n * Implements the abstract serializer interface hiding the underlying writer object\n */\ntemplate <class CharType = TCHAR, class PrintPolicy = TPrettyJsonxPrintPolicy<CharType> >\nclass FJsonxSerializerWriter :\n\tpublic FJsonxSerializerBase\n{\n\t/** The object to write the JSONX output to */\n\tTSharedRef<TJsonxWriter<CharType, PrintPolicy> > JsonxWriter;\n\npublic:\n\n\t/**\n\t * Initializes the writer object\n\t *\n\t * @param InJsonxWriter the object to write the JSONX output to\n\t */\n\tFJsonxSerializerWriter(TSharedRef<TJsonxWriter<CharType, PrintPolicy> > InJsonxWriter) :\n\t\tJsonxWriter(InJsonxWriter)\n\t{\n\t}\n\n\tvirtual ~FJsonxSerializerWriter()\n\t{\n\t}\n\n\t/** Is the JSONX being read from */\n\tvirtual bool IsLoading() const override { return false; }\n\t/** Is the JSONX being written to */\n\tvirtual bool IsSaving() const override { return true; }\n\t/** Access to the root object */\n\tvirtual TSharedPtr<FJsonxObject> GetObject() override { return TSharedPtr<FJsonxObject>(); }\n\n\t/**\n\t * Starts a new object \"{\"\n\t */\n\tvirtual void StartObject() override\n\t{\n\t\tJsonxWriter->WriteObjectStart();\n\t}\n\n\t/**\n\t * Starts a new object \"{\"\n\t */\n\tvirtual void StartObject(const FString& Name) override\n\t{\n\t\tJsonxWriter->WriteObjectStart(Name);\n\t}\n\t/**\n\t * Completes the definition of an object \"}\"\n\t */\n\tvirtual void EndObject() override\n\t{\n\t\tJsonxWriter->WriteObjectEnd();\n\t}\n\n\tvirtual void StartArray() override\n\t{\n\t\tJsonxWriter->WriteArrayStart();\n\t}\n\n\tvirtual void StartArray(const FString& Name) override\n\t{\n\t\tJsonxWriter->WriteArrayStart(Name);\n\t}\n\n\tvirtual void EndArray() override\n\t{\n\t\tJsonxWriter->WriteArrayEnd();\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, int32& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, uint32& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, static_cast<int64>(Value));\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, int64& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, bool& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, FString& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, FText& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value.ToString());\n\t}\n\t/**\n\t * Writes the field name and the corresponding value to the JSONX data\n\t *\n\t * @param Name the field name to write out\n\t * @param Value the value to write out\n\t */\n\tvirtual void Serialize(const TCHAR* Name, float& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t* Writes the field name and the corresponding value to the JSONX data\n\t*\n\t* @param Name the field name to write out\n\t* @param Value the value to write out\n\t*/\n\tvirtual void Serialize(const TCHAR* Name, double& Value) override\n\t{\n\t\tJsonxWriter->WriteValue(Name, Value);\n\t}\n\t/**\n\t* Writes the field name and the corresponding value to the JSONX data\n\t*\n\t* @param Name the field name to write out\n\t* @param Value the value to write out\n\t*/\n\tvirtual void Serialize(const TCHAR* Name, FDateTime& Value) override\n\t{\n\t\tif (Value.GetTicks() > 0)\n\t\t{\n\t\t\tJsonxWriter->WriteValue(Name, Value.ToIso8601());\n\t\t}\n\t}\n\t/**\n\t * Serializes an array of values\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(FJsonxSerializableArray& Array) override\n\t{\n\t\tJsonxWriter->WriteArrayStart();\n\t\t// Iterate all of values\n\t\tfor (FJsonxSerializableArray::TIterator ArrayIt(Array); ArrayIt; ++ArrayIt)\n\t\t{\n\t\t\tJsonxWriter->WriteValue(*ArrayIt);\n\t\t}\n\t\tJsonxWriter->WriteArrayEnd();\n\t}\n\t/**\n\t * Serializes an array of values with an identifier\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArray& Array) override\n\t{\n\t\tJsonxWriter->WriteArrayStart(Name);\n\t\t// Iterate all of values\n\t\tfor (FJsonxSerializableArray::ElementType& Item :  Array)\n\t\t{\n\t\t\tJsonxWriter->WriteValue(Item);\n\t\t}\n\t\tJsonxWriter->WriteArrayEnd();\n\t}\n\t/**\n\t * Serializes an array of values with an identifier\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArrayInt& Array) override\n\t{\n\t\tJsonxWriter->WriteArrayStart(Name);\n\t\t// Iterate all of values\n\t\tfor (FJsonxSerializableArrayInt::ElementType& Item : Array)\n\t\t{\n\t\t\tJsonxWriter->WriteValue(Item);\n\t\t}\n\t\tJsonxWriter->WriteArrayEnd();\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map) override\n\t{\n\t\tJsonxWriter->WriteObjectStart(Name);\n\t\t// Iterate all of the keys and their values\n\t\tfor (FJsonxSerializableKeyValueMap::ElementType& Pair : Map)\n\t\t{\n\t\t\tSerialize(*Pair.Key, Pair.Value);\n\t\t}\n\t\tJsonxWriter->WriteObjectEnd();\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt& Map) override\n\t{\n\t\tJsonxWriter->WriteObjectStart(Name);\n\t\t// Iterate all of the keys and their values\n\t\tfor (FJsonxSerializableKeyValueMapInt::ElementType& Pair : Map)\n\t\t{\n\t\t\tSerialize(*Pair.Key, Pair.Value);\n\t\t}\n\t\tJsonxWriter->WriteObjectEnd();\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt64& Map) override\n\t{\n\t\tJsonxWriter->WriteObjectStart(Name);\n\t\t// Iterate all of the keys and their values\n\t\tfor (FJsonxSerializableKeyValueMapInt64::ElementType& Pair : Map)\n\t\t{\n\t\t\tSerialize(*Pair.Key, Pair.Value);\n\t\t}\n\t\tJsonxWriter->WriteObjectEnd();\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapFloat& Map) override\n\t{\n\t\tJsonxWriter->WriteObjectStart(Name);\n\t\t// Iterate all of the keys and their values\n\t\tfor (FJsonxSerializableKeyValueMapFloat::ElementType& Pair : Map)\n\t\t{\n\t\t\tSerialize(*Pair.Key, Pair.Value);\n\t\t}\n\t\tJsonxWriter->WriteObjectEnd();\n\t}\n\n\tvirtual void SerializeSimpleMap(FJsonxSerializableKeyValueMap& Map) override\n\t{\n\t\t// writing does nothing here, this is meant to read in all data from a json object \n\t\t// writing is explicitly handled per key/type\n\t}\n\n\t/**\n\t * Serializes keys and values from an object into a map.\n\t *\n\t * @param Name Name of property to serialize\n\t * @param Map The Map to copy String values from\n\t */\n\tvirtual void SerializeMapSafe(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map)\n\t{\n\t\tSerializeMap(Name, Map);\n\t}\n\n\tvirtual void WriteIdentifierPrefix(const TCHAR* Name)\n\t{\n\t\tJsonxWriter->WriteIdentifierPrefix(Name);\n\t}\n\n\tvirtual void WriteRawJSONXValue(const TCHAR* Value)\n\t{\n\t\tJsonxWriter->WriteRawJSONXValue(Value);\n\t}\n};\n\n/**\n * Implements the abstract serializer interface hiding the underlying reader object\n */\nclass FJsonxSerializerReader :\n\tpublic FJsonxSerializerBase\n{\n\t/** The object that holds the parsed JSONX data */\n\tTSharedPtr<FJsonxObject> JsonxObject;\n\npublic:\n\t/**\n\t * Inits the base JSONX object that is being read from\n\t *\n\t * @param InJsonxObject the JSONX object to serialize from\n\t */\n\tFJsonxSerializerReader(TSharedPtr<FJsonxObject> InJsonxObject) :\n\t\tJsonxObject(InJsonxObject)\n\t{\n\t}\n\n\tvirtual ~FJsonxSerializerReader()\n\t{\n\t}\n\n\t/** Is the JSONX being read from */\n\tvirtual bool IsLoading() const override { return true; }\n\t/** Is the JSONX being written to */\n\tvirtual bool IsSaving() const override { return false; }\n\t/** Access to the root Jsonx object being read */\n\tvirtual TSharedPtr<FJsonxObject> GetObject() override { return JsonxObject; }\n\n\t/** Ignored */\n\tvirtual void StartObject() override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/** Ignored */\n\tvirtual void StartObject(const FString& Name) override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/** Ignored */\n\tvirtual void EndObject() override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/** Ignored */\n\tvirtual void StartArray() override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/** Ignored */\n\tvirtual void StartArray(const FString& Name) override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/** Ignored */\n\tvirtual void EndArray() override\n\t{\n\t\t// Empty on purpose\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, int32& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Number>(Name))\n\t\t{\n\t\t\tJsonxObject->TryGetNumberField(Name, Value);\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, uint32& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Number>(Name))\n\t\t{\n\t\t\tJsonxObject->TryGetNumberField(Name, Value);\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, int64& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Number>(Name))\n\t\t{\n\t\t\tJsonxObject->TryGetNumberField(Name, Value);\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, bool& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Boolean>(Name))\n\t\t{\n\t\t\tValue = JsonxObject->GetBoolField(Name);\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, FString& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::String>(Name))\n\t\t{\n\t\t\tValue = JsonxObject->GetStringField(Name);\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, FText& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::String>(Name))\n\t\t{\n\t\t\tValue = FText::FromString(JsonxObject->GetStringField(Name));\n\t\t}\n\t}\n\t/**\n\t * If the underlying json object has the field, it is read into the value\n\t *\n\t * @param Name the name of the field to read\n\t * @param Value the out value to read the data into\n\t */\n\tvirtual void Serialize(const TCHAR* Name, float& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Number>(Name))\n\t\t{\n\t\t\tValue = (float)JsonxObject->GetNumberField(Name);\n\t\t}\n\t}\n\t/**\n\t* If the underlying json object has the field, it is read into the value\n\t*\n\t* @param Name the name of the field to read\n\t* @param Value the out value to read the data into\n\t*/\n\tvirtual void Serialize(const TCHAR* Name, double& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Number>(Name))\n\t\t{\n\t\t\tValue = JsonxObject->GetNumberField(Name);\n\t\t}\n\t}\n\t/**\n\t* Writes the field name and the corresponding value to the JSONX data\n\t*\n\t* @param Name the field name to write out\n\t* @param Value the value to write out\n\t*/\n\tvirtual void Serialize(const TCHAR* Name, FDateTime& Value) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::String>(Name))\n\t\t{\n\t\t\tFDateTime::ParseIso8601(*JsonxObject->GetStringField(Name), Value);\n\t\t}\n\t}\n\t/**\n\t * Serializes an array of values\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(FJsonxSerializableArray& Array) override\n\t{\n\t\t// @todo - higher level serialization is expecting a Jsonx Object\n\t\tcheck(0 && TEXT(\"Not implemented\"));\n\t}\n\t/**\n\t * Serializes an array of values with an identifier\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArray& Array) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Array>(Name))\n\t\t{\n\t\t\tTArray< TSharedPtr<FJsonxValue> > JsonxArray = JsonxObject->GetArrayField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (TSharedPtr<FJsonxValue>& Value : JsonxArray)\n\t\t\t{\n\t\t\t\tArray.Add(Value->AsString());\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * Serializes an array of values with an identifier\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Array the array to serialize\n\t */\n\tvirtual void SerializeArray(const TCHAR* Name, FJsonxSerializableArrayInt& Array) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Array>(Name))\n\t\t{\n\t\t\tTArray< TSharedPtr<FJsonxValue> > JsonxArray = JsonxObject->GetArrayField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (TSharedPtr<FJsonxValue>& Value : JsonxArray)\n\t\t\t{\n\t\t\t\tArray.Add(Value->AsNumber());\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Object>(Name))\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> JsonxMap = JsonxObject->GetObjectField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& Pair : JsonxMap->Values)\n\t\t\t{\n\t\t\t\tMap.Add(Pair.Key, Pair.Value->AsString());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt& Map) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Object>(Name))\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> JsonxMap = JsonxObject->GetObjectField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& Pair : JsonxMap->Values)\n\t\t\t{\n\t\t\t\tconst int32 Value = (int32)Pair.Value->AsNumber();\n\t\t\t\tMap.Add(Pair.Key, Value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapInt64& Map) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Object>(Name))\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> JsonxMap = JsonxObject->GetObjectField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& Pair : JsonxMap->Values)\n\t\t\t{\n\t\t\t\tconst int64 Value = (int64)Pair.Value->AsNumber();\n\t\t\t\tMap.Add(Pair.Key, Value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Serializes the keys & values for map\n\t *\n\t * @param Name the name of the property to serialize\n\t * @param Map the map to serialize\n\t */\n\tvirtual void SerializeMap(const TCHAR* Name, FJsonxSerializableKeyValueMapFloat& Map) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Object>(Name))\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> JsonxMap = JsonxObject->GetObjectField(Name);\n\t\t\t// Iterate all of the keys and their values\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& Pair : JsonxMap->Values)\n\t\t\t{\n\t\t\t\tconst float Value = (float)Pair.Value->AsNumber();\n\t\t\t\tMap.Add(Pair.Key, Value);\n\t\t\t}\n\t\t}\n\t}\n\n\tvirtual void SerializeSimpleMap(FJsonxSerializableKeyValueMap& Map) override\n\t{\n\t\t// Iterate all of the keys and their values, only taking simple types (not array/object), all in string form\n\t\tfor (auto KeyValueIt = JsonxObject->Values.CreateConstIterator(); KeyValueIt; ++KeyValueIt)\n\t\t{\n\t\t\tFString Value;\n\t\t\tif (KeyValueIt.Value()->TryGetString(Value))\n\t\t\t{\n\t\t\t\tMap.Add(KeyValueIt.Key(), MoveTemp(Value));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deserializes keys and values from an object into a map, but only if the value is trivially convertable to string.\n\t *\n\t * @param Name Name of property to deserialize\n\t * @param Map The Map to fill with String values found\n\t */\n\tvirtual void SerializeMapSafe(const TCHAR* Name, FJsonxSerializableKeyValueMap& Map) override\n\t{\n\t\tif (JsonxObject->HasTypedField<EJsonx::Object>(Name))\n\t\t{\n\t\t\t// Iterate all of the keys and their values, only taking simple types (not array/object), all in string form\n\t\t\tTSharedPtr<FJsonxObject> JsonxMap = JsonxObject->GetObjectField(Name);\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& Pair : JsonxMap->Values)\n\t\t\t{\n\t\t\t\tFString Value;\n\t\t\t\tif (Pair.Value->TryGetString(Value))\n\t\t\t\t{\n\t\t\t\t\tMap.Add(Pair.Key, MoveTemp(Value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvirtual void WriteIdentifierPrefix(const TCHAR* Name)\n\t{\n\t\t// Should never be called on a reader\n\t\tcheck(false);\n\t}\n\n\tvirtual void WriteRawJSONXValue(const TCHAR* Value)\n\t{\n\t\t// Should never be called on a reader\n\t\tcheck(false);\n\t}\n};\n\n/**\n * Base class for a JSONX serializable object\n */\nstruct FJsonxSerializable\n{\n\t/**\n\t *\tVirtualize destructor as we provide overridable functions\n\t */\n\tvirtual ~FJsonxSerializable() {}\n\n\t/**\n\t * Used to allow serialization of a const ref\n\t *\n\t * @return the corresponding json string\n\t */\n\tinline const FString ToJsonx(bool bPrettyPrint = true) const\n\t{\n\t\t// Strip away const, because we use a single method that can read/write which requires non-const semantics\n\t\t// Otherwise, we'd have to have 2 separate macros for declaring const to json and non-const from json\n\t\treturn ((FJsonxSerializable*)this)->ToJsonx(bPrettyPrint);\n\t}\n\t/**\n\t * Serializes this object to its JSONX string form\n\t *\n\t * @param bPrettyPrint - If true, will use the pretty json formatter\n\t * @return the corresponding json string\n\t */\n\tvirtual const FString ToJsonx(bool bPrettyPrint=true)\n\t{\n\t\tFString JsonxStr;\n\t\tif (bPrettyPrint)\n\t\t{\n\t\t\tTSharedRef<TJsonxWriter<> > JsonxWriter = TJsonxWriterFactory<>::Create(&JsonxStr);\n\t\t\tFJsonxSerializerWriter<> Serializer(JsonxWriter);\n\t\t\tSerialize(Serializer, false);\n\t\t\tJsonxWriter->Close();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTSharedRef< TJsonxWriter< TCHAR, TCondensedJsonxPrintPolicy< TCHAR > > > JsonxWriter = TJsonxWriterFactory< TCHAR, TCondensedJsonxPrintPolicy< TCHAR > >::Create( &JsonxStr );\n\t\t\tFJsonxSerializerWriter<TCHAR, TCondensedJsonxPrintPolicy< TCHAR >> Serializer(JsonxWriter);\n\t\t\tSerialize(Serializer, false);\n\t\t\tJsonxWriter->Close();\n\t\t}\n\t\treturn JsonxStr;\n\t}\n\tvirtual void ToJsonx(TSharedRef<TJsonxWriter<> >& JsonxWriter, bool bFlatObject) const\n\t{\n\t\tFJsonxSerializerWriter<> Serializer(JsonxWriter);\n\t\t((FJsonxSerializable*)this)->Serialize(Serializer, bFlatObject);\n\t}\n\tvirtual void ToJsonx(TSharedRef< TJsonxWriter< TCHAR, TCondensedJsonxPrintPolicy< TCHAR > > >& JsonxWriter, bool bFlatObject) const\n\t{\n\t\tFJsonxSerializerWriter<TCHAR, TCondensedJsonxPrintPolicy< TCHAR >> Serializer(JsonxWriter);\n\t\t((FJsonxSerializable*)this)->Serialize(Serializer, bFlatObject);\n\t}\n\n\t/**\n\t * Serializes the contents of a JSONX string into this object\n\t *\n\t * @param Jsonx the JSONX data to serialize from\n\t */\n\tvirtual bool FromJsonx(const FString& Jsonx)\n\t{\n\t\treturn FromJsonx(CopyTemp(Jsonx));\n\t}\n\n\t/**\n\t * Serializes the contents of a JSONX string into this object\n\t *\n\t * @param Jsonx the JSONX data to serialize from\n\t */\n\tvirtual bool FromJsonx(FString&& Jsonx)\n\t{\n\t\tTSharedPtr<FJsonxObject> JsonxObject;\n\t\tTSharedRef<TJsonxReader<> > JsonxReader = TJsonxReaderFactory<>::Create(MoveTemp(Jsonx));\n\t\tif (FJsonxSerializer::Deserialize(JsonxReader,JsonxObject) &&\n\t\t\tJsonxObject.IsValid())\n\t\t{\n\t\t\tFJsonxSerializerReader Serializer(JsonxObject);\n\t\t\tSerialize(Serializer, false);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tvirtual bool FromJsonx(TSharedPtr<FJsonxObject> JsonxObject)\n\t{\n\t\tif (JsonxObject.IsValid())\n\t\t{\n\t\t\tFJsonxSerializerReader Serializer(JsonxObject);\n\t\t\tSerialize(Serializer, false);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Abstract method that needs to be supplied using the macros\n\t *\n\t * @param Serializer the object that will perform serialization in/out of JSONX\n\t * @param bFlatObject if true then no object wrapper is used\n\t */\n\tvirtual void Serialize(FJsonxSerializerBase& Serializer, bool bFlatObject) = 0;\n};\n\n/**\n * Useful if you just want access to the underlying FJsonxObject (for cases where the schema is loose or an outer system will do further de/serialization)\n */\nstruct FJsonxDataBag\n\t: public FJsonxSerializable\n{\n\tvirtual void Serialize(FJsonxSerializerBase& Serializer, bool bFlatObject) override\n\t{\n\t\tif (Serializer.IsLoading())\n\t\t{\n\t\t\t// just grab a reference to the underlying JSONX object\n\t\t\tJsonxObject = Serializer.GetObject();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!bFlatObject)\n\t\t\t{\n\t\t\t\tSerializer.StartObject();\n\t\t\t}\n\n\t\t\tif (JsonxObject.IsValid())\n\t\t\t{\n\t\t\t\tfor (const auto& It : JsonxObject->Values)\n\t\t\t\t{\n\t\t\t\t\tTSharedPtr<FJsonxValue> JsonxValue = It.Value;\n\t\t\t\t\tif (JsonxValue.IsValid())\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (JsonxValue->Type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase EJsonx::Boolean:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tauto Value = JsonxValue->AsBool();\n\t\t\t\t\t\t\t\tSerializer.Serialize(*It.Key, Value);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase EJsonx::Number:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tauto Value = JsonxValue->AsNumber();\n\t\t\t\t\t\t\t\tSerializer.Serialize(*It.Key, Value);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase EJsonx::String:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tauto Value = JsonxValue->AsString();\n\t\t\t\t\t\t\t\tSerializer.Serialize(*It.Key, Value);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase EJsonx::Array:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// if we have an array, serialize to string and write raw\n\t\t\t\t\t\t\t\tFString JsonxStr;\n\t\t\t\t\t\t\t\tauto Writer = TJsonxWriterFactory<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>::Create(&JsonxStr);\n\t\t\t\t\t\t\t\tFJsonxSerializer::Serialize(JsonxValue->AsArray(), Writer);\n\t\t\t\t\t\t\t\tSerializer.WriteIdentifierPrefix(*It.Key);\n\t\t\t\t\t\t\t\tSerializer.WriteRawJSONXValue(*JsonxStr);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase EJsonx::Object:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// if we have an object, serialize to string and write raw\n\t\t\t\t\t\t\t\tFString JsonxStr;\n\t\t\t\t\t\t\t\tauto Writer = TJsonxWriterFactory<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>::Create(&JsonxStr);\n\t\t\t\t\t\t\t\tFJsonxSerializer::Serialize(JsonxValue->AsObject().ToSharedRef(), Writer);\n\t\t\t\t\t\t\t\t// too bad there's no JsonxObject serialization method on FJsonxSerializerBase directly :-/\n\t\t\t\t\t\t\t\tSerializer.WriteIdentifierPrefix(*It.Key);\n\t\t\t\t\t\t\t\tSerializer.WriteRawJSONXValue(*JsonxStr);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!bFlatObject)\n\t\t\t{\n\t\t\t\tSerializer.EndObject();\n\t\t\t}\n\t\t}\n\t}\n\n\tdouble GetDouble(const FString& Key) const\n\t{\n\t\tconst auto Jsonx = GetField(Key);\n\t\treturn Jsonx.IsValid() ? Jsonx->AsNumber() : 0.0;\n\t}\n\n\tFString GetString(const FString& Key) const\n\t{\n\t\tconst auto Jsonx = GetField(Key);\n\t\treturn Jsonx.IsValid() ? Jsonx->AsString() : FString();\n\t}\n\n\tbool GetBool(const FString& Key) const\n\t{\n\t\tconst auto Jsonx = GetField(Key);\n\t\treturn Jsonx.IsValid() ? Jsonx->AsBool() : false;\n\t}\n\n\tTSharedPtr<const FJsonxValue> GetField(const FString& Key) const\n\t{\n\t\tif (JsonxObject.IsValid())\n\t\t{\n\t\t\treturn JsonxObject->TryGetField(Key);\n\t\t}\n\t\treturn TSharedPtr<const FJsonxValue>();\n\t}\n\n\ttemplate<typename JSONX_TYPE, typename Arg>\n\tvoid SetField(const FString& Key, Arg&& Value)\n\t{\n\t\tSetFieldJsonx(Key, MakeShared<JSONX_TYPE>(MoveTempIfPossible(Value)));\n\t}\n\n\tvoid SetFieldJsonx(const FString& Key, const TSharedPtr<FJsonxValue>& Value)\n\t{\n\t\tif (!JsonxObject.IsValid())\n\t\t{\n\t\t\tJsonxObject = MakeShared<FJsonxObject>();\n\t\t}\n\t\tJsonxObject->SetField(Key, Value);\n\t}\n\npublic:\n\tTSharedPtr<FJsonxObject> JsonxObject;\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Serialization/JsonxTypes.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n\nclass Error;\n\n/**\n * Jsonx (JavaScript Object Notation) is a lightweight data-interchange format.\n * Information on how it works can be found here: http://www.json.org/.\n * This code was written from scratch with only the Jsonx spec as a guide.\n *\n * In order to use Jsonx effectively, you need to be familiar with the Object/Value\n * hierarchy, and you should use the FJsonxObject class and FJsonxValue subclasses.\n */\n\n/**\n * Represents all the types a Jsonx Value can be.\n */\nenum class EJsonx\n{\n\tNone,\n\tNull,\n\tString,\n\tNumber,\n\tBoolean,\n\tArray,\n\tObject\n};\n\n\nenum class EJsonxToken\n{\n\tNone,\n\tComma,\n\tCurlyOpen,\n\tCurlyClose,\n\tSquareOpen,\n\tSquareClose,\n\tColon,\n\tString,\n\n\t// short values\n\tNumber,\n\tTrue,\n\tFalse,\n\tNull,\n\n\tIdentifier\n};\n\nFORCEINLINE bool EJsonxToken_IsShortValue(EJsonxToken Token)\n{\n\treturn Token >= EJsonxToken::Number && Token <= EJsonxToken::Null;\n}\n\nenum class EJsonxNotation\n{\n\tObjectStart,\n\tObjectEnd,\n\tArrayStart,\n\tArrayEnd,\n\tBoolean,\n\tString,\n\tNumber,\n\tNull,\n\tError\n};\n"
  },
  {
    "path": "Source/Jsonx/Public/Serialization/JsonxWriter.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"GenericPlatform/GenericPlatformMath.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Policies/PrettyJsonxPrintPolicy.h\"\n#include \"Serialization/MemoryWriter.h\"\n\n#define JSONX_LOW_PRECISION\n\n/**\n * Takes an input string and escapes it so it can be written as a valid Jsonx string. Also adds the quotes.\n * Appends to a given string-like object to avoid reallocations.\n * String-like object must support operator+=(const TCHAR*) and operation+=(TCHAR)\n *\n * @param AppendTo the string to append to.\n * @param StringVal the string to escape\n * @return the AppendTo string for convenience.\n */\ntemplate<typename StringType>\ninline StringType& AppendEscapeJsonxString(StringType& AppendTo, const FString& StringVal)\n{\n\tAppendTo += TEXT(\"\\\"\");\n\tfor (const TCHAR* Char = *StringVal; *Char != TCHAR('\\0'); ++Char)\n\t{\n\t\tswitch (*Char)\n\t\t{\n\t\tcase TCHAR('\\\\'): AppendTo += TEXT(\"\\\\\\\\\"); break;\n\t\tcase TCHAR('\\n'): AppendTo += TEXT(\"\\\\n\"); break;\n\t\tcase TCHAR('\\t'): AppendTo += TEXT(\"\\\\t\"); break;\n\t\tcase TCHAR('\\b'): AppendTo += TEXT(\"\\\\b\"); break;\n\t\tcase TCHAR('\\f'): AppendTo += TEXT(\"\\\\f\"); break;\n\t\tcase TCHAR('\\r'): AppendTo += TEXT(\"\\\\r\"); break;\n\t\tcase TCHAR('\\\"'): AppendTo += TEXT(\"\\\\\\\"\"); break;\n\t\tdefault:\n\t\t\t// Must escape control characters\n\t\t\tif (*Char >= TCHAR(32))\n\t\t\t{\n\t\t\t\tAppendTo += *Char;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tAppendTo.Appendf(TEXT(\"\\\\u%04x\"), *Char);\n\t\t\t}\n\t\t}\n\t}\n\tAppendTo += TEXT(\"\\\"\");\n\n\treturn AppendTo;\n}\n\n/**\n * Takes an input string and escapes it so it can be written as a valid Jsonx string. Also adds the quotes.\n *\n * @param StringVal the string to escape\n * @return the given string, escaped to produce a valid Jsonx string.\n */\ninline FString EscapeJsonxString(const FString& StringVal)\n{\n\tFString Result;\n\treturn AppendEscapeJsonxString(Result, StringVal);\n}\n\n/**\n * Template for Jsonx writers.\n *\n * @param CharType The type of characters to print, i.e. TCHAR or ANSICHAR.\n * @param PrintPolicy The print policy to use when writing the output string (default = TPrettyJsonxPrintPolicy).\n */\ntemplate <class CharType = TCHAR, class PrintPolicy = TPrettyJsonxPrintPolicy<CharType> >\nclass TJsonxWriter\n{\npublic:\n\n\tstatic TSharedRef< TJsonxWriter > Create( FArchive* const Stream, int32 InitialIndentLevel = 0 )\n\t{\n\t\treturn MakeShareable( new TJsonxWriter< CharType, PrintPolicy >( Stream, InitialIndentLevel ) );\n\t}\n\npublic:\n\n\tvirtual ~TJsonxWriter() { }\n\n\tFORCEINLINE int32 GetIndentLevel() const { return IndentLevel; }\n\n\tbool CanWriteObjectStart() const\n\t{\n\t\treturn CanWriteObjectWithoutIdentifier();\n\t}\n\n\tvoid WriteObjectStart()\n\t{\n\t\tcheck(CanWriteObjectWithoutIdentifier());\n\t\tif (PreviousTokenWritten != EJsonxToken::None )\n\t\t{\n\t\t\tWriteCommaIfNeeded();\n\t\t}\n\n\t\tif ( PreviousTokenWritten != EJsonxToken::None )\n\t\t{\n\t\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\t}\n\n\t\tPrintPolicy::WriteChar(Stream, CharType('{'));\n\t\t++IndentLevel;\n\t\tStack.Push( EJsonx::Object );\n\t\tPreviousTokenWritten = EJsonxToken::CurlyOpen;\n\t}\n\n\tvoid WriteObjectStart( const FString& Identifier )\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Object );\n\t\tWriteIdentifier( Identifier );\n\n\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\tPrintPolicy::WriteChar(Stream, CharType('{'));\n\t\t++IndentLevel;\n\t\tStack.Push( EJsonx::Object );\n\t\tPreviousTokenWritten = EJsonxToken::CurlyOpen;\n\t}\n\n\tvoid WriteObjectEnd()\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Object );\n\n\t\tPrintPolicy::WriteLineTerminator(Stream);\n\n\t\t--IndentLevel;\n\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\tPrintPolicy::WriteChar(Stream, CharType('}'));\n\t\tStack.Pop();\n\t\tPreviousTokenWritten = EJsonxToken::CurlyClose;\n\t}\n\n\tvoid WriteArrayStart()\n\t{\n\t\tcheck(CanWriteValueWithoutIdentifier());\n\t\tif ( PreviousTokenWritten != EJsonxToken::None )\n\t\t{\n\t\t\tWriteCommaIfNeeded();\n\t\t}\n\n\t\tif ( PreviousTokenWritten != EJsonxToken::None )\n\t\t{\n\t\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\t}\n\n\t\tPrintPolicy::WriteChar(Stream, CharType('['));\n\t\t++IndentLevel;\n\t\tStack.Push( EJsonx::Array );\n\t\tPreviousTokenWritten = EJsonxToken::SquareOpen;\n\t}\n\n\tvoid WriteArrayStart( const FString& Identifier )\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Object );\n\t\tWriteIdentifier( Identifier );\n\n\t\tPrintPolicy::WriteSpace( Stream );\n\t\tPrintPolicy::WriteChar(Stream, CharType('['));\n\t\t++IndentLevel;\n\t\tStack.Push( EJsonx::Array );\n\t\tPreviousTokenWritten = EJsonxToken::SquareOpen;\n\t}\n\n\tvoid WriteArrayEnd()\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Array );\n\n\t\t--IndentLevel;\n\t\tif ( PreviousTokenWritten == EJsonxToken::SquareClose || PreviousTokenWritten == EJsonxToken::CurlyClose || PreviousTokenWritten == EJsonxToken::String )\n\t\t{\n\t\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\t}\n\t\telse if ( PreviousTokenWritten != EJsonxToken::SquareOpen )\n\t\t{\n\t\t\tPrintPolicy::WriteSpace( Stream );\n\t\t}\n\n\t\tPrintPolicy::WriteChar(Stream, CharType(']'));\n\t\tStack.Pop();\n\t\tPreviousTokenWritten = EJsonxToken::SquareClose;\n\t}\n\n\ttemplate <class FValue>\n\tvoid WriteValue(FValue Value)\n\t{\n\t\tcheck(CanWriteValueWithoutIdentifier());\n\t\tWriteCommaIfNeeded();\n\n\t\tif (PreviousTokenWritten == EJsonxToken::SquareOpen || EJsonxToken_IsShortValue(PreviousTokenWritten))\n\t\t{\n\t\t\tPrintPolicy::WriteSpace( Stream );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\t}\n\n\t\tPreviousTokenWritten = WriteValueOnly( Value );\n\t}\n\n\tvoid WriteValue(const FString& Value)\n\t{\n\t\tcheck(CanWriteValueWithoutIdentifier());\n\t\tWriteCommaIfNeeded();\n\n\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\tPreviousTokenWritten = WriteValueOnly(Value);\n\t}\n\n\ttemplate <class FValue>\n\tvoid WriteValue(const FString& Identifier, FValue Value)\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Object );\n\t\tWriteIdentifier( Identifier );\n\n\t\tPrintPolicy::WriteSpace(Stream);\n\t\tPreviousTokenWritten = WriteValueOnly(MoveTemp(Value));\n\t}\n\n\ttemplate<class ElementType>\n\tvoid WriteValue(const FString& Identifier, const TArray<ElementType>& Array)\n\t{\n\t\tWriteArrayStart(Identifier);\n\t\tfor (int Idx = 0; Idx < Array.Num(); Idx++)\n\t\t{\n\t\t\tWriteValue(Array[Idx]);\n\t\t}\n\t\tWriteArrayEnd();\n\t}\n\n\tvoid WriteValue(const FString& Identifier, const TCHAR* Value)\n\t{\n\t\tWriteValue(Identifier, FString(Value));\n\t}\n\n\t// WARNING: THIS IS DANGEROUS. Use this only if you know for a fact that the Value is valid JSONX!\n\t// Use this to insert the results of a different JSONX Writer in.\n\tvoid WriteRawJSONXValue( const FString& Identifier, const FString& Value )\n\t{\n\t\tcheck( Stack.Top() == EJsonx::Object );\n\t\tWriteIdentifier( Identifier );\n\n\t\tPrintPolicy::WriteSpace(Stream);\n\t\tPrintPolicy::WriteString(Stream, Value);\n\t\tPreviousTokenWritten = EJsonxToken::String;\n\t}\n\n\tvoid WriteNull( const FString& Identifier )\n\t{\n\t\tWriteValue(Identifier, nullptr);\n\t}\n\n\tvoid WriteValue( const TCHAR* Value )\n\t{\n\t\tWriteValue(FString(Value));\n\t}\n\n\t// WARNING: THIS IS DANGEROUS. Use this only if you know for a fact that the Value is valid JSONX!\n\t// Use this to insert the results of a different JSONX Writer in.\n\tvoid WriteRawJSONXValue( const FString& Value )\n\t{\n\t\tcheck(CanWriteValueWithoutIdentifier());\n\t\tWriteCommaIfNeeded();\n\n\t\tif ( PreviousTokenWritten != EJsonxToken::True && PreviousTokenWritten != EJsonxToken::False && PreviousTokenWritten != EJsonxToken::SquareOpen )\n\t\t{\n\t\t\tPrintPolicy::WriteLineTerminator(Stream);\n\t\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPrintPolicy::WriteSpace( Stream );\n\t\t}\n\n\t\tPrintPolicy::WriteString(Stream, Value);\n\t\tPreviousTokenWritten = EJsonxToken::String;\n\t}\n\n\tvoid WriteNull()\n\t{\n\t\tWriteValue(nullptr);\n\t}\n\n\tvirtual bool Close()\n\t{\n\t\treturn ( PreviousTokenWritten == EJsonxToken::None ||\n\t\t\t\t PreviousTokenWritten == EJsonxToken::CurlyClose  ||\n\t\t\t\t PreviousTokenWritten == EJsonxToken::SquareClose )\n\t\t\t\t&& Stack.Num() == 0;\n\t}\n\n\t/**\n\t * WriteValue(\"Foo\", Bar) should be equivalent to WriteIdentifierPrefix(\"Foo\"), WriteValue(Bar)\n\t */\n\tvoid WriteIdentifierPrefix(const FString& Identifier)\n\t{\n\t\tcheck(Stack.Top() == EJsonx::Object);\n\t\tWriteIdentifier(Identifier);\n\t\tPrintPolicy::WriteSpace(Stream);\n\t\tPreviousTokenWritten = EJsonxToken::Identifier;\n\t}\n\nprotected:\n\n\t/**\n\t * Creates and initializes a new instance.\n\t *\n\t * @param InStream An archive containing the input.\n\t * @param InitialIndentLevel The initial indentation level.\n\t */\n\tTJsonxWriter( FArchive* const InStream, int32 InitialIndentLevel )\n\t\t: Stream( InStream )\n\t\t, Stack()\n\t\t, PreviousTokenWritten(EJsonxToken::None)\n\t\t, IndentLevel(InitialIndentLevel)\n\t{ }\n\nprotected:\n\n\tFORCEINLINE bool CanWriteValueWithoutIdentifier() const\n\t{\n\t\treturn Stack.Num() <= 0 || Stack.Top() == EJsonx::Array || PreviousTokenWritten == EJsonxToken::Identifier;\n\t}\n\n\tFORCEINLINE bool CanWriteObjectWithoutIdentifier() const\n\t{\n\t\treturn Stack.Num() <= 0 || Stack.Top() == EJsonx::Array || PreviousTokenWritten == EJsonxToken::Identifier || PreviousTokenWritten == EJsonxToken::Colon;\n\t}\n\n\tFORCEINLINE void WriteCommaIfNeeded()\n\t{\n\t\tif ( PreviousTokenWritten != EJsonxToken::CurlyOpen && PreviousTokenWritten != EJsonxToken::SquareOpen && PreviousTokenWritten != EJsonxToken::Identifier)\n\t\t{\n\t\t\tPrintPolicy::WriteChar(Stream, CharType(','));\n\t\t}\n\t}\n\n\tFORCEINLINE void WriteIdentifier( const FString& Identifier )\n\t{\n\t\tWriteCommaIfNeeded();\n\t\tPrintPolicy::WriteLineTerminator(Stream);\n\n\t\tPrintPolicy::WriteTabs(Stream, IndentLevel);\n\t\tWriteStringValue( Identifier );\n\t\tPrintPolicy::WriteChar(Stream, CharType(':'));\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(bool Value)\n\t{\n\t\tPrintPolicy::WriteString(Stream, Value ? TEXT(\"true\") : TEXT(\"false\"));\n\t\treturn Value ? EJsonxToken::True : EJsonxToken::False;\n\t}\n\n#ifdef JSONX_LOW_PRECISION\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(float Value)\n\t{\n\t\tif (FGenericPlatformMath::IsFinite(Value))\n\t\t{\n\t\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%.7g\"), Value));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"Infinity\")));\n\t\t}\n\t\t//PrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%g\"), Value));\n\t\treturn EJsonxToken::Number;\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(double Value)\n\t{\n\t\t// Specify 17 significant digits, the most that can ever be useful from a double\n\t\t// In particular, this ensures large integers are written correctly\n\t\tif (FGenericPlatformMath::IsFinite(Value))\n\t\t{\n\t\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%.7g\"), Value));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"Infinity\")));\n\t\t}\n\n\t\treturn EJsonxToken::Number;\n\t}\n\n#else\n\tFORCEINLINE EJsonxToken WriteValueOnly(float Value)\n\t{\n\n\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%g\"), Value));\n\t\treturn EJsonxToken::Number;\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(double Value)\n\t{\n\t\t// Specify 17 significant digits, the most that can ever be useful from a double\n\t\t// In particular, this ensures large integers are written correctly\n\n\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%.17g\"), Value));\n\t\treturn EJsonxToken::Number;\n\t}\n#endif\n\n\n\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(int32 Value)\n\t{\n\t\treturn WriteValueOnly((int64)Value);\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(int64 Value)\n\t{\n\t\tPrintPolicy::WriteString(Stream, FString::Printf(TEXT(\"%lld\"), Value));\n\t\treturn EJsonxToken::Number;\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(TYPE_OF_NULLPTR)\n\t{\n\t\tPrintPolicy::WriteString(Stream, TEXT(\"null\"));\n\t\treturn EJsonxToken::Null;\n\t}\n\n\tFORCEINLINE EJsonxToken WriteValueOnly(const FString& Value)\n\t{\n\t\tWriteStringValue(Value);\n\t\treturn EJsonxToken::String;\n\t}\n\n\tvirtual void WriteStringValue( const FString& String )\n\t{\n\t\tFString OutString = EscapeJsonxString(String);\n\t\tPrintPolicy::WriteString(Stream, OutString);\n\t}\n\n\tFArchive* const Stream;\n\tTArray<EJsonx> Stack;\n\tEJsonxToken PreviousTokenWritten;\n\tint32 IndentLevel;\n};\n\n\ntemplate <class PrintPolicy = TPrettyJsonxPrintPolicy<TCHAR>>\nclass TJsonxStringWriter\n\t: public TJsonxWriter<TCHAR, PrintPolicy>\n{\npublic:\n\n\tstatic TSharedRef<TJsonxStringWriter> Create( FString* const InStream, int32 InitialIndent = 0 )\n\t{\n\t\treturn MakeShareable(new TJsonxStringWriter(InStream, InitialIndent));\n\t}\n\npublic:\n\n\tvirtual ~TJsonxStringWriter()\n\t{\n\t\tcheck(this->Stream->Close());\n\t\tdelete this->Stream;\n\t}\n\n\tvirtual bool Close() override\n\t{\n\t\tFString Out;\n\n\t\tfor (int32 i = 0; i < Bytes.Num(); i+=sizeof(TCHAR))\n\t\t{\n\t\t\tTCHAR* Char = static_cast<TCHAR*>(static_cast<void*>(&Bytes[i]));\n\t\t\tOut += *Char;\n\t\t}\n\n\t\t*OutString = Out;\n\n\t\treturn TJsonxWriter<TCHAR, PrintPolicy>::Close();\n\t}\n\nprotected:\n\n\tTJsonxStringWriter( FString* const InOutString, int32 InitialIndent )\n\t\t: TJsonxWriter<TCHAR, PrintPolicy>(new FMemoryWriter(Bytes), InitialIndent)\n\t\t, Bytes()\n\t\t, OutString(InOutString)\n\t{ }\n\nprivate:\n\n\tTArray<uint8> Bytes;\n\tFString* OutString;\n};\n\n\ntemplate <class CharType = TCHAR, class PrintPolicy = TPrettyJsonxPrintPolicy<CharType>>\nclass TJsonxWriterFactory\n{\npublic:\n\n\tstatic TSharedRef<TJsonxWriter<CharType, PrintPolicy>> Create( FArchive* const Stream, int32 InitialIndent = 0 )\n\t{\n\t\treturn TJsonxWriter< CharType, PrintPolicy >::Create(Stream, InitialIndent);\n\t}\n\n\tstatic TSharedRef<TJsonxWriter<TCHAR, PrintPolicy>> Create( FString* const Stream, int32 InitialIndent = 0 )\n\t{\n\t\treturn StaticCastSharedRef<TJsonxWriter<TCHAR, PrintPolicy>>(TJsonxStringWriter<PrintPolicy>::Create(Stream, InitialIndent));\n\t}\n};\n"
  },
  {
    "path": "Source/JsonxUtilities/JsonxUtilities.Build.cs",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\nusing UnrealBuildTool;\n\npublic class JsonxUtilities : ModuleRules\n{\n\tpublic JsonxUtilities( ReadOnlyTargetRules Target ) : base(Target)\n\t{\n\t\tPublicDependencyModuleNames.AddRange(\n\t\t\tnew string[]\n\t\t\t{\n\t\t\t\t\"Core\",\n\t\t\t\t\"CoreUObject\",\n\t\t\t\t\"Jsonx\",\n\t\t\t}\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "Source/JsonxUtilities/Private/JsonxObjectConverter.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"JsonxObjectConverter.h\"\n#include \"Internationalization/Culture.h\"\n#include \"UObject/ObjectMacros.h\"\n#include \"UObject/Class.h\"\n#include \"UObject/UnrealType.h\"\n#include \"UObject/EnumProperty.h\"\n#include \"UObject/TextProperty.h\"\n#include \"UObject/PropertyPortFlags.h\"\n#include \"UObject/Package.h\"\n#include \"Policies/CondensedJsonxPrintPolicy.h\"\n#include \"JsonxObjectWrapper.h\"\n\nFString FJsonxObjectConverter::StandardizeCase(const FString &StringIn)\n{\n\t// this probably won't work for all cases, consider downcaseing the string fully\n\tFString FixedString = StringIn;\n\tFixedString[0] = FChar::ToLower(FixedString[0]); // our json classes/variable start lower case\n\tFixedString.ReplaceInline(TEXT(\"ID\"), TEXT(\"Id\"), ESearchCase::CaseSensitive); // Id is standard instead of ID, some of our fnames use ID\n\treturn FixedString;\n}\n\n\nnamespace\n{\n\tconst FString ObjectClassNameKey = \"_ClassName\";\n\n/** Convert property to JSONX, assuming either the property is not an array or the value is an individual array element */\nTSharedPtr<FJsonxValue> ConvertScalarFPropertyToJsonxValue(FProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const FJsonxObjectConverter::CustomExportCallback* ExportCb, FProperty* OuterProperty)\n{\n\t// See if there's a custom export callback first, so it can override default behavior\n\tif (ExportCb && ExportCb->IsBound())\n\t{\n\t\tTSharedPtr<FJsonxValue> CustomValue = ExportCb->Execute(Property, Value);\n\t\tif (CustomValue.IsValid())\n\t\t{\n\t\t\treturn CustomValue;\n\t\t}\n\t\t// fall through to default cases\n\t}\n\n\tif (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))\n\t{\n\t\t// export enums as strings\n\t\tUEnum* EnumDef = EnumProperty->GetEnum();\n\t\tFString StringValue = EnumDef->GetNameStringByValue(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(Value));\n\t\treturn MakeShared<FJsonxValueString>(StringValue);\n\t}\n\telse if (FNumericProperty *NumericProperty = CastField<FNumericProperty>(Property))\n\t{\n\t\t// see if it's an enum\n\t\tUEnum* EnumDef = NumericProperty->GetIntPropertyEnum();\n\t\tif (EnumDef != NULL)\n\t\t{\n\t\t\t// export enums as strings\n\t\t\tFString StringValue = EnumDef->GetNameStringByValue(NumericProperty->GetSignedIntPropertyValue(Value));\n\t\t\treturn MakeShared<FJsonxValueString>(StringValue);\n\t\t}\n\n\t\t// We want to export numbers as numbers\n\t\tif (NumericProperty->IsFloatingPoint())\n\t\t{\n\t\t\treturn MakeShared<FJsonxValueNumber>(NumericProperty->GetFloatingPointPropertyValue(Value));\n\t\t}\n\t\telse if (NumericProperty->IsInteger())\n\t\t{\n\t\t\treturn MakeShared<FJsonxValueNumber>(NumericProperty->GetSignedIntPropertyValue(Value));\n\t\t}\n\n\t\t// fall through to default\n\t}\n\telse if (FBoolProperty *BoolProperty = CastField<FBoolProperty>(Property))\n\t{\n\t\t// Export bools as bools\n\t\treturn MakeShared<FJsonxValueBoolean>(BoolProperty->GetPropertyValue(Value));\n\t}\n\telse if (FStrProperty *StringProperty = CastField<FStrProperty>(Property))\n\t{\n\t\treturn MakeShared<FJsonxValueString>(StringProperty->GetPropertyValue(Value));\n\t}\n\telse if (FTextProperty *TextProperty = CastField<FTextProperty>(Property))\n\t{\n\t\treturn MakeShared<FJsonxValueString>(TextProperty->GetPropertyValue(Value).ToString());\n\t}\n\telse if (FArrayProperty *ArrayProperty = CastField<FArrayProperty>(Property))\n\t{\n\t\tTArray< TSharedPtr<FJsonxValue> > Out;\n\t\tFScriptArrayHelper Helper(ArrayProperty, Value);\n\t\tfor (int32 i=0, n=Helper.Num(); i<n; ++i)\n\t\t{\n\t\t\tTSharedPtr<FJsonxValue> Elem = FJsonxObjectConverter::UPropertyToJsonxValue(ArrayProperty->Inner, Helper.GetRawPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb, ArrayProperty);\n\t\t\tif ( Elem.IsValid() )\n\t\t\t{\n\t\t\t\t// add to the array\n\t\t\t\tOut.Push(Elem);\n\t\t\t}\n\t\t}\n\t\treturn MakeShared<FJsonxValueArray>(Out);\n\t}\n\telse if ( FSetProperty* SetProperty = CastField<FSetProperty>(Property) )\n\t{\n\t\tTArray< TSharedPtr<FJsonxValue> > Out;\n\t\tFScriptSetHelper Helper(SetProperty, Value);\n\t\tfor ( int32 i=0, n=Helper.Num(); n; ++i )\n\t\t{\n\t\t\tif ( Helper.IsValidIndex(i) )\n\t\t\t{\n\t\t\t\tTSharedPtr<FJsonxValue> Elem = FJsonxObjectConverter::UPropertyToJsonxValue(SetProperty->ElementProp, Helper.GetElementPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb, SetProperty);\n\t\t\t\tif ( Elem.IsValid() )\n\t\t\t\t{\n\t\t\t\t\t// add to the array\n\t\t\t\t\tOut.Push(Elem);\n\t\t\t\t}\n\n\t\t\t\t--n;\n\t\t\t}\n\t\t}\n\t\treturn MakeShared<FJsonxValueArray>(Out);\n\t}\n\telse if ( FMapProperty* MapProperty = CastField<FMapProperty>(Property) )\n\t{\n\t\tTSharedRef<FJsonxObject> Out = MakeShared<FJsonxObject>();\n\n\t\tFScriptMapHelper Helper(MapProperty, Value);\n\t\tfor ( int32 i=0, n = Helper.Num(); n; ++i )\n\t\t{\n\t\t\tif ( Helper.IsValidIndex(i) )\n\t\t\t{\n\t\t\t\tTSharedPtr<FJsonxValue> KeyElement = FJsonxObjectConverter::UPropertyToJsonxValue(MapProperty->KeyProp, Helper.GetKeyPtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb, MapProperty);\n\t\t\t\tTSharedPtr<FJsonxValue> ValueElement = FJsonxObjectConverter::UPropertyToJsonxValue(MapProperty->ValueProp, Helper.GetValuePtr(i), CheckFlags & ( ~CPF_ParmFlags ), SkipFlags, ExportCb, MapProperty);\n\t\t\t\tif ( KeyElement.IsValid() && ValueElement.IsValid() )\n\t\t\t\t{\n\t\t\t\t\tFString KeyString;\n\t\t\t\t\tif (!KeyElement->TryGetString(KeyString))\n\t\t\t\t\t{\n\t\t\t\t\t\tMapProperty->KeyProp->ExportTextItem(KeyString, Helper.GetKeyPtr(i), nullptr, nullptr, 0);\n\t\t\t\t\t\tif (KeyString.IsEmpty())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"Unable to convert key to string for property %s.\"), *MapProperty->GetName())\n\t\t\t\t\t\t\tKeyString = FString::Printf(TEXT(\"Unparsed Key %d\"), i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tOut->SetField(KeyString, ValueElement);\n\t\t\t\t}\n\n\t\t\t\t--n;\n\t\t\t}\n\t\t}\n\n\t\treturn MakeShared<FJsonxValueObject>(Out);\n\t}\n\telse if (FStructProperty *StructProperty = CastField<FStructProperty>(Property))\n\t{\n\t\tUScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();\n\t\t// Intentionally exclude the JSONX Object wrapper, which specifically needs to export JSONX in an object representation instead of a string\n\t\tif (StructProperty->Struct != FJsonxObjectWrapper::StaticStruct() && TheCppStructOps && TheCppStructOps->HasExportTextItem())\n\t\t{\n\t\t\tFString OutValueStr;\n\t\t\tTheCppStructOps->ExportTextItem(OutValueStr, Value, nullptr, nullptr, PPF_None, nullptr);\n\t\t\treturn MakeShared<FJsonxValueString>(OutValueStr);\n\t\t}\n\n\t\tTSharedRef<FJsonxObject> Out = MakeShared<FJsonxObject>();\n\t\tif (FJsonxObjectConverter::UStructToJsonxObject(StructProperty->Struct, Value, Out, CheckFlags & (~CPF_ParmFlags), SkipFlags, ExportCb))\n\t\t{\n\t\t\treturn MakeShared<FJsonxValueObject>(Out);\n\t\t}\n\t}\n\telse if (FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))\n\t{\n\t\t// Instanced properties should be copied by value, while normal UObject* properties should output as asset references\n\t\tUObject* Object = ObjectProperty->GetObjectPropertyValue(Value);\n\t\tif (Object && (ObjectProperty->HasAnyPropertyFlags(CPF_PersistentInstance) || (OuterProperty && OuterProperty->HasAnyPropertyFlags(CPF_PersistentInstance))))\n\t\t{\n\t\t\tTSharedRef<FJsonxObject> Out = MakeShared<FJsonxObject>();\n\n\t\t\tOut->SetStringField(ObjectClassNameKey, Object->GetClass()->GetFName().ToString());\n\t\t\tif (FJsonxObjectConverter::UStructToJsonxObject(ObjectProperty->GetObjectPropertyValue(Value)->GetClass(), Object, Out, CheckFlags, SkipFlags, ExportCb))\n\t\t\t{\n\t\t\t\tTSharedRef<FJsonxValueObject> JsonxObject = MakeShared<FJsonxValueObject>(Out);\n\t\t\t\tJsonxObject->Type = EJsonx::Object;\n\t\t\t\treturn JsonxObject;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFString StringValue;\n\t\t\tProperty->ExportTextItem(StringValue, Value, nullptr, nullptr, PPF_None);\n\t\t\treturn MakeShared<FJsonxValueString>(StringValue);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Default to export as string for everything else\n\t\tFString StringValue;\n\t\tProperty->ExportTextItem(StringValue, Value, NULL, NULL, PPF_None);\n\t\treturn MakeShared<FJsonxValueString>(StringValue);\n\t}\n\n\t// invalid\n\treturn TSharedPtr<FJsonxValue>();\n}\n}\n\nPRAGMA_DISABLE_DEPRECATION_WARNINGS\n\nTSharedPtr<FJsonxValue> FJsonxObjectConverter::ObjectJsonxCallback(FProperty* Property, const void* Value)\n{\n\tif (FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))\n\t{\n\t\tif (!ObjectProperty->HasAnyFlags(RF_Transient)) // We are taking Transient to mean we don't want to serialize to Jsonx either (could make a new flag if nessasary)\n\t\t{\n\t\t\tTSharedRef<FJsonxObject> Out = MakeShared<FJsonxObject>();\n\n\t\t\tCustomExportCallback CustomCB;\n\t\t\tCustomCB.BindStatic(FJsonxObjectConverter::ObjectJsonxCallback);\n\n\t\t\tvoid** PtrToValuePtr = (void**)Value;\n\n\t\t\tif (FJsonxObjectConverter::UStructToJsonxObject(ObjectProperty->PropertyClass, (*PtrToValuePtr), Out, 0, 0, &CustomCB))\n\t\t\t{\n\t\t\t\treturn MakeShared<FJsonxValueObject>(Out);\n\t\t\t}\n\t\t}\n\t}\n\n\t// invalid\n\treturn TSharedPtr<FJsonxValue>();\n}\n\nPRAGMA_ENABLE_DEPRECATION_WARNINGS\n\nTSharedPtr<FJsonxValue> FJsonxObjectConverter::UPropertyToJsonxValue(FProperty* Property, const void* Value, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb, FProperty* OuterProperty)\n{\n\tif (Property->ArrayDim == 1)\n\t{\n\t\treturn ConvertScalarFPropertyToJsonxValue(Property, Value, CheckFlags, SkipFlags, ExportCb, OuterProperty);\n\t}\n\n\tTArray< TSharedPtr<FJsonxValue> > Array;\n\tfor (int Index = 0; Index != Property->ArrayDim; ++Index)\n\t{\n\t\tArray.Add(ConvertScalarFPropertyToJsonxValue(Property, (char*)Value + Index * Property->ElementSize, CheckFlags, SkipFlags, ExportCb, OuterProperty));\n\t}\n\treturn MakeShared<FJsonxValueArray>(Array);\n}\n\nbool FJsonxObjectConverter::UStructToJsonxObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonxObject> OutJsonxObject, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb)\n{\n\treturn UStructToJsonxAttributes(StructDefinition, Struct, OutJsonxObject->Values, CheckFlags, SkipFlags, ExportCb);\n}\n\nbool FJsonxObjectConverter::UStructToJsonxAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonxValue> >& OutJsonxAttributes, int64 CheckFlags, int64 SkipFlags, const CustomExportCallback* ExportCb)\n{\n\tif (SkipFlags == 0)\n\t{\n\t\t// If we have no specified skip flags, skip deprecated, transient and skip serialization by default when writing\n\t\tSkipFlags |= CPF_Deprecated | CPF_Transient;\n\t}\n\n\tif (StructDefinition == FJsonxObjectWrapper::StaticStruct())\n\t{\n\t\t// Just copy it into the object\n\t\tconst FJsonxObjectWrapper* ProxyObject = (const FJsonxObjectWrapper *)Struct;\n\n\t\tif (ProxyObject->JsonxObject.IsValid())\n\t\t{\n\t\t\tOutJsonxAttributes = ProxyObject->JsonxObject->Values;\n\t\t}\n\t\treturn true;\n\t}\n\n\tfor (TFieldIterator<FProperty> It(StructDefinition); It; ++It)\n\t{\n\t\tFProperty* Property = *It;\n\n\t\t// Check to see if we should ignore this property\n\t\tif (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tif (Property->HasAnyPropertyFlags(SkipFlags))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFString VariableName = StandardizeCase(Property->GetName());\n\t\tconst void* Value = Property->ContainerPtrToValuePtr<uint8>(Struct);\n\n\t\t// convert the property to a FJsonxValue\n\t\tTSharedPtr<FJsonxValue> JsonxValue = UPropertyToJsonxValue(Property, Value, CheckFlags, SkipFlags, ExportCb);\n\t\tif (!JsonxValue.IsValid())\n\t\t{\n\t\t\tFFieldClass* PropClass = Property->GetClass();\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"UStructToJsonxObject - Unhandled property type '%s': %s\"), *PropClass->GetName(), *Property->GetPathName());\n\t\t\treturn false;\n\t\t}\n\n\t\t// set the value on the output object\n\t\tOutJsonxAttributes.Add(VariableName, JsonxValue);\n\t}\n\n\treturn true;\n}\n\ntemplate<class CharType, class PrintPolicy>\nbool UStructToJsonxObjectStringInternal(const TSharedRef<FJsonxObject>& JsonxObject, FString& OutJsonxString, int32 Indent)\n{\n\tTSharedRef<TJsonxWriter<CharType, PrintPolicy> > JsonxWriter = TJsonxWriterFactory<CharType, PrintPolicy>::Create(&OutJsonxString, Indent);\n\tbool bSuccess = FJsonxSerializer::Serialize(JsonxObject, JsonxWriter);\n\tJsonxWriter->Close();\n\treturn bSuccess;\n}\n\nbool FJsonxObjectConverter::UStructToJsonxObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonxString, int64 CheckFlags, int64 SkipFlags, int32 Indent, const CustomExportCallback* ExportCb, bool bPrettyPrint)\n{\n\tTSharedRef<FJsonxObject> JsonxObject = MakeShared<FJsonxObject>();\n\tif (UStructToJsonxObject(StructDefinition, Struct, JsonxObject, CheckFlags, SkipFlags, ExportCb))\n\t{\n\t\tbool bSuccess = false;\n\t\tif (bPrettyPrint)\n\t\t{\n\t\t\tbSuccess = UStructToJsonxObjectStringInternal<TCHAR, TPrettyJsonxPrintPolicy<TCHAR> >(JsonxObject, OutJsonxString, Indent);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbSuccess = UStructToJsonxObjectStringInternal<TCHAR, TCondensedJsonxPrintPolicy<TCHAR> >(JsonxObject, OutJsonxString, Indent);\n\t\t}\n\t\tif (bSuccess)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"UStructToJsonxObjectString - Unable to write out json\"));\n\t\t}\n\t}\n\n\treturn false;\n}\n\n//static\nbool FJsonxObjectConverter::GetTextFromObject(const TSharedRef<FJsonxObject>& Obj, FText& TextOut)\n{\n\t// get the prioritized culture name list\n\tFCultureRef CurrentCulture = FInternationalization::Get().GetCurrentCulture();\n\tTArray<FString> CultureList = CurrentCulture->GetPrioritizedParentCultureNames();\n\n\t// try to follow the fall back chain that the engine uses\n\tFString TextString;\n\tfor (const FString& CultureCode : CultureList)\n\t{\n\t\tif (Obj->TryGetStringField(CultureCode, TextString))\n\t\t{\n\t\t\tTextOut = FText::FromString(TextString);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// try again but only search on the locale region (in the localized data). This is a common omission (i.e. en-US source text should be used if no en is defined)\n\tfor (const FString& LocaleToMatch : CultureList)\n\t{\n\t\tint32 SeparatorPos;\n\t\t// only consider base language entries in culture chain (i.e. \"en\")\n\t\tif (!LocaleToMatch.FindChar('-', SeparatorPos))\n\t\t{\n\t\t\tfor (const auto& Pair : Obj->Values)\n\t\t\t{\n\t\t\t\t// only consider coupled entries now (base ones would have been matched on first path) (i.e. \"en-US\")\n\t\t\t\tif (Pair.Key.FindChar('-', SeparatorPos))\n\t\t\t\t{\n\t\t\t\t\tif (Pair.Key.StartsWith(LocaleToMatch))\n\t\t\t\t\t{\n\t\t\t\t\t\tTextOut = FText::FromString(Pair.Value->AsString());\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// no luck, is this possibly an unrelated json object?\n\treturn false;\n}\n\n\nnamespace\n{\n\tbool JsonxValueToFPropertyWithContainer(const TSharedPtr<FJsonxValue>& JsonxValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);\n\tbool JsonxAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonxValue> >& JsonxAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags);\n\n\t/** Convert JSONX to property, assuming either the property is not an array or the value is an individual array element */\n\tbool ConvertScalarJsonxValueToFPropertyWithContainer(const TSharedPtr<FJsonxValue>& JsonxValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)\n\t{\n\tif (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))\n\t{\n\t\tif (JsonxValue->Type == EJsonx::String)\n\t\t{\n\t\t\t// see if we were passed a string for the enum\n\t\t\tconst UEnum* Enum = EnumProperty->GetEnum();\n\t\t\tcheck(Enum);\n\t\t\tFString StrValue = JsonxValue->AsString();\n\t\t\tint64 IntValue = Enum->GetValueByName(FName(*StrValue));\n\t\t\tif (IntValue == INDEX_NONE)\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable import enum %s from string value %s for property %s\"), *Enum->CppType, *StrValue, *Property->GetNameCPP());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tEnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, IntValue);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// AsNumber will log an error for completely inappropriate types (then give us a default)\n\t\t\tEnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(OutValue, (int64)JsonxValue->AsNumber());\n\t\t}\n\t}\n\telse if (FNumericProperty *NumericProperty = CastField<FNumericProperty>(Property))\n\t{\n\t\tif (NumericProperty->IsEnum() && JsonxValue->Type == EJsonx::String)\n\t\t{\n\t\t\t// see if we were passed a string for the enum\n\t\t\tconst UEnum* Enum = NumericProperty->GetIntPropertyEnum();\n\t\t\tcheck(Enum); // should be assured by IsEnum()\n\t\t\tFString StrValue = JsonxValue->AsString();\n\t\t\tint64 IntValue = Enum->GetValueByName(FName(*StrValue));\n\t\t\tif (IntValue == INDEX_NONE)\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable import enum %s from string value %s for property %s\"), *Enum->CppType, *StrValue, *Property->GetNameCPP());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tNumericProperty->SetIntPropertyValue(OutValue, IntValue);\n\t\t}\n\t\telse if (NumericProperty->IsFloatingPoint())\n\t\t{\n\t\t\t// AsNumber will log an error for completely inappropriate types (then give us a default)\n\t\t\tNumericProperty->SetFloatingPointPropertyValue(OutValue, JsonxValue->AsNumber());\n\t\t}\n\t\telse if (NumericProperty->IsInteger())\n\t\t{\n\t\t\tif (JsonxValue->Type == EJsonx::String)\n\t\t\t{\n\t\t\t\t// parse string -> int64 ourselves so we don't lose any precision going through AsNumber (aka double)\n\t\t\t\tNumericProperty->SetIntPropertyValue(OutValue, FCString::Atoi64(*JsonxValue->AsString()));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// AsNumber will log an error for completely inappropriate types (then give us a default)\n\t\t\t\tNumericProperty->SetIntPropertyValue(OutValue, (int64)JsonxValue->AsNumber());\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable to set numeric property type %s for property %s\"), *Property->GetClass()->GetName(), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (FBoolProperty *BoolProperty = CastField<FBoolProperty>(Property))\n\t{\n\t\t// AsBool will log an error for completely inappropriate types (then give us a default)\n\t\tBoolProperty->SetPropertyValue(OutValue, JsonxValue->AsBool());\n\t}\n\telse if (FStrProperty *StringProperty = CastField<FStrProperty>(Property))\n\t{\n\t\t// AsString will log an error for completely inappropriate types (then give us a default)\n\t\tStringProperty->SetPropertyValue(OutValue, JsonxValue->AsString());\n\t}\n\telse if (FArrayProperty *ArrayProperty = CastField<FArrayProperty>(Property))\n\t{\n\t\tif (JsonxValue->Type == EJsonx::Array)\n\t\t{\n\t\t\tTArray< TSharedPtr<FJsonxValue> > ArrayValue = JsonxValue->AsArray();\n\t\t\tint32 ArrLen = ArrayValue.Num();\n\n\t\t\t// make the output array size match\n\t\t\tFScriptArrayHelper Helper(ArrayProperty, OutValue);\n\t\t\tHelper.Resize(ArrLen);\n\n\t\t\t// set the property values\n\t\t\tfor (int32 i = 0; i < ArrLen; ++i)\n\t\t\t{\n\t\t\t\tconst TSharedPtr<FJsonxValue>& ArrayValueItem = ArrayValue[i];\n\t\t\t\tif (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())\n\t\t\t\t{\n\t\t\t\t\tif (!JsonxValueToFPropertyWithContainer(ArrayValueItem, ArrayProperty->Inner, Helper.GetRawPtr(i), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))\n\t\t\t\t\t{\n\t\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable to deserialize array element [%d] for property %s\"), i, *Property->GetNameCPP());\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import TArray from non-array JSONX key for property %s\"), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (FMapProperty* MapProperty = CastField<FMapProperty>(Property))\n\t{\n\t\tif (JsonxValue->Type == EJsonx::Object)\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> ObjectValue = JsonxValue->AsObject();\n\n\t\t\tFScriptMapHelper Helper(MapProperty, OutValue);\n\n\t\t\tcheck(ObjectValue);\n\n\t\t\tint32 MapSize = ObjectValue->Values.Num();\n\t\t\tHelper.EmptyValues(MapSize);\n\n\t\t\t// set the property values\n\t\t\tfor (const auto& Entry : ObjectValue->Values)\n\t\t\t{\n\t\t\t\tif (Entry.Value.IsValid() && !Entry.Value->IsNull())\n\t\t\t\t{\n\t\t\t\t\tint32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();\n\n\t\t\t\t\tTSharedPtr<FJsonxValueString> TempKeyValue = MakeShared<FJsonxValueString>(Entry.Key);\n\n\t\t\t\t\tconst bool bKeySuccess = JsonxValueToFPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);\n\t\t\t\t\tconst bool bValueSuccess = JsonxValueToFPropertyWithContainer(Entry.Value, MapProperty->ValueProp, Helper.GetValuePtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags);\n\n\t\t\t\t\tif (!(bKeySuccess && bValueSuccess))\n\t\t\t\t\t{\n\t\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable to deserialize map element [key: %s] for property %s\"), *Entry.Key, *Property->GetNameCPP());\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tHelper.Rehash();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import TMap from non-object JSONX key for property %s\"), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (FSetProperty* SetProperty = CastField<FSetProperty>(Property))\n\t{\n\t\tif (JsonxValue->Type == EJsonx::Array)\n\t\t{\n\t\t\tTArray< TSharedPtr<FJsonxValue> > ArrayValue = JsonxValue->AsArray();\n\t\t\tint32 ArrLen = ArrayValue.Num();\n\n\t\t\tFScriptSetHelper Helper(SetProperty, OutValue);\n\n\t\t\t// set the property values\n\t\t\tfor (int32 i = 0; i < ArrLen; ++i)\n\t\t\t{\n\t\t\t\tconst TSharedPtr<FJsonxValue>& ArrayValueItem = ArrayValue[i];\n\t\t\t\tif (ArrayValueItem.IsValid() && !ArrayValueItem->IsNull())\n\t\t\t\t{\n\t\t\t\t\tint32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash();\n\t\t\t\t\tif (!JsonxValueToFPropertyWithContainer(ArrayValueItem, SetProperty->ElementProp, Helper.GetElementPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))\n\t\t\t\t\t{\n\t\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable to deserialize set element [%d] for property %s\"), i, *Property->GetNameCPP());\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tHelper.Rehash();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import TSet from non-array JSONX key for property %s\"), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (FTextProperty* TextProperty = CastField<FTextProperty>(Property))\n\t{\n\t\tif (JsonxValue->Type == EJsonx::String)\n\t\t{\n\t\t\t// assume this string is already localized, so import as invariant\n\t\t\tTextProperty->SetPropertyValue(OutValue, FText::FromString(JsonxValue->AsString()));\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::Object)\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> Obj = JsonxValue->AsObject();\n\t\t\tcheck(Obj.IsValid()); // should not fail if Type == EJsonx::Object\n\n\t\t\t// import the subvalue as a culture invariant string\n\t\t\tFText Text;\n\t\t\tif (!FJsonxObjectConverter::GetTextFromObject(Obj.ToSharedRef(), Text))\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import FText from JSONX object with invalid keys for property %s\"), *Property->GetNameCPP());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tTextProperty->SetPropertyValue(OutValue, Text);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import FText from JSONX that was neither string nor object for property %s\"), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (FStructProperty *StructProperty = CastField<FStructProperty>(Property))\n\t{\n\t\tstatic const FName NAME_DateTime(TEXT(\"DateTime\"));\n\t\tstatic const FName NAME_Color(TEXT(\"Color\"));\n\t\tstatic const FName NAME_LinearColor(TEXT(\"LinearColor\"));\n\t\tif (JsonxValue->Type == EJsonx::Object)\n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> Obj = JsonxValue->AsObject();\n\t\t\tcheck(Obj.IsValid()); // should not fail if Type == EJsonx::Object\n\t\t\tif (!JsonxAttributesToUStructWithContainer(Obj->Values, StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags))\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - FJsonxObjectConverter::JsonxObjectToUStruct failed for property %s\"), *Property->GetNameCPP());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::String && StructProperty->Struct->GetFName() == NAME_LinearColor)\n\t\t{\n\t\t\tFLinearColor& ColorOut = *(FLinearColor*)OutValue;\n\t\t\tFString ColorString = JsonxValue->AsString();\n\n\t\t\tFColor IntermediateColor;\n\t\t\tIntermediateColor = FColor::FromHex(ColorString);\n\n\t\t\tColorOut = IntermediateColor;\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::String && StructProperty->Struct->GetFName() == NAME_Color)\n\t\t{\n\t\t\tFColor& ColorOut = *(FColor*)OutValue;\n\t\t\tFString ColorString = JsonxValue->AsString();\n\n\t\t\tColorOut = FColor::FromHex(ColorString);\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::String && StructProperty->Struct->GetFName() == NAME_DateTime)\n\t\t{\n\t\t\tFString DateString = JsonxValue->AsString();\n\t\t\tFDateTime& DateTimeOut = *(FDateTime*)OutValue;\n\t\t\tif (DateString == TEXT(\"min\"))\n\t\t\t{\n\t\t\t\t// min representable value for our date struct. Actual date may vary by platform (this is used for sorting)\n\t\t\t\tDateTimeOut = FDateTime::MinValue();\n\t\t\t}\n\t\t\telse if (DateString == TEXT(\"max\"))\n\t\t\t{\n\t\t\t\t// max representable value for our date struct. Actual date may vary by platform (this is used for sorting)\n\t\t\t\tDateTimeOut = FDateTime::MaxValue();\n\t\t\t}\n\t\t\telse if (DateString == TEXT(\"now\"))\n\t\t\t{\n\t\t\t\t// this value's not really meaningful from json serialization (since we don't know timezone) but handle it anyway since we're handling the other keywords\n\t\t\t\tDateTimeOut = FDateTime::UtcNow();\n\t\t\t}\n\t\t\telse if (FDateTime::ParseIso8601(*DateString, DateTimeOut))\n\t\t\t{\n\t\t\t\t// ok\n\t\t\t}\n\t\t\telse if (FDateTime::Parse(DateString, DateTimeOut))\n\t\t\t{\n\t\t\t\t// ok\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable to import FDateTime for property %s\"), *Property->GetNameCPP());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::String && StructProperty->Struct->GetCppStructOps() && StructProperty->Struct->GetCppStructOps()->HasImportTextItem())\n\t\t{\n\t\t\tUScriptStruct::ICppStructOps* TheCppStructOps = StructProperty->Struct->GetCppStructOps();\n\n\t\t\tFString ImportTextString = JsonxValue->AsString();\n\t\t\tconst TCHAR* ImportTextPtr = *ImportTextString;\n\t\t\tif (!TheCppStructOps->ImportTextItem(ImportTextPtr, OutValue, PPF_None, nullptr, (FOutputDevice*)GWarn))\n\t\t\t{\n\t\t\t\t// Fall back to trying the tagged property approach if custom ImportTextItem couldn't get it done\n\t\t\t\tProperty->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);\n\t\t\t}\n\t\t}\n\t\telse if (JsonxValue->Type == EJsonx::String)\n\t\t{\n\t\t\tFString ImportTextString = JsonxValue->AsString();\n\t\t\tconst TCHAR* ImportTextPtr = *ImportTextString;\n\t\t\tProperty->ImportText(ImportTextPtr, OutValue, PPF_None, nullptr);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import UStruct from non-object JSONX key for property %s\"), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\t\telse if (FObjectProperty *ObjectProperty = CastField<FObjectProperty>(Property))\n\t\t{\n\t\t\tif (JsonxValue->Type == EJsonx::Object)\n\t\t\t{\n\t\t\t\tUObject* Outer = GetTransientPackage();\n\t\t\t\tif (ContainerStruct->IsChildOf(UObject::StaticClass()))\n\t\t\t\t{\n\t\t\t\t\tOuter = (UObject*)Container;\n\t\t\t\t}\n\n\t\t\t\tTSharedPtr<FJsonxObject> Obj = JsonxValue->AsObject();\n\t\t\t\tUClass* PropertyClass = ObjectProperty->PropertyClass;\n\n\t\t\t\t// If a specific subclass was stored in the Jsonx, use that instead of the PropertyClass\n\t\t\t\tFString ClassString = Obj->GetStringField(ObjectClassNameKey);\n\t\t\t\tObj->RemoveField(ObjectClassNameKey);\n\t\t\t\tif (!ClassString.IsEmpty())\n\t\t\t\t{\n\t\t\t\t\tUClass* FoundClass = FindObject<UClass>(ANY_PACKAGE, *ClassString);\n\t\t\t\t\tif (FoundClass)\n\t\t\t\t\t{\n\t\t\t\t\t\tPropertyClass = FoundClass;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tUObject* createdObj = StaticAllocateObject(PropertyClass, Outer, NAME_None, EObjectFlags::RF_NoFlags, EInternalObjectFlags::None, false);\n\t\t\t\t(*PropertyClass->ClassConstructor)(FObjectInitializer(createdObj, PropertyClass->ClassDefaultObject, false, false));\n\n\t\t\t\tObjectProperty->SetObjectPropertyValue(OutValue, createdObj);\n\n\t\t\t\tcheck(Obj.IsValid()); // should not fail if Type == EJsonx::Object\n\t\t\t\tif (!JsonxAttributesToUStructWithContainer(Obj->Values, PropertyClass, createdObj, PropertyClass, createdObj, CheckFlags & (~CPF_ParmFlags), SkipFlags))\n\t\t\t\t{\n\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - FJsonxObjectConverter::JsonxObjectToUStruct failed for property %s\"), *Property->GetNameCPP());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (JsonxValue->Type == EJsonx::String)\n\t\t\t{\n\t\t\t\t// Default to expect a string for everything else\n\t\t\t\tif (Property->ImportText(*JsonxValue->AsString(), OutValue, 0, NULL) == NULL)\n\t\t\t\t{\n\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable import property type %s from string value for property %s\"), *Property->GetClass()->GetName(), *Property->GetNameCPP());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\telse\n\t{\n\t\t// Default to expect a string for everything else\n\t\tif (Property->ImportText(*JsonxValue->AsString(), OutValue, 0, NULL) == NULL)\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Unable import property type %s from string value for property %s\"), *Property->GetClass()->GetName(), *Property->GetNameCPP());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n\t}\n\n\n\tbool JsonxValueToFPropertyWithContainer(const TSharedPtr<FJsonxValue>& JsonxValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)\n\t{\n\t\tif (!JsonxValue.IsValid())\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Invalid value JSONX key\"));\n\t\t\treturn false;\n\t\t}\n\n\t\tbool bArrayOrSetProperty = Property->IsA<FArrayProperty>() || Property->IsA<FSetProperty>();\n\t\tbool bJsonxArray = JsonxValue->Type == EJsonx::Array;\n\n\t\tif (!bJsonxArray)\n\t\t{\n\t\t\tif (bArrayOrSetProperty)\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxValueToUProperty - Attempted to import TArray from non-array JSONX key\"));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (Property->ArrayDim != 1)\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"Ignoring excess properties when deserializing %s\"), *Property->GetName());\n\t\t\t}\n\n\t\t\treturn ConvertScalarJsonxValueToFPropertyWithContainer(JsonxValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);\n\t\t}\n\n\t\t// In practice, the ArrayDim == 1 check ought to be redundant, since nested arrays of FPropertys are not supported\n\t\tif (bArrayOrSetProperty && Property->ArrayDim == 1)\n\t\t{\n\t\t\t// Read into TArray\n\t\t\treturn ConvertScalarJsonxValueToFPropertyWithContainer(JsonxValue, Property, OutValue, ContainerStruct, Container, CheckFlags, SkipFlags);\n\t\t}\n\n\t\t// We're deserializing a JSONX array\n\t\tconst auto& ArrayValue = JsonxValue->AsArray();\n\t\tif (Property->ArrayDim < ArrayValue.Num())\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"Ignoring excess properties when deserializing %s\"), *Property->GetName());\n\t\t}\n\n\t\t// Read into native array\n\t\tint ItemsToRead = FMath::Clamp(ArrayValue.Num(), 0, Property->ArrayDim);\n\t\tfor (int Index = 0; Index != ItemsToRead; ++Index)\n\t\t{\n\t\t\tif (!ConvertScalarJsonxValueToFPropertyWithContainer(ArrayValue[Index], Property, (char*)OutValue + Index * Property->ElementSize, ContainerStruct, Container, CheckFlags, SkipFlags))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool JsonxAttributesToUStructWithContainer(const TMap< FString, TSharedPtr<FJsonxValue> >& JsonxAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags)\n\t{\n\t\tif (StructDefinition == FJsonxObjectWrapper::StaticStruct())\n\t\t{\n\t\t\t// Just copy it into the object\n\t\t\tFJsonxObjectWrapper* ProxyObject = (FJsonxObjectWrapper*)OutStruct;\n\t\t\tProxyObject->JsonxObject = MakeShared<FJsonxObject>();\n\t\t\tProxyObject->JsonxObject->Values = JsonxAttributes;\n\t\t\treturn true;\n\t\t}\n\n\t\tint32 NumUnclaimedProperties = JsonxAttributes.Num();\n\t\tif (NumUnclaimedProperties <= 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t// iterate over the struct properties\n\t\tfor (TFieldIterator<FProperty> PropIt(StructDefinition); PropIt; ++PropIt)\n\t\t{\n\t\t\tFProperty* Property = *PropIt;\n\n\t\t\t// Check to see if we should ignore this property\n\t\t\tif (CheckFlags != 0 && !Property->HasAnyPropertyFlags(CheckFlags))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Property->HasAnyPropertyFlags(SkipFlags))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// find a json value matching this property name\n\t\t\tconst TSharedPtr<FJsonxValue>* JsonxValue = JsonxAttributes.Find(Property->GetName());\n\t\t\tif (!JsonxValue)\n\t\t\t{\n\t\t\t\t// we allow values to not be found since this mirrors the typical UObject mantra that all the fields are optional when deserializing\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (JsonxValue->IsValid() && !(*JsonxValue)->IsNull())\n\t\t\t{\n\t\t\t\tvoid* Value = Property->ContainerPtrToValuePtr<uint8>(OutStruct);\n\t\t\t\tif (!JsonxValueToFPropertyWithContainer(*JsonxValue, Property, Value, ContainerStruct, Container, CheckFlags, SkipFlags))\n\t\t\t\t{\n\t\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"JsonxObjectToUStruct - Unable to parse %s.%s from JSONX\"), *StructDefinition->GetName(), *Property->GetName());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (--NumUnclaimedProperties <= 0)\n\t\t\t{\n\t\t\t\t// If we found all properties that were in the JsonxAttributes map, there is no reason to keep looking for more.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n}\n\nbool FJsonxObjectConverter::JsonxValueToUProperty(const TSharedPtr<FJsonxValue>& JsonxValue, FProperty* Property, void* OutValue, int64 CheckFlags, int64 SkipFlags)\n{\n\treturn JsonxValueToFPropertyWithContainer(JsonxValue, Property, OutValue, nullptr, nullptr, CheckFlags, SkipFlags);\n}\n\nbool FJsonxObjectConverter::JsonxObjectToUStruct(const TSharedRef<FJsonxObject>& JsonxObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)\n{\n\treturn JsonxAttributesToUStruct(JsonxObject->Values, StructDefinition, OutStruct, CheckFlags, SkipFlags);\n}\n\nbool FJsonxObjectConverter::JsonxAttributesToUStruct(const TMap< FString, TSharedPtr<FJsonxValue> >& JsonxAttributes, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags)\n{\n\treturn JsonxAttributesToUStructWithContainer(JsonxAttributes, StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags);\n}\n\n//static \nbool FJsonxObjectConverter::GetTextFromField(const FString& FieldName, const TSharedPtr<FJsonxValue>& FieldValue, FText& TextOut)\n{\n\tif (FieldValue.IsValid())\n\t{\n\t\tswitch (FieldValue->Type)\n\t\t{\n\t\t\tcase EJsonx::Number:\n\t\t\t{\n\t\t\t\t// number\n\t\t\t\tTextOut = FText::AsNumber(FieldValue->AsNumber());\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase EJsonx::String:\n\t\t\t{\n\t\t\t\tif (FieldName.StartsWith(TEXT(\"date-\")))\n\t\t\t\t{\n\t\t\t\t\tFDateTime Dte;\n\t\t\t\t\tif (FDateTime::ParseIso8601(*FieldValue->AsString(), Dte))\n\t\t\t\t\t{\n\t\t\t\t\t\tTextOut = FText::AsDate(Dte);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (FieldName.StartsWith(TEXT(\"datetime-\")))\n\t\t\t\t{\n\t\t\t\t\tFDateTime Dte;\n\t\t\t\t\tif (FDateTime::ParseIso8601(*FieldValue->AsString(), Dte))\n\t\t\t\t\t{\n\t\t\t\t\t\tTextOut = FText::AsDateTime(Dte);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t// culture invariant string\n\t\t\t\t\tTextOut = FText::FromString(FieldValue->AsString());\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase EJsonx::Object:\n\t\t\t{\n\t\t\t\t// localized string\n\t\t\t\tif (FJsonxObjectConverter::GetTextFromObject(FieldValue->AsObject().ToSharedRef(), TextOut))\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"Unable to apply Jsonx parameter %s (could not parse object)\"), *FieldName);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Error, TEXT(\"Unable to apply Jsonx parameter %s (bad type)\"), *FieldName);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nFFormatNamedArguments FJsonxObjectConverter::ParseTextArgumentsFromJsonx(const TSharedPtr<const FJsonxObject>& JsonxObject)\n{\n\tFFormatNamedArguments NamedArgs;\n\tif (JsonxObject.IsValid())\n\t{\n\t\tfor (const auto& It : JsonxObject->Values)\n\t\t{\n\t\t\tFText TextValue;\n\t\t\tif (GetTextFromField(It.Key, It.Value, TextValue))\n\t\t\t{\n\t\t\t\tNamedArgs.Emplace(It.Key, TextValue);\n\t\t\t}\n\t\t}\n\t}\n\treturn NamedArgs;\n}"
  },
  {
    "path": "Source/JsonxUtilities/Private/JsonxObjectWrapper.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"JsonxObjectWrapper.h\"\n#include \"Policies/CondensedJsonxPrintPolicy.h\"\n#include \"Serialization/JsonxReader.h\"\n#include \"Serialization/JsonxSerializer.h\"\n\nbool FJsonxObjectWrapper::ImportTextItem(const TCHAR*& Buffer, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText)\n{\n\t// read JSONX string from Buffer\n\tFString Jsonx;\n\tif (*Buffer == TCHAR('\"'))\n\t{\n\t\tint32 NumCharsRead = 0;\n\t\tif (!FParse::QuotedString(Buffer, Jsonx, &NumCharsRead))\n\t\t{\n\t\t\tErrorText->Logf(ELogVerbosity::Warning, TEXT(\"FJsonxObjectWrapper::ImportTextItem: Bad quoted string: %s\\n\"), Buffer);\n\t\t\treturn false;\n\t\t}\n\t\tBuffer += NumCharsRead;\n\t}\n\telse\n\t{\n\t\t// consume the rest of the string (this happens on Paste)\n\t\tJsonx = Buffer;\n\t\tBuffer += Jsonx.Len();\n\t}\n\n\t// empty string yields empty shared pointer\n\tif (Jsonx.IsEmpty())\n\t{\n\t\tJsonxString.Empty();\n\t\tJsonxObject.Reset();\n\t\treturn true;\n\t}\n\n\t// parse the json\n\tif (!JsonxObjectFromString(Jsonx))\n\t{\n\t\tif (ErrorText)\n\t\t{\n\t\t\tErrorText->Logf(ELogVerbosity::Warning, TEXT(\"FJsonxObjectWrapper::ImportTextItem - Unable to parse json: %s\\n\"), *Jsonx);\n\t\t}\n\t\treturn false;\n\t}\n\tJsonxString = Jsonx;\n\treturn true;\n}\n\nbool FJsonxObjectWrapper::ExportTextItem(FString& ValueStr, FJsonxObjectWrapper const& DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const\n{\n\t// empty pointer yields empty string\n\tif (!JsonxObject.IsValid())\n\t{\n\t\tValueStr.Empty();\n\t\treturn true;\n\t}\n\n\t// serialize the json\n\treturn JsonxObjectToString(ValueStr);\n}\n\nvoid FJsonxObjectWrapper::PostSerialize(const FArchive& Ar)\n{\n\tif (!JsonxString.IsEmpty())\n\t{\n\t\t// try to parse JsonxString\n\t\tif (!JsonxObjectFromString(JsonxString))\n\t\t{\n\t\t\t// do not abide a string that won't parse\n\t\t\tJsonxString.Empty();\n\t\t}\n\t}\n}\n\nbool FJsonxObjectWrapper::JsonxObjectToString(FString& Str) const\n{\n\tTSharedRef<TJsonxWriter<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>> JsonxWriter = TJsonxWriterFactory<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>::Create(&Str, 0);\n\treturn FJsonxSerializer::Serialize(JsonxObject.ToSharedRef(), JsonxWriter, true);\n}\n\nbool FJsonxObjectWrapper::JsonxObjectFromString(const FString& Str)\n{\n\tTSharedRef<TJsonxReader<>> JsonxReader = TJsonxReaderFactory<>::Create(Str);\n\treturn FJsonxSerializer::Deserialize(JsonxReader, JsonxObject);\n}\n\n"
  },
  {
    "path": "Source/JsonxUtilities/Private/JsonxUtilitiesModule.cpp",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"CoreMinimal.h\"\n#include \"Modules/ModuleManager.h\"\n\nIMPLEMENT_MODULE( FDefaultModuleImpl, JsonxUtilities );\n"
  },
  {
    "path": "Source/JsonxUtilities/Public/JsonxDomBuilder.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"Dom/JsonxValue.h\"\n#include \"Dom/JsonxObject.h\"\n#include \"Serialization/JsonxSerializer.h\"\n\n#include \"Templates/IsFloatingPoint.h\"\n#include \"Templates/IsIntegral.h\"\n#include \"Templates/EnableIf.h\"\n#include \"Templates/Invoke.h\"\n\n/**\n * Helpers for creating TSharedPtr<FJsonxValue> JSONX trees\n *\n * Simple example:\n *\n *\tFJsonxDomBuilder::FArray InnerArray;\n *\tInnerArray.Add(7.f, TEXT(\"Hello\"), true);\n *\n *\tFJsonxDomBuilder::FObject Object;\n *\tObject.Set(TEXT(\"Array\"), InnerArray);\n *\tObject.Set(TEXT(\"Number\"), 13.f);\n *\n *\tObject.AsJsonxValue();\n *\n * produces {\"Array\": [7., \"Hello\", true], \"Number\": 13.}\n */\n\nclass FJsonxDomBuilder\n{\npublic:\n\tclass FArray;\n\n\tclass FObject\n\t{\n\tpublic:\n\t\tFObject()\n\t\t\t: Object(MakeShared<FJsonxObject>())\n\t\t{\n\t\t}\n\n\t\tTSharedRef<FJsonxValueObject> AsJsonxValue() const\n\t\t{\n\t\t\treturn MakeShared<FJsonxValueObject>(Object);\n\t\t}\n\n\t\tTSharedRef<FJsonxObject> AsJsonxObject() const\n\t\t{\n\t\t\treturn Object;\n\t\t}\n\n\t\ttemplate <template <class> class TPrintPolicy = TPrettyJsonxPrintPolicy>\n\t\tFString ToString() const\n\t\t{\n\t\t\tFString Result;\n\t\t\tauto JsonxWriter = TJsonxWriterFactory<TCHAR, TPrintPolicy<TCHAR>>::Create(&Result);\n\t\t\tFJsonxSerializer::Serialize(Object, JsonxWriter);\n\t\t\treturn Result;\n\t\t}\n\n\t\tint Num() const\n\t\t{\n\t\t\treturn Object->Values.Num();\n\t\t}\n\n\t\tFObject& Set(const FString& Key, const FArray& Arr)            { Object->SetField(Key, Arr.AsJsonxValue());                            return *this; }\n\t\tFObject& Set(const FString& Key, const FObject& Obj)           { Object->SetField(Key, Obj.AsJsonxValue());                            return *this; }\n\n\t\tFObject& Set(const FString& Key, const FString& Str)           { Object->SetField(Key, MakeShared<FJsonxValueString>(Str));            return *this; }\n\n\t\ttemplate <class FNumber>\n\t\ttypename TEnableIf<!TIsSame<FNumber, bool>::Value && (TIsIntegral<FNumber>::Value || TIsFloatingPoint<FNumber>::Value), FObject&>::Type\n\t\t\tSet(const FString& Key, FNumber Number)                    { Object->SetField(Key, MakeShared<FJsonxValueNumber>(Number));         return *this; }\n\n\t\ttemplate <class FBool>\n\t\ttypename TEnableIf<TIsSame<FBool, bool>::Value, FObject&>::Type\n\t\t\tSet(const FString& Key, FBool Boolean)                     { Object->SetField(Key, MakeShared<FJsonxValueBoolean>(Boolean));       return *this; }\n\n\t\tFObject& Set(const FString& Key, TYPE_OF_NULLPTR)              { Object->SetField(Key, MakeShared<FJsonxValueNull>());                 return *this; }\n\n\t\tFObject& Set(const FString& Key, TSharedPtr<FJsonxValue> Value) { Object->SetField(Key, Value ? Value : MakeShared<FJsonxValueNull>()); return *this; }\n\n\t\tvoid CopyIf(const FJsonxObject& Src, TFunctionRef<bool (const FString&, const FJsonxValue&)> Pred)\n\t\t{\n\t\t\tfor (const TPair<FString, TSharedPtr<FJsonxValue>>& KV: Src.Values)\n\t\t\t{\n\t\t\t\tif (ensure(KV.Value) && Pred(KV.Key, *KV.Value))\n\t\t\t\t{\n\t\t\t\t\tObject->SetField(KV.Key, KV.Value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tprivate:\n\t\tTSharedRef<FJsonxObject> Object;\n\t};\n\n\tclass FArray\n\t{\n\tpublic:\n\t\tTSharedRef<FJsonxValueArray> AsJsonxValue() const\n\t\t{\n\t\t\treturn MakeShared<FJsonxValueArray>(Array);\n\t\t}\n\n\t\ttemplate <template <class> class TPrintPolicy = TPrettyJsonxPrintPolicy>\n\t\tFString ToString() const\n\t\t{\n\t\t\tFString Result;\n\t\t\tauto JsonxWriter = TJsonxWriterFactory<TCHAR, TPrintPolicy<TCHAR>>::Create(&Result);\n\t\t\tFJsonxSerializer::Serialize(Array, JsonxWriter);\n\t\t\treturn Result;\n\t\t}\n\n\t\tint Num() const\n\t\t{\n\t\t\treturn Array.Num();\n\t\t}\n\n\t\tFArray& Add(const FArray& Arr)            { Array.Emplace(Arr.AsJsonxValue());                      return *this; }\n\t\tFArray& Add(const FObject& Obj)           { Array.Emplace(Obj.AsJsonxValue());                      return *this; }\n\n\t\tFArray& Add(const FString& Str)           { Array.Emplace(MakeShared<FJsonxValueString>(Str));      return *this; }\n\n\t\ttemplate <class FNumber>\n\t\ttypename TEnableIf<TIsIntegral<FNumber>::Value || TIsFloatingPoint<FNumber>::Value, FArray&>::Type\n\t\t\tAdd(FNumber Number)                   { Array.Emplace(MakeShared<FJsonxValueNumber>(Number));   return *this; }\n\n\t\tFArray& Add(bool Boolean)                 { Array.Emplace(MakeShared<FJsonxValueBoolean>(Boolean)); return *this; }\n\t\tFArray& Add(TYPE_OF_NULLPTR)              { Array.Emplace(MakeShared<FJsonxValueNull>());           return *this; }\n\n\t\tFArray& Add(TSharedPtr<FJsonxValue> Value) { Array.Emplace(Value);                                  return *this; }\n\n\t\t/** Add multiple values */\n\t\ttemplate <class... FValue>\n\t\ttypename TEnableIf<(sizeof...(FValue) > 1), FArray&>::Type\n\t\t\tAdd(FValue&&... Value)\n\t\t{\n\t\t\t// This should be implemented with a fold expression when our compilers support it\n\t\t\tint Temp[] = {0, (Add(Forward<FValue>(Value)), 0)...};\n\t\t\t(void)Temp;\n\t\t\treturn *this;\n\t\t}\n\n\t\tvoid CopyIf(const TArray<TSharedPtr<FJsonxValue>>& Src, TFunctionRef<bool (const FJsonxValue&)> Pred)\n\t\t{\n\t\t\tfor (const TSharedPtr<FJsonxValue>& Value: Src)\n\t\t\t{\n\t\t\t\tif (ensure(Value) && Pred(*Value))\n\t\t\t\t{\n\t\t\t\t\tArray.Emplace(Value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tprivate:\n\t\tTArray<TSharedPtr<FJsonxValue>> Array;\n\t};\n};\n"
  },
  {
    "path": "Source/JsonxUtilities/Public/JsonxObjectConverter.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"UObject/Class.h\"\n#include \"Serialization/JsonxTypes.h\"\n#include \"Dom/JsonxObject.h\"\n#include \"Serialization/JsonxReader.h\"\n#include \"Serialization/JsonxSerializer.h\"\n#include \"JsonxObjectWrapper.h\"\n\n/** Class that handles converting Jsonx objects to and from UStructs */\nclass JSONXUTILITIES_API FJsonxObjectConverter\n{\npublic:\n\n\t/** FName case insensitivity can make the casing of UPROPERTIES unpredictable. Attempt to standardize output. */\n\tstatic FString StandardizeCase(const FString &StringIn);\n\n\t/** Parse an FText from a json object (assumed to be of the form where keys are culture codes and values are strings) */\n\tstatic bool GetTextFromObject(const TSharedRef<FJsonxObject>& Obj, FText& TextOut);\n\n\t/** Convert a Jsonx value to text (takes some hints from the value name) */\n\tstatic bool GetTextFromField(const FString& FieldName, const TSharedPtr<FJsonxValue>& FieldValue, FText& TextOut);\n\npublic: // UStruct -> JSONX\n\n\t/**\n\t * Optional callback that will be run when exporting a single property to Jsonx.\n\t * If this returns a valid value it will be inserted into the export chain.\n\t * If this returns nullptr or is not bound, it will try generic type-specific export behavior before falling back to outputting ExportText as a string.\n\t */\n\tDECLARE_DELEGATE_RetVal_TwoParams(TSharedPtr<FJsonxValue>, CustomExportCallback, FProperty* /* Property */, const void* /* Value */);\n\n\t/**\n\t * Utility Export Callback for having object properties expanded to full Jsonx.\n\t */\n\tUE_DEPRECATED(4.25, \"ObjectJsonxCallback has been deprecated - please remove the usage of it from your project\")\n\tstatic TSharedPtr<FJsonxValue> ObjectJsonxCallback(FProperty* Property , const void* Value);\n\n\t/**\n\t * Templated version of UStructToJsonxObject to try and make most of the params. Also serves as an example use case\n\t *\n\t * @param InStruct The UStruct instance to read from\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t * @return FJsonxObject pointer. Invalid if an error occurred.\n\t */\n\ttemplate<typename InStructType>\n\tstatic TSharedPtr<FJsonxObject> UStructToJsonxObject(const InStructType& InStruct, int64 CheckFlags = 0, int64 SkipFlags = 0, const CustomExportCallback* ExportCb = nullptr)\n\t{\n\t\tTSharedRef<FJsonxObject> JsonxObject = MakeShared<FJsonxObject>();\n\t\tif (UStructToJsonxObject(InStructType::StaticStruct(), &InStruct, JsonxObject, CheckFlags, SkipFlags, ExportCb))\n\t\t{\n\t\t\treturn JsonxObject;\n\t\t}\n\t\treturn TSharedPtr<FJsonxObject>(); // something went wrong\n\t}\n\n\t/**\n\t * Converts from a UStruct to a Jsonx Object, using exportText\n\t *\n\t * @param StructDefinition UStruct definition that is looked over for properties\n\t * @param Struct The UStruct instance to copy out of\n\t * @param JsonxObject Jsonx Object to be filled in with data from the ustruct\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t *\n\t * @return False if any properties failed to write\n\t */\n\tstatic bool UStructToJsonxObject(const UStruct* StructDefinition, const void* Struct, TSharedRef<FJsonxObject> OutJsonxObject, int64 CheckFlags = 0, int64 SkipFlags = 0, const CustomExportCallback* ExportCb = nullptr);\n\n\t/**\n\t * Converts from a UStruct to a json string containing an object, using exportText\n\t *\n\t * @param StructDefinition UStruct definition that is looked over for properties\n\t * @param Struct The UStruct instance to copy out of\n\t * @param JsonxObject Jsonx Object to be filled in with data from the ustruct\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t * @param Indent How many tabs to add to the json serializer\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t * @param bPrettyPrint Option to use pretty print (e.g., adds line endings) or condensed print\n\t *\n\t * @return False if any properties failed to write\n\t */\n\tstatic bool UStructToJsonxObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonxString, int64 CheckFlags = 0, int64 SkipFlags = 0, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr, bool bPrettyPrint = true);\n\n\t/**\n\t * Templated version; Converts from a UStruct to a json string containing an object, using exportText\n\t *\n\t * @param Struct The UStruct instance to copy out of\n\t * @param JsonxObject Jsonx Object to be filled in with data from the ustruct\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t * @param Indent How many tabs to add to the json serializer\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t * @param bPrettyPrint Option to use pretty print (e.g., adds line endings) or condensed print\n\t *\n\t * @return False if any properties failed to write\n\t */\n\ttemplate<typename InStructType>\n\tstatic bool UStructToJsonxObjectString(const InStructType& InStruct, FString& OutJsonxString, int64 CheckFlags = 0, int64 SkipFlags = 0, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr, bool bPrettyPrint = true)\n\t{\n\t\treturn UStructToJsonxObjectString(InStructType::StaticStruct(), &InStruct, OutJsonxString, CheckFlags, SkipFlags, Indent, ExportCb, bPrettyPrint);\n\t}\n\n\t/**\n\t * Wrapper to UStructToJsonxObjectString that allows a print policy to be specified.\n\t */\n\ttemplate<typename CharType, template<typename> class PrintPolicy>\n\tstatic bool UStructToFormattedJsonxObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonxString, int64 CheckFlags = 0, int64 SkipFlags = 0, int32 Indent = 0, const CustomExportCallback* ExportCb = nullptr)\n\t{\n\t\tTSharedRef<FJsonxObject> JsonxObject = MakeShareable(new FJsonxObject());\n\t\tif (UStructToJsonxObject(StructDefinition, Struct, JsonxObject, CheckFlags, SkipFlags, ExportCb))\n\t\t{\n\t\t\tTSharedRef<TJsonxWriter<CharType, PrintPolicy<CharType>>> JsonxWriter = TJsonxWriterFactory<CharType, PrintPolicy<CharType>>::Create(&OutJsonxString, Indent);\n\n\t\t\tif (FJsonxSerializer::Serialize(JsonxObject, JsonxWriter))\n\t\t\t{\n\t\t\t\tJsonxWriter->Close();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"UStructToFormattedObjectString - Unable to write out json\"));\n\t\t\t\tJsonxWriter->Close();\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Converts from a UStruct to a set of json attributes (possibly from within a JsonxObject)\n\t *\n\t * @param StructDefinition UStruct definition that is looked over for properties\n\t * @param Struct The UStruct instance to copy out of\n\t * @param JsonxAttributes Map of attributes to copy in to\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t *\n\t * @return False if any properties failed to write\n\t */\n\tstatic bool UStructToJsonxAttributes(const UStruct* StructDefinition, const void* Struct, TMap< FString, TSharedPtr<FJsonxValue> >& OutJsonxAttributes, int64 CheckFlags = 0, int64 SkipFlags = 0, const CustomExportCallback* ExportCb = nullptr);\n\n\t/* * Converts from a FProperty to a Jsonx Value using exportText\n\t *\n\t * @param Property\t\t\tThe property to export\n\t * @param Value\t\t\t\tPointer to the value of the property\n\t * @param CheckFlags\t\tOnly convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags\t\t\tSkip properties that match any of these flags\n\t * @param ExportCb Optional callback to override export behavior, if this returns null it will fallback to the default\n\t * @param OuterProperty\t\tIf applicable, the Array/Set/Map Property that contains this property\n\t *\n\t * @return\t\t\t\t\tThe constructed JsonxValue from the property\n\t */\n\tstatic TSharedPtr<FJsonxValue> UPropertyToJsonxValue(FProperty* Property, const void* Value, int64 CheckFlags = 0, int64 SkipFlags = 0, const CustomExportCallback* ExportCb = nullptr, FProperty* OuterProperty = nullptr);\n\npublic: // JSONX -> UStruct\n\n\t/**\n\t * Converts from a Jsonx Object to a UStruct, using importText\n\t *\n\t * @param JsonxObject Jsonx Object to copy data out of\n\t * @param StructDefinition UStruct definition that is looked over for properties\n\t * @param Struct The UStruct instance to copy in to\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t *\n\t * @return False if any properties matched but failed to deserialize\n\t */\n\tstatic bool JsonxObjectToUStruct(const TSharedRef<FJsonxObject>& JsonxObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0);\n\n\t/**\n\t * Templated version of JsonxObjectToUStruct\n\t *\n\t * @param JsonxObject Jsonx Object to copy data out of\n\t * @param OutStruct The UStruct instance to copy in to\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t *\n\t * @return False if any properties matched but failed to deserialize\n\t */\n\ttemplate<typename OutStructType>\n\tstatic bool JsonxObjectToUStruct(const TSharedRef<FJsonxObject>& JsonxObject, OutStructType* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0)\n\t{\n\t\treturn JsonxObjectToUStruct(JsonxObject, OutStructType::StaticStruct(), OutStruct, CheckFlags, SkipFlags);\n\t}\n\n\t/**\n\t * Converts a set of json attributes (possibly from within a JsonxObject) to a UStruct, using importText\n\t *\n\t * @param JsonxAttributes Jsonx Object to copy data out of\n\t * @param StructDefinition UStruct definition that is looked over for properties\n\t * @param OutStruct The UStruct instance to copy in to\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t *\n\t * @return False if any properties matched but failed to deserialize\n\t */\n\tstatic bool JsonxAttributesToUStruct(const TMap< FString, TSharedPtr<FJsonxValue> >& JsonxAttributes, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0);\n\n\t/**\n\t * Converts a single JsonxValue to the corresponding FProperty (this may recurse if the property is a UStruct for instance).\n\t *\n\t * @param JsonxValue The value to assign to this property\n\t * @param Property The FProperty definition of the property we're setting.\n\t * @param OutValue Pointer to the property instance to be modified.\n\t * @param CheckFlags Only convert sub-properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip sub-properties that match any of these flags\n\t *\n\t * @return False if the property failed to serialize\n\t */\n\tstatic bool JsonxValueToUProperty(const TSharedPtr<FJsonxValue>& JsonxValue, FProperty* Property, void* OutValue, int64 CheckFlags = 0, int64 SkipFlags = 0);\n\n\t/**\n\t * Converts from a json string containing an object to a UStruct\n\t *\n\t * @param JsonxString String containing JSONX formatted data.\n\t * @param OutStruct The UStruct instance to copy in to\n\t * @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t * @param SkipFlags Skip properties that match any of these flags\n\t *\n\t * @return False if any properties matched but failed to deserialize\n\t */\n\ttemplate<typename OutStructType>\n\tstatic bool JsonxObjectStringToUStruct(const FString& JsonxString, OutStructType* OutStruct, int64 CheckFlags = 0, int64 SkipFlags = 0)\n\t{\n\t\tTSharedPtr<FJsonxObject> JsonxObject;\n\t\tTSharedRef<TJsonxReader<> > JsonxReader = TJsonxReaderFactory<>::Create(JsonxString);\n\t\tif (!FJsonxSerializer::Deserialize(JsonxReader, JsonxObject) || !JsonxObject.IsValid())\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxObjectStringToUStruct - Unable to parse json=[%s]\"), *JsonxString);\n\t\t\treturn false;\n\t\t}\n\t\tif (!FJsonxObjectConverter::JsonxObjectToUStruct(JsonxObject.ToSharedRef(), OutStruct, CheckFlags, SkipFlags))\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxObjectStringToUStruct - Unable to deserialize. json=[%s]\"), *JsonxString);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t* Converts from a json string containing an array to an array of UStructs\n\t*\n\t* @param JsonxString String containing JSONX formatted data.\n\t* @param OutStructArray The UStruct array to copy in to\n\t* @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t* @param SkipFlags Skip properties that match any of these flags.\n\t*\n\t* @return False if any properties matched but failed to deserialize.\n\t*/\n\ttemplate<typename OutStructType>\n\tstatic bool JsonxArrayStringToUStruct(const FString& JsonxString, TArray<OutStructType>* OutStructArray, int64 CheckFlags = 0, int64 SkipFlags = 0)\n\t{\n\t\tTArray<TSharedPtr<FJsonxValue> > JsonxArray;\n\t\tTSharedRef<TJsonxReader<> > JsonxReader = TJsonxReaderFactory<>::Create(JsonxString);\n\t\tif (!FJsonxSerializer::Deserialize(JsonxReader, JsonxArray))\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxArrayStringToUStruct - Unable to parse. json=[%s]\"), *JsonxString);\n\t\t\treturn false;\n\t\t}\n\t\tif (!JsonxArrayToUStruct(JsonxArray, OutStructArray, CheckFlags, SkipFlags))\n\t\t{\n\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxArrayStringToUStruct - Error parsing one of the elements. json=[%s]\"), *JsonxString);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t* Converts from an array of json values to an array of UStructs.\n\t*\n\t* @param JsonxArray Array containing json values to convert.\n\t* @param OutStructArray The UStruct array to copy in to\n\t* @param CheckFlags Only convert properties that match at least one of these flags. If 0 check all properties.\n\t* @param SkipFlags Skip properties that match any of these flags.\n\t*\n\t* @return False if any of the matching elements are not an object, or if one of the matching elements could not be converted to the specified UStruct type.\n\t*/\n\ttemplate<typename OutStructType>\n\tstatic bool JsonxArrayToUStruct(const TArray<TSharedPtr<FJsonxValue>>& JsonxArray, TArray<OutStructType>* OutStructArray, int64 CheckFlags = 0, int64 SkipFlags = 0)\n\t{\n\t\tOutStructArray->SetNum(JsonxArray.Num());\n\t\tfor (int32 i = 0; i < JsonxArray.Num(); ++i)\n\t\t{\n\t\t\tconst auto& Value = JsonxArray[i];\n\t\t\tif (Value->Type != EJsonx::Object)\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxArrayToUStruct - Array element [%i] was not an object.\"), i);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!FJsonxObjectConverter::JsonxObjectToUStruct(Value->AsObject().ToSharedRef(), OutStructType::StaticStruct(), &(*OutStructArray)[i], CheckFlags, SkipFlags))\n\t\t\t{\n\t\t\t\tUE_LOG(LogJsonx, Warning, TEXT(\"JsonxArrayToUStruct - Unable to convert element [%i].\"), i);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*\n\t* Parses text arguments from Jsonx into a map\n\t* @param JsonxObject Object to parse arguments from\n\t*/\n\tstatic FFormatNamedArguments ParseTextArgumentsFromJsonx(const TSharedPtr<const FJsonxObject>& JsonxObject);\n};\n"
  },
  {
    "path": "Source/JsonxUtilities/Public/JsonxObjectWrapper.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"UObject/ObjectMacros.h\"\n#include \"UObject/Object.h\"\n#include \"UObject/Class.h\"\n\n#include \"JsonxObjectWrapper.generated.h\"\n\nclass FJsonxObject;\n\n/** UStruct that holds a JsonxObject, can be used by structs passed to JsonxObjectConverter to pass through JsonxObjects directly */\nUSTRUCT(BlueprintType)\nstruct JSONXUTILITIES_API FJsonxObjectWrapper\n{\n\tGENERATED_USTRUCT_BODY()\npublic:\n\n\tUPROPERTY(EditAnywhere, Category = \"JSONX\")\n\tFString JsonxString;\n\n\tTSharedPtr<FJsonxObject> JsonxObject;\n\n\tbool ImportTextItem(const TCHAR*& Buffer, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText);\n\tbool ExportTextItem(FString& ValueStr, FJsonxObjectWrapper const& DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const;\n\tvoid PostSerialize(const FArchive& Ar);\n\n\texplicit operator bool() const\n\t{\n\t\treturn JsonxObject.IsValid();\n\t}\n\n\tbool JsonxObjectToString(FString& Str) const;\n\tbool JsonxObjectFromString(const FString& Str);\n};\n\ntemplate<>\nstruct TStructOpsTypeTraits<FJsonxObjectWrapper> : public TStructOpsTypeTraitsBase2<FJsonxObjectWrapper>\n{\n\tenum\n\t{\n\t\tWithImportTextItem = true,\n\t\tWithExportTextItem = true,\n\t\tWithPostSerialize = true,\n\t};\n};\nUCLASS()\nclass UJsonxUtilitiesDummyObject : public UObject\n{\n\tGENERATED_BODY()\n};\n"
  },
  {
    "path": "Source/JsonxUtilities/Public/JsonxUtilities.h",
    "content": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"Misc/MonolithicHeaderBoilerplate.h\"\nMONOLITHIC_HEADER_BOILERPLATE()\n\n#include \"Jsonx.h\"\n#include \"JsonxObjectConverter.h\"\n#include \"JsonxObjectWrapper.h\"\n\n"
  },
  {
    "path": "Source/UHMP/AgentBaseCpp.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"AgentBaseCpp.h\"\n\n// Sets default values\nAAgentBaseCpp::AAgentBaseCpp()\n{\n \t// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.\n\tPrimaryActorTick.bCanEverTick = true;\n\n}\n\n// Called when the game starts or when spawned\nvoid AAgentBaseCpp::BeginPlay()\n{\n\tSuper::BeginPlay();\n\t\n}\n\n\n// Called to bind functionality to input\nvoid AAgentBaseCpp::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)\n{\n\tSuper::SetupPlayerInputComponent(PlayerInputComponent);\n\n}\n\n"
  },
  {
    "path": "Source/UHMP/AgentBaseCpp.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"GameFramework/Character.h\"\n#include \"GenericTeamAgentInterface.h\"\n#include \"AgentBaseCpp.generated.h\"\n\nUCLASS()\nclass UHMP_API AAgentBaseCpp : public ACharacter, public IGenericTeamAgentInterface\n{\n\tGENERATED_BODY()\n\npublic:\n\t// Sets default values for this character's properties\n\tAAgentBaseCpp();\n\tUPROPERTY(BlueprintReadWrite)\n\t\tFGenericTeamId GenericTeamNo;\n\n\nprotected:\n\t// Called when the game starts or when spawned\n\tvirtual void BeginPlay() override;\n\n\n\npublic:\t\n\n\t// Called to bind functionality to input\n\tvirtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;\n\n\n\tvirtual FGenericTeamId GetGenericTeamId() const override { return GenericTeamNo; }\n\n\n};\n"
  },
  {
    "path": "Source/UHMP/DataStruct.h",
    "content": "#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Containers/UnrealString.h\"\n#include \"XtensorAPIBPLibrary.h\"\n#include \"DataStruct.generated.h\"\n\nUSTRUCT(BlueprintType)\nstruct FAgentProperty\n{\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString ClassName = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool DebugAgent = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator InitRotator;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString WeaponType = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Color = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat DodgeProb = 0.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat ExplodeDmg = 20.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat FireRange = 1000.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat GuardRange = 1400.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat PerceptionRange = 1400.0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD2 = \"\";\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FParsedDataInput\n{\n\t// please change lines in \n\t// bool AHMPLevelScriptActor::ParsedTcpInData()\n\t// together with this struct\n\n\tGENERATED_BODY()\n\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString DataCmd;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint NumAgents = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FAgentProperty> AgentSettingArray;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeStep = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeStepMax = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<float> Actions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> StringActions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FAgentDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool AgentAlive = true;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator AgentRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint PreviousAction;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> AvailActions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> AgentPerception;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Reward;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> Interaction;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FKeyObjDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString ClassName;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Location;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFRotator Rotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Scale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector Velocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Hp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n};\n\n\n\n\n\n\nUSTRUCT(BlueprintType)\nstruct FGlobalDataOutput\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat TeamReward = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool UseTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> Events;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> VisibleMatFlatten;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<float> DisMatFlatten;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxEpisodeStep = 999;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeCnt = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Time = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool EpisodeDone = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString EpisodeEndReason = \"unknown\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TeamWin = -1;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FKeyObjDataOutput> KeyObjArr;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString LevelName = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFXTensor DistanceMat;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString RSVD1 = \"\";\n\n\n\n};\n\nUSTRUCT(BlueprintType)\nstruct FAgentDataOutputArr\n{\n\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FAgentDataOutput> DataArr;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFGlobalDataOutput DataGlobal;\n};\n"
  },
  {
    "path": "Source/UHMP/HMPAIController.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"HMPAIController.h\"\n\n"
  },
  {
    "path": "Source/UHMP/HMPAIController.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"AIController.h\"\n#include \"HMPAIController.generated.h\"\n\n/**\n * \n */\nUCLASS()\nclass UHMP_API AHMPAIController : public AAIController\n{\n\tGENERATED_BODY()\n\t\n};\n"
  },
  {
    "path": "Source/UHMP/HMPLevelScriptActor.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"HMPLevelScriptActor.h\"\n#include \"Math/UnrealMathUtility.h\""
  },
  {
    "path": "Source/UHMP/HMPLevelScriptActor.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n\n#include \"CoreMinimal.h\"\n#include \"Networking.h\"\n#include \"Engine/LevelScriptActor.h\"\n#include \"Containers/UnrealString.h\"\n#include \"HMPLevelScriptActor.generated.h\"\n\n/**\n *\n */\n\nUSTRUCT(BlueprintType)\nstruct FAgentSetting\n{\n\tGENERATED_USTRUCT_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString ClassName = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool AcceptRLControl = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector InitVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxEpisodeStep = 999;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString InitBuff = \"\";\n};\n\n\n\nUSTRUCT(BlueprintType)\nstruct FParsedTcpInData\n{\n\t// please change lines in \n\t// bool AHMPLevelScriptActor::ParsedTcpInData()\n\t// together with this struct\n\n\tGENERATED_USTRUCT_BODY()\n\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString DataCmd;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint NumAgents = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FAgentSetting> AgentSettingArray;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeStep = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<float> Actions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FString> StringActions;\n\n};\n\n\nUSTRUCT(BlueprintType)\nstruct FTcpOutAgentData\n{\n\t// please change lines in \n\t// bool AHMPLevelScriptActor::ParsedTcpInData()\n\t// together with this struct\n\n\tGENERATED_USTRUCT_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool AgentAlive = true;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint AgentTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint IndexInTeam = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint UID = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool AcceptRLControl = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxMoveSpeed = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentLocation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentRotation;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentScale;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFVector AgentVelocity;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat AgentHp;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat WeaponCD = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat MaxEpisodeStep = 999;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint TimeCnt = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Time = 0;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tint PreviousAction;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<int> AvailActions;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tfloat Reward;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool IsTeamReward = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool EpisodeDone = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString Type = \"\";\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tFString CurrentBuff = \"\";\n};\n\n\nUSTRUCT(BlueprintType)\nstruct FTcpOutAgentDataArr\n{\n\t// please change lines in \n\t// bool AHMPLevelScriptActor::ParsedTcpInData()\n\t// together with this struct\n\n\tGENERATED_USTRUCT_BODY()\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tbool Valid = false;\n\n\tUPROPERTY(EditDefaultsOnly, BlueprintReadWrite)\n\t\tTArray<FTcpOutAgentData> DataArr;\n};\n\nUCLASS()\nclass UHMP_API AHMPLevelScriptActor : public ALevelScriptActor\n{\n\tGENERATED_BODY()\n\n};\n\n\n"
  },
  {
    "path": "Source/UHMP/HmpCrowdFollowingComponent.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n#include \"HmpCrowdFollowingComponent.h\"\n#include \"Navigation/MetaNavMeshPath.h\"\n\nvoid UHmpCrowdFollowingComponent::UpdatePathSegment()\n{\n\tif (GEngine)\n\t{\n\t\tGEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, \"Hack is Done\");\n\t}\n\tif (!IsCrowdSimulationEnabled())\n\t{\n\t\tSuper::UpdatePathSegment();\n\t\treturn;\n\t}\n\n\tif (!Path.IsValid() || MovementComp == NULL)\n\t{\n\t\tOnPathFinished(FPathFollowingResult(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath));\n\t\treturn;\n\t}\n\n\tif (!Path->IsValid())\n\t{\n\t\tif (!Path->IsWaitingForRepath())\n\t\t{\n\t\t\t//UE_VLOG(this, LogPathFollowing, Log, TEXT(\"Aborting move due to path being invalid and not waiting for repath\"));\n\t\t\tOnPathFinished(FPathFollowingResult(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath));\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// continue with execution, if navigation is being rebuild constantly AI will get stuck with current waypoint\n\t\t\t// path updates should be still coming in, even though they get invalidated right away\n\t\t\t//UE_VLOG(this, LogPathFollowing, Log, TEXT(\"Updating path points in invalid & pending path!\"));\n\t\t}\n\t}\n\n\t// if agent has control over its movement, check finish conditions\n\tconst FVector CurrentLocation = MovementComp->GetActorFeetLocation();\n\tconst bool bCanReachTarget = MovementComp->CanStopPathFollowing();\n\tif (bCanReachTarget && Status == EPathFollowingStatus::Moving)\n\t{\n\t\tconst FVector GoalLocation = GetCurrentTargetLocation();\n\n\t\tif (bCollidedWithGoal)\n\t\t{\n\t\t\t// check if collided with goal actor\n\t\t\tOnSegmentFinished();\n\t\t\tOnPathFinished(FPathFollowingResult(EPathFollowingResult::Success, FPathFollowingResultFlags::None));\n\t\t}\n\t\telse if (bFinalPathPart)\n\t\t{\n\t\t\tconst FVector ToTarget = (GoalLocation - MovementComp->GetActorFeetLocation());\n\t\t\tconst bool bMovedTooFar = false; // (bCheckMovementAngle || bCanCheckMovingTooFar) && !CrowdAgentMoveDirection.IsNearlyZero() && FVector::DotProduct(ToTarget, CrowdAgentMoveDirection) < 0.0;\n\n//#if ENABLE_VISUAL_LOG\n//\t\t\tif (bMovedTooFar)\n//\t\t\t{\n//\t\t\t\tconst FVector AgentLoc = MovementComp->GetActorFeetLocation();\n//\t\t\t\tUE_VLOG_SEGMENT(GetOwner(), LogCrowdFollowing, Log, AgentLoc, AgentLoc + CrowdAgentMoveDirection * 100.0f, FColor::Cyan, TEXT(\"moveDir\"));\n//\t\t\t\tUE_VLOG_SEGMENT(GetOwner(), LogCrowdFollowing, Log, AgentLoc, AgentLoc + ToTarget.GetSafeNormal() * 100.0f, FColor::Cyan, TEXT(\"toTarget\"));\n//\t\t\t\tUE_VLOG(GetOwner(), LogCrowdFollowing, Log, TEXT(\"Moved too far, dotValue: %.2f (normalized dot: %.2f) velocity:%s (speed:%.0f)\"),\n//\t\t\t\t\tFVector::DotProduct(ToTarget, CrowdAgentMoveDirection),\n//\t\t\t\t\tFVector::DotProduct(ToTarget.GetSafeNormal(), CrowdAgentMoveDirection),\n//\t\t\t\t\t*MovementComp->Velocity.ToString(),\n//\t\t\t\t\tMovementComp->Velocity.Size()\n//\t\t\t\t);\n//\t\t\t}\n//#endif\n\n\t\t\t// can't use HasReachedDestination here, because it will use last path point\n\t\t\t// which is not set correctly for partial paths without string pulling\n\t\t\tconst float UseAcceptanceRadius = GetFinalAcceptanceRadius(*Path, OriginalMoveRequestGoalLocation, &GoalLocation);\n\t\t\tif (bMovedTooFar || HasReachedInternal(GoalLocation, 0.0f, 0.0f, CurrentLocation, UseAcceptanceRadius, bReachTestIncludesAgentRadius ? MinAgentRadiusPct : 0.0f))\n\t\t\t{\n\t\t\t\t//UE_VLOG(GetOwner(), LogCrowdFollowing, Log, TEXT(\"Last path segment finished due to \\'%s\\'\"), bMovedTooFar ? TEXT(\"Missing Last Point\") : TEXT(\"Reaching Destination\"));\n\t\t\t\tOnPathFinished(FPathFollowingResult(EPathFollowingResult::Success, FPathFollowingResultFlags::None));\n\t\t\t}\n\t\t}\n\t\telse if (bCanUpdatePathPartInTick)\n\t\t{\n\t\t\t// override radius multiplier and switch to next path part when closer than 4x agent radius\n\t\t\tconst float NextPartMultiplier = 4.0f;\n\t\t\tconst bool bHasReached = HasReachedInternal(GoalLocation, 0.0f, 0.0f, CurrentLocation, 0.0f, NextPartMultiplier);\n\n\t\t\tif (bHasReached)\n\t\t\t{\n\t\t\t\tSwitchToNextPathPart();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bCanReachTarget && Status == EPathFollowingStatus::Moving)\n\t{\n\t\t// check waypoint switch condition in meta paths\n\t\tFMetaNavMeshPath* MetaNavPath = bIsUsingMetaPath ? Path->CastPath<FMetaNavMeshPath>() : nullptr;\n\t\tif (MetaNavPath && Status == EPathFollowingStatus::Moving)\n\t\t{\n\t\t\tMetaNavPath->ConditionalMoveToNextSection(CurrentLocation, EMetaPathUpdateReason::MoveTick);\n\t\t}\n\n\t\t// gather location samples to detect if moving agent is blocked\n\t\tconst bool bHasNewSample = UpdateBlockDetection();\n\t\tif (bHasNewSample && IsBlocked())\n\t\t{\n\t\t\tOnPathFinished(FPathFollowingResult(EPathFollowingResult::Blocked, FPathFollowingResultFlags::None));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Source/UHMP/HmpCrowdFollowingComponent.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n#include \"CoreMinimal.h\"\n#include \"UObject/ObjectMacros.h\"\n#include \"UObject/WeakInterfacePtr.h\"\n#include \"EngineDefines.h\"\n#include \"AITypes.h\"\n#include \"Navigation/PathFollowingComponent.h\"\n#include \"Navigation/CrowdAgentInterface.h\"\n#include \"AI/Navigation/NavigationAvoidanceTypes.h\"\n#include \"Navigation/CrowdFollowingComponent.h\"\n#include \"HmpCrowdFollowingComponent.generated.h\"\n\n/**\n * \n */\nUCLASS(ClassGroup = AI, HideCategories = (Activation, Collision), meta = (BlueprintSpawnableComponent), config = Game)\nclass UHMP_API UHmpCrowdFollowingComponent : public UCrowdFollowingComponent\n{\n\tGENERATED_BODY()\n\nprotected:\n\tvoid UpdatePathSegment() override;\n\t\n};\n"
  },
  {
    "path": "Source/UHMP/HmpPythonIO.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"HmpPythonIO.h\"\n\n// Sets default values\nAHmpPythonIO::AHmpPythonIO()\n{\n\t// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.\n\tPrimaryActorTick.bCanEverTick = true;\n\n}\n\nvoid AHmpPythonIO::BeginDestroy()\n{\n\tSuper::BeginDestroy();\n\tif (TcpAccpetedSocket)\n\t{\n\t\tTcpAccpetedSocket->Close();\n\t}\n\tif (ListenSocket)\n\t{\n\t\tListenSocket->Close();\n\t}\n}\n\n\nvoid AHmpPythonIO::StartTcpServer(int32 InListenPort)\n{\n\tFIPv4Address Address;\n\tFIPv4Address::Parse(TEXT(\"0.0.0.0\"), Address);\n\n\t//Create Socket\n\tFIPv4Endpoint Endpoint(Address, InListenPort);\n\tFString ListenSocketName = TEXT(\"ue4-tcp-server\");\n\tListenSocket = FTcpSocketBuilder(*ListenSocketName)\n\t\t.AsBlocking()\n\t\t.AsReusable()\n\t\t.BoundToEndpoint(Endpoint);\n\tint32 setBufferSizeRes = 0;\n\tListenSocket->SetReceiveBufferSize(ReceiveBufferSize, setBufferSizeRes);\n\tListenSocket->SetSendBufferSize(SendBufferSize, setBufferSizeRes);\n\tListenSocket->Listen(8);\n\n}\n\n\nvoid AHmpPythonIO::TcpServerSendJson(TSharedPtr<FJsonxObject> ReplyJson, float& encTime, float& sendTime)\n{\n\tdouble t1 = FPlatformTime::Seconds();\n\n\tFString ReplyString;\n\tauto Writer = TJsonxWriterFactory<TCHAR, TCondensedJsonxPrintPolicy<TCHAR>>::Create(&ReplyString);\n\tFJsonxSerializer::Serialize(ReplyJson.ToSharedRef(), Writer);\n\tTCHAR* serializedChar = ReplyString.GetCharArray().GetData();\n\tFTCHARToUTF8 Converted(serializedChar);\n\tuint8* sendbuf = (uint8*)Converted.Get();\n\tint32 buffsize_raw = Converted.Length();\n\n\t// use XLZ4 built in unreal engine core to compress data before send, reducing IPC cost\n\tint dstSize = XLZ4_compress_fast((const char*)sendbuf, (char*)SendBuffer, buffsize_raw, SendBufferSize, 1);\n\tbuffsize_raw = dstSize;\n\n\tdouble t2 = FPlatformTime::Seconds();\n\n\n\t// add checkEOF\n\tSendBuffer[buffsize_raw]     = 0xaa;\n\tSendBuffer[buffsize_raw + 1] = 0x55;\n\tSendBuffer[buffsize_raw + 2] = 0xaa;\n\tSendBuffer[buffsize_raw + 3] = 'H';\n\tSendBuffer[buffsize_raw + 4] = 'M';\n\tSendBuffer[buffsize_raw + 5] = 'P';\n\tSendBuffer[buffsize_raw + 6] = 0xaa;\n\tSendBuffer[buffsize_raw + 7] = 0x55;\n\tSendBuffer[buffsize_raw + 8] = '\\0';\n\n\tensureMsgf((buffsize_raw + 8 < SendBufferSize), TEXT(\"send buffer overflow!\"));\t// prevent buffer overflow!\n\tSendBufferUsage = (float)(buffsize_raw + 8) / SendBufferSize;\n\tint32 BytesSent;\n\n\n\n\tTcpAccpetedSocket->Send(SendBuffer, buffsize_raw + 8, BytesSent);\t// tcp send\n\tensureMsgf((BytesSent == buffsize_raw + 8), TEXT(\"send buffer incomplete!\"));\n\n\tdouble t3 = FPlatformTime::Seconds();\n\tencTime = t2 - t1;\n\tsendTime = t3 - t2;\n}\n\nvoid AHmpPythonIO::TcpServerWaitClient()\n{\n\tbool bHasPendingConnection = false;\n\tFTimespan timespan = FTimespan(0, 10, 0);\n\tListenSocket->WaitForPendingConnection(bHasPendingConnection, timespan);\n\tTSharedPtr<FInternetAddr> Addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();\n\tTcpAccpetedSocket = ListenSocket->Accept(*Addr, TEXT(\"tcp-client\"));\n\n}\n\n\nbool AHmpPythonIO::TcpServerHasWaitingClient()\n{\n\tbool bHasPendingConnection = false;\n\tListenSocket->HasPendingConnection(bHasPendingConnection);\n\treturn bHasPendingConnection;\n\n}\n\nbool AHmpPythonIO::TcpServerHasRecvData()\n{\n\tuint32 BytesPendingdata = 0;\n\treturn TcpAccpetedSocket->HasPendingData(BytesPendingdata);\n}\n\nFString AHmpPythonIO::TcpServerRecvBlocked(bool checkEOF, float& tcpWaitTime, float &decodeTime)\n{\n\tESocketConnectionState ConnectionState = TcpAccpetedSocket->GetConnectionState();\n\tif (ConnectionState != ESocketConnectionState::SCS_Connected)\n\t{\n\t\treturn \"\";\n\t}\n\tint32 currenthead = 0;\n\twhile (1)\n\t{\n\t\tint32 BytesRead = 0;\n\t\tuint32 BytesPendingdata = 0;\n\t\tdouble t1 = FPlatformTime::Seconds();\n\t\t// 24小时，有时候测试之间的间隔很长，长达1小时也是有可能的\n\t\tTcpAccpetedSocket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromHours(24));\n\t\tdouble t2 = FPlatformTime::Seconds();\n\t\ttcpWaitTime = t2 - t1;\n\t\tif (!TcpAccpetedSocket->HasPendingData(BytesPendingdata)) {\n\t\t\tensureMsgf(false, TEXT(\"Tcp connection with Python is broken! Exiting ...\"));\n\t\t\treturn \"\";\n\t\t}\n\t\tTcpAccpetedSocket->Recv(&RecvBuffer[currenthead], ReceiveBufferSize, BytesRead);\n\t\tcurrenthead = currenthead + BytesRead;\n\t\tensureMsgf((currenthead + 1 < ReceiveBufferSize), TEXT(\"recv buffer overflow\"));\t// Buffer overflow!\n\t\tRecvBufferUsage = (float)(currenthead + 1) / ReceiveBufferSize;\n\t\tRecvBuffer[currenthead] = '\\0'; // seal the tail of buffer with '\\0'\n\t\tif (checkEOF) \n\t\t{\n\t\t\tif (currenthead >= 8 &&\n\t\t\t\tRecvBuffer[currenthead - 1] == 0x55 && RecvBuffer[currenthead - 2] == 0xaa &&\n\t\t\t\tRecvBuffer[currenthead - 3] == 'P'  && RecvBuffer[currenthead - 4] == 'M'  && RecvBuffer[currenthead - 5] == 'H' &&\n\t\t\t\tRecvBuffer[currenthead - 6] == 0xaa && RecvBuffer[currenthead - 7] == 0x55 && RecvBuffer[currenthead - 8] == 0xaa)\n\t\t\t{\n\t\t\t\t// data ends with pre-defined marker, clear the marker, convert to String\n\t\t\t\tRecvBuffer[currenthead - 8] = '\\0'; RecvBuffer[currenthead - 7] = '\\0'; RecvBuffer[currenthead - 6] = '\\0';\n\t\t\t\tRecvBuffer[currenthead - 5] = '\\0'; RecvBuffer[currenthead - 4] = '\\0'; RecvBuffer[currenthead - 3] = '\\0';\n\t\t\t\tRecvBuffer[currenthead - 2] = '\\0'; RecvBuffer[currenthead - 1] = '\\0';\n\n\t\t\t\t// decompress\n\t\t\t\tint dst_read = XLZ4_decompress_safe((const char*)RecvBuffer, (char*)RecvDecompressBuffer, currenthead - 8, ReceiveBufferSize);\n\t\t\t\tensureMsgf((dst_read>0), TEXT(\"Recv Buffer Overflow!\"));\t// Buffer overflow!\n\t\t\t\tRecvDecompressBuffer[dst_read] = '\\0'; // seal the top of buffer\n\n\t\t\t\tFString RecvString = FString(UTF8_TO_TCHAR(RecvDecompressBuffer));\n\t\t\t\treturn RecvString;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tensureMsgf(false, TEXT(\"must use checkEOF!\"));\t// prevent buffer overflow!\n\t\t\tFString RecvString = FString(UTF8_TO_TCHAR(RecvBuffer));\n\t\t\treturn RecvString;\n\t\t}\n\t\tdouble t3 = FPlatformTime::Seconds();\n\t\tdecodeTime = t3 - t2;\n\t}\n}\n\nFParsedDataInput AHmpPythonIO::ParsedTcpInData(FString TcpLatestRecvString)\n{\n\tstatic const struct FParsedDataInput EmptyStruct;\n\tFParsedDataInput TcpParsedTcpInData = EmptyStruct;\n\ttry \n\t{\n\n\t\tTSharedRef<TJsonxReader<TCHAR>> JsonReader = TJsonxReaderFactory<TCHAR>::Create(TcpLatestRecvString);\n\t\tTSharedPtr<FJsonxObject> JsonObject = MakeShareable(new FJsonxObject);\n\t\tFJsonxSerializer::Deserialize(JsonReader, JsonObject);\n\t\tFJsonxObjectConverter::JsonxObjectToUStruct<FParsedDataInput>(JsonObject.ToSharedRef(), &TcpParsedTcpInData, 0, 0);\n\t\tTcpParsedTcpInData.valid = true;\n\t\treturn TcpParsedTcpInData;\n\n\t}\n\tcatch (...) \n\t{\n\t\tTcpParsedTcpInData.valid = false;\n\t\treturn TcpParsedTcpInData;\n\t}\n\n\n}\n\n\nvoid AHmpPythonIO::ConvertOutDataToJsonAndSendTcp(TArray<FAgentDataOutput> TcpOutDataArr, \n\tFGlobalDataOutput GlobalData, float &toJsonTime, float &encTime, float &sendTime)\n{\n\tdouble t1 = FPlatformTime::Seconds();\n\n\tTSharedRef<FJsonxObject> JsonReply = MakeShareable(new FJsonxObject);\n\tFAgentDataOutputArr Reply;\n\tReply.Valid = true;\n\tReply.DataArr = TcpOutDataArr;\n\tReply.DataGlobal = GlobalData;\n\tFJsonxObjectConverter::UStructToJsonxObject(FAgentDataOutputArr::StaticStruct(), &Reply, JsonReply, 0, 0);\n\ttoJsonTime = FPlatformTime::Seconds() - t1;\n\t//const FString t = FString(\"DataArr\");\n\tauto J_DataArr = JsonReply->TryGetField(\"dataArr\")->AsArray();\n\n\tTArray<TSharedPtr<FJsonxValue>> J_DataArrNew;\n\n\tfor (int i = 0; i < J_DataArr.Num(); i++) \n\t{\n\t\tauto agentJsonDataObj = J_DataArr[i]->AsObject();\n\t\tbool alive = true;\n\t\tagentJsonDataObj->TryGetBoolField(\"agentAlive\", alive);\n\t\tif (!alive) \n\t\t{\n\t\t\tTSharedPtr<FJsonxObject> replacement = MakeShareable(new FJsonxObject);\n\t\t\t// five core property\n\t\t\treplacement->SetBoolField(\"valid\", true);\n\t\t\treplacement->SetBoolField(\"agentAlive\", false);\n\t\t\treplacement->SetNumberField(\"agentTeam\", (int)(agentJsonDataObj->GetNumberField(\"agentTeam\")));\n\t\t\treplacement->SetNumberField(\"indexInTeam\", (int)(agentJsonDataObj->GetNumberField(\"indexInTeam\")));\n\t\t\treplacement->SetNumberField(\"uId\", (int)(agentJsonDataObj->GetNumberField(\"uID\") ));\n\t\t\tJ_DataArrNew.Add(MakeShareable(new FJsonxValueObject(replacement)));\n\t\t}\n\t\telse \n\t\t{\n\t\t\tJ_DataArrNew.Add(J_DataArr[i]);\n\t\t}\n\t}\n\tJsonReply->SetArrayField(\"dataArr\", J_DataArrNew);\n\n\n\t//for (auto t : J_DataArr->AsArray()) \n\t//{\n\t//\tauto tt = t->AsObject();\n\t//\tif (tt) {\n\t//\t\tbool alive = true;\n\t//\t\tif (tt->TryGetBoolField(\"agentAlive\", alive)) {\n\t//\t\t\tif (!alive) {\n\t//\t\t\t\tTSharedPtr<FJsonxObject> replacement = MakeShareable(new FJsonxObject);\n\t//\t\t\t\t// five core property\n\t//\t\t\t\treplacement->SetBoolField(\"valid\", true);\n\t//\t\t\t\treplacement->SetBoolField(\"agentAlive\", false);\n\t//\t\t\t\treplacement->SetNumberField(\"agentTeam\", (int)(tt->GetNumberField(\"agentTeam\")));\n\t//\t\t\t\treplacement->SetNumberField(\"indexInTeam\", (int)(tt->GetNumberField(\"indexInTeam\")));\n\t//\t\t\t\treplacement->SetNumberField(\"uID\", (int)(tt->GetNumberField(\"uID\") ));\n\t//\t\t\t\t*tt = *replacement;\n\t//\t\t\t}\n\t//\t\t}\n\t//\t}\n\t//}\n\n\n\n\tTcpServerSendJson(JsonReply, encTime, sendTime);\n}\n\nvoid AHmpPythonIO::sleep_thread(float second)\n{\n\tFPlatformProcess::Sleep(second);\n}\n\nvoid AHmpPythonIO::ChangeEngineFixedFrameRate(float fps)\n{\n\tif (fps >= 2 && fps <= 5000)\n\t{\n\t\tGEngine->FixedFrameRate = fps;\n\t}\n}\n\nvoid AHmpPythonIO::DisableRendering()\n{\n\tensureMsgf(false, TEXT(\"do not use this func!\"));\n\tGEngine->GameViewport->bDisableWorldRendering = false;\n}\n\nvoid AHmpPythonIO::EnableRendering()\n{\n\tensureMsgf(false, TEXT(\"do not use this func!\"));\n\tGEngine->GameViewport->bDisableWorldRendering = true;\n}\n\n\nvoid AHmpPythonIO::tic()\n{\n\ttic_second = FPlatformTime::Seconds();\n}\n\nfloat AHmpPythonIO::toc()\n{\n\t//FPlatformProcess::Sleep(0.05);\n\ttoc_second = FPlatformTime::Seconds();\n\treturn toc_second - tic_second;\n}\n\nvoid AHmpPythonIO::dur_tic()\n{\n\tdur_tic_second = FPlatformTime::Seconds();\n}\nvoid AHmpPythonIO::dur_toc()\n{\n\tdur_toc_second = FPlatformTime::Seconds();\n\tdur_sum_second += dur_toc_second - dur_tic_second;\n}\nfloat AHmpPythonIO::dur_reset()\n{\n\tfloat tmp = dur_sum_second;\n\tdur_sum_second = 0;\n\treturn tmp;\n}\n\n\n\nvoid AHmpPythonIO::RaiseErrorNative(UObject* WorldContextObject, const FString& ErrorMessage, bool bPrintToOutputLog)\n{\n\tFString MessageToLog = FString::Printf(TEXT(\"\\\"%s\\\"\"), *ErrorMessage);\n\n\n#if WITH_EDITOR\n\t/*FKismetDebugUtilities::OnScriptException(WorldContextObject,);*/\n\tTSharedRef<FTokenizedMessage> TokenizedMessage = FTokenizedMessage::Create(EMessageSeverity::Error, FText::FromString(MessageToLog));\n\tTokenizedMessage.Get().AddToken(FUObjectToken::Create(WorldContextObject));\n\tFMessageLog BlueprintLog = FMessageLog(\"BlueprintLog\").SuppressLoggingToOutputLog(!bPrintToOutputLog);\n\tBlueprintLog.AddMessage(TokenizedMessage);\n\tBlueprintLog.Notify();\n\n#else\n\tif (bPrintToOutputLog)\n\t{\n\t\tUE_LOG(LogTemp, Error, TEXT(\"%s\"), *MessageToLog);\n\t}\n#endif\n}\n\nvoid AHmpPythonIO::exit_hmp(bool force)\n{\n\tFGenericPlatformMisc::RequestExit(force);\n}"
  },
  {
    "path": "Source/UHMP/HmpPythonIO.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Jsonx.h\"\n#include \"Networking.h\"\n#include \"JsonxObjectConverter.h\"\n#include \"Containers/UnrealString.h\"\n#include \"GenericPlatform/GenericPlatformMisc.h\"\n#include \"GameFramework/Actor.h\"\n#include \"Misc/UObjectToken.h\"\n#include \"IOCompress/lz4.h\"\n#include \"DataStruct.h\"\n#include \"HmpPythonIO.generated.h\"\n\nUCLASS()\nclass UHMP_API AHmpPythonIO : public AActor\n{\n\tGENERATED_BODY()\n\npublic:\n\t// Sets default values for this actor's properties\n\tAHmpPythonIO();\n\nprotected:\n\tvoid BeginDestroy() override;\n\n\t// Tcp server start\n\tFSocket* ListenSocket = NULL;\n\tFSocket* TcpAccpetedSocket = NULL;\n\n\tUPROPERTY(BlueprintReadWrite)\n\t\tfloat SendBufferUsage = 0.0f;\n\tUPROPERTY(BlueprintReadWrite)\n\t\tfloat RecvBufferUsage = 0.0f;\n\n\n\tdouble tic_second = 0.0f;\n\tdouble toc_second = 0.0f;\n\n\tdouble dur_tic_second = 0.0f;\n\tdouble dur_toc_second = 0.0f;\n\tdouble dur_sum_second = 0.0f;\n\n\tint32 SendBufferSize = 1024 * 1024 * 16;\n\tint32 ReceiveBufferSize = 1024 * 1024 * 16;\n\tuint8* SendBuffer = new uint8[SendBufferSize];\n\tuint8* RecvBuffer = new uint8[ReceiveBufferSize];\n\tuint8* RecvDecompressBuffer = new uint8[ReceiveBufferSize];\n\n\t// Tcp server send out json (Blocked)\n\tvoid TcpServerSendJson(TSharedPtr<FJsonxObject> ReplyJson, float& encTime, float& sendTime);\n\t// Warning, do not use this func!\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid DisableRendering();\n\t// Warning, do not use this func!\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid EnableRendering();\n\t\n\t// In order to make sure that game is invariant to machine performance\n\t// there are several thing need to do\n\t// <1> Fixed the frame rate in project setting -> engine -> general\n\t// <2> Set frame rate by writing GEngine->FixedFrameRate\n\t// <3> Set Global Time Dilation by UGameplayStatics::SetGlobalTimeDilation(WorldContext)\n\t// Delta tick time is precisely ```dT = TimeDilation / FixedFrameRate```\n\t// Therefore, we must keep ```TimeDilation / FixedFrameRate``` constant\n\t// <void ChangeEngineFixedFrameRate(float fps)>: Change the fixed frame rate of Unreal Engine, \n\t// Warning, please change it together with Time Dilation, or everything will fuck up, 2 <= fps <= 5000\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid ChangeEngineFixedFrameRate(float fps);\n\t// Initialize Tcp Server at specific port\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid StartTcpServer(int32 InListenPort);\n\t// Tcp server wait client (blocking)\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid TcpServerWaitClient();\n\t// is any client waiting to connect\n\tUFUNCTION(BlueprintCallable)\n\t\tbool TcpServerHasWaitingClient();\n\t// convert Json, then it will call \"TcpServerSendJson\"\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid ConvertOutDataToJsonAndSendTcp(TArray<FAgentDataOutput> TcpOutDataArr, FGlobalDataOutput GlobalData, \n\t\t\tfloat& toJsonTime, float& encTime, float& sendTime);\n\t// Any Data to read\n\tUFUNCTION(BlueprintCallable)\n\t\tbool TcpServerHasRecvData();\n\t// sleep for wall-clock time, use with caution\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid sleep_thread(float second);\n\t// receive tcp data (blocked)\n\tUFUNCTION(BlueprintCallable)\n\t\tFString TcpServerRecvBlocked(bool checkEOF, float& tcpWaitTime, float& decodeTime);\n\t// parse string read from tcp\n\tUFUNCTION(BlueprintCallable)\n\t\tFParsedDataInput ParsedTcpInData(FString TcpLatestRecvString);\n\n\t// exit game\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid exit_hmp(bool force);\n\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid tic();\n\n\tUFUNCTION(BlueprintCallable)\n\t\tfloat toc();\n\n\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid dur_tic();\n\n\tUFUNCTION(BlueprintCallable)\n\t\tvoid dur_toc();\n\n\tUFUNCTION(BlueprintCallable)\n\t\tfloat dur_reset();\n\n\n\tUFUNCTION(BlueprintCallable, meta = (WorldContext = \"WorldContextObject\", CallableWithoutWorldContext, Keywords = \"raise error\", DevelopmentOnly), Category = \"Utilities|Debugging\")\n\t\tstatic void RaiseErrorNative(UObject* WorldContextObject, const FString& ErrorMessage = FString(TEXT(\"An error occurred\")), bool bPrintToOutputLog = true);\n\n};\n"
  },
  {
    "path": "Source/UHMP/IOCompress/lz4.c",
    "content": "/*\n   XLZ4 - Fast LZ compression algorithm\n   Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - XLZ4 homepage : http://www.lz4.org\n    - XLZ4 source repository : https://github.com/lz4/lz4\n*/\n\n/*-************************************\n*  Tuning parameters\n**************************************/\n/*\n * XLZ4_HEAPMODE :\n * Select how default compression functions will allocate memory for their hash table,\n * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).\n */\n#ifndef XLZ4_HEAPMODE\n#  define XLZ4_HEAPMODE 0\n#endif\n\n/*\n * XLZ4_ACCELERATION_DEFAULT :\n * Select \"acceleration\" for XLZ4_compress_fast() when parameter value <= 0\n */\n#define XLZ4_ACCELERATION_DEFAULT 1\n/*\n * XLZ4_ACCELERATION_MAX :\n * Any \"acceleration\" value higher than this threshold\n * get treated as XLZ4_ACCELERATION_MAX instead (fix #876)\n */\n#define XLZ4_ACCELERATION_MAX 65537\n\n\n/*-************************************\n*  CPU Feature Detection\n**************************************/\n/* XLZ4_FORCE_MEMORY_ACCESS\n * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.\n * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.\n * The below switch allow to select different access method for improved performance.\n * Method 0 (default) : use `memcpy()`. Safe and portable.\n * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).\n *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.\n * Method 2 : direct access. This method is portable but violate C standard.\n *            It can generate buggy code on targets which assembly generation depends on alignment.\n *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)\n * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.\n * Prefer these methods in priority order (0 > 1 > 2)\n */\n#ifndef XLZ4_FORCE_MEMORY_ACCESS   /* can be defined externally */\n#  if defined(__GNUC__) && \\\n  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \\\n  || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )\n#    define XLZ4_FORCE_MEMORY_ACCESS 2\n#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)\n#    define XLZ4_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n/*\n * XLZ4_FORCE_SW_BITCOUNT\n * Define this parameter if your target system or compiler does not support hardware bit count\n */\n#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for WinCE doesn't support Hardware bit count */\n#  undef  XLZ4_FORCE_SW_BITCOUNT  /* avoid double def */\n#  define XLZ4_FORCE_SW_BITCOUNT\n#endif\n\n\n\n/*-************************************\n*  Dependency\n**************************************/\n/*\n * XLZ4_SRC_INCLUDED:\n * Amalgamation flag, whether lz4.c is included\n */\n#ifndef XLZ4_SRC_INCLUDED\n#  define XLZ4_SRC_INCLUDED 1\n#endif\n\n#ifndef XLZ4_STATIC_LINKING_ONLY\n#define XLZ4_STATIC_LINKING_ONLY\n#endif\n\n#ifndef XLZ4_DISABLE_DEPRECATE_WARNINGS\n#define XLZ4_DISABLE_DEPRECATE_WARNINGS /* due to XLZ4_decompress_safe_withPrefix64k */\n#endif\n\n#define XLZ4_STATIC_LINKING_ONLY  /* XLZ4_DISTANCE_MAX */\n#include \"lz4.h\"\n/* see also \"memory routines\" below */\n\n\n/*-************************************\n*  Compiler Options\n**************************************/\n#if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */\n#  include <intrin.h>               /* only present in VS2005+ */\n#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */\n#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */\n#endif  /* _MSC_VER */\n\n#ifndef XLZ4_FORCE_INLINE\n#  ifdef _MSC_VER    /* Visual Studio */\n#    define XLZ4_FORCE_INLINE static __forceinline\n#  else\n#    if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */\n#      ifdef __GNUC__\n#        define XLZ4_FORCE_INLINE static inline __attribute__((always_inline))\n#      else\n#        define XLZ4_FORCE_INLINE static inline\n#      endif\n#    else\n#      define XLZ4_FORCE_INLINE static\n#    endif /* __STDC_VERSION__ */\n#  endif  /* _MSC_VER */\n#endif /* XLZ4_FORCE_INLINE */\n\n/* XLZ4_FORCE_O2 and XLZ4_FORCE_INLINE\n * gcc on ppc64le generates an unrolled SIMDized loop for XLZ4_wildCopy8,\n * together with a simple 8-byte copy loop as a fall-back path.\n * However, this optimization hurts the decompression speed by >30%,\n * because the execution does not go to the optimized loop\n * for typical compressible data, and all of the preamble checks\n * before going to the fall-back path become useless overhead.\n * This optimization happens only with the -O3 flag, and -O2 generates\n * a simple 8-byte copy loop.\n * With gcc on ppc64le, all of the XLZ4_decompress_* and XLZ4_wildCopy8\n * functions are annotated with __attribute__((optimize(\"O2\"))),\n * and also XLZ4_wildCopy8 is forcibly inlined, so that the O2 attribute\n * of XLZ4_wildCopy8 does not affect the compression speed.\n */\n#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)\n#  define XLZ4_FORCE_O2  __attribute__((optimize(\"O2\")))\n#  undef XLZ4_FORCE_INLINE\n#  define XLZ4_FORCE_INLINE  static __inline __attribute__((optimize(\"O2\"),always_inline))\n#else\n#  define XLZ4_FORCE_O2\n#endif\n\n#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)\n#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )\n#else\n#  define expect(expr,value)    (expr)\n#endif\n\n#ifndef likely\n#define likely(expr)     expect((expr) != 0, 1)\n#endif\n#ifndef unlikely\n#define unlikely(expr)   expect((expr) != 0, 0)\n#endif\n\n/* Should the alignment test prove unreliable, for some reason,\n * it can be disabled by setting XLZ4_ALIGN_TEST to 0 */\n#ifndef XLZ4_ALIGN_TEST  /* can be externally provided */\n# define XLZ4_ALIGN_TEST 1\n#endif\n\n\n/*-************************************\n*  Memory routines\n**************************************/\n\n/*! XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :\n *  Disable relatively high-level XLZ4/HC functions that use dynamic memory\n *  allocation functions (malloc(), calloc(), free()).\n *\n *  Note that this is a compile-time switch. And since it disables\n *  public/stable XLZ4 v1 API functions, we don't recommend using this\n *  symbol to generate a library for distribution.\n *\n *  The following public functions are removed when this symbol is defined.\n *  - lz4   : XLZ4_createStream, XLZ4_freeStream,\n *            XLZ4_createStreamDecode, XLZ4_freeStreamDecode, XLZ4_create (deprecated)\n *  - lz4hc : XLZ4_createStreamHC, XLZ4_freeStreamHC,\n *            XLZ4_createHC (deprecated), XLZ4_freeHC  (deprecated)\n *  - lz4frame, lz4file : All XLZ4F_* functions\n */\n#if defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\n#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled\n#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled\n#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled\n#elif defined(XLZ4_USER_MEMORY_FUNCTIONS)\n/* memory management functions can be customized by user project.\n * Below functions must exist somewhere in the Project\n * and be available at link time */\nvoid* XLZ4_malloc(size_t s);\nvoid* XLZ4_calloc(size_t n, size_t s);\nvoid  XLZ4_free(void* p);\n# define ALLOC(s)          XLZ4_malloc(s)\n# define ALLOC_AND_ZERO(s) XLZ4_calloc(1,s)\n# define FREEMEM(p)        XLZ4_free(p)\n#else\n# include <stdlib.h>   /* malloc, calloc, free */\n# define ALLOC(s)          malloc(s)\n# define ALLOC_AND_ZERO(s) calloc(1,s)\n# define FREEMEM(p)        free(p)\n#endif\n\n#if ! XLZ4_FREESTANDING\n#  include <string.h>   /* memset, memcpy */\n#endif\n#if !defined(XLZ4_memset)\n#  define XLZ4_memset(p,v,s) memset((p),(v),(s))\n#endif\n#define MEM_INIT(p,v,s)   XLZ4_memset((p),(v),(s))\n\n\n/*-************************************\n*  Common Constants\n**************************************/\n#define MINMATCH 4\n\n#define WILDCOPYLENGTH 8\n#define LASTLITERALS   5   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MFLIMIT       12   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MATCH_SAFEGUARD_DISTANCE  ((2*WILDCOPYLENGTH) - MINMATCH)   /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */\n#define FASTLOOP_SAFE_DISTANCE 64\nstatic const int XLZ4_minLength = (MFLIMIT+1);\n\n#define KB *(1 <<10)\n#define MB *(1 <<20)\n#define GB *(1U<<30)\n\n#define XLZ4_DISTANCE_ABSOLUTE_MAX 65535\n#if (XLZ4_DISTANCE_MAX > XLZ4_DISTANCE_ABSOLUTE_MAX)   /* max supported by XLZ4 format */\n#  error \"XLZ4_DISTANCE_MAX is too big : must be <= 65535\"\n#endif\n\n#define ML_BITS  4\n#define ML_MASK  ((1U<<ML_BITS)-1)\n#define RUN_BITS (8-ML_BITS)\n#define RUN_MASK ((1U<<RUN_BITS)-1)\n\n\n/*-************************************\n*  Error detection\n**************************************/\n#if defined(XLZ4_DEBUG) && (XLZ4_DEBUG>=1)\n#  include <assert.h>\n#else\n#  ifndef assert\n#    define assert(condition) ((void)0)\n#  endif\n#endif\n\n#define XLZ4_STATIC_ASSERT(c)   { enum { XLZ4_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */\n\n#if defined(XLZ4_DEBUG) && (XLZ4_DEBUG>=2)\n#  include <stdio.h>\n   static int g_debuglog_enable = 1;\n#  define DEBUGLOG(l, ...) {                          \\\n        if ((g_debuglog_enable) && (l<=XLZ4_DEBUG)) {  \\\n            fprintf(stderr, __FILE__ \": \");           \\\n            fprintf(stderr, __VA_ARGS__);             \\\n            fprintf(stderr, \" \\n\");                   \\\n    }   }\n#else\n#  define DEBUGLOG(l, ...) {}    /* disabled */\n#endif\n\nstatic int XLZ4_isAligned(const void* ptr, size_t alignment)\n{\n    return ((size_t)ptr & (alignment -1)) == 0;\n}\n\n\n/*-************************************\n*  Types\n**************************************/\n#include <limits.h>\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  uint8_t BYTE;\n  typedef uint16_t U16;\n  typedef uint32_t U32;\n  typedef  int32_t S32;\n  typedef uint64_t U64;\n  typedef uintptr_t uptrval;\n#else\n# if UINT_MAX != 4294967295UL\n#   error \"XLZ4 code (when not C++ or C99) assumes that sizeof(int) == 4\"\n# endif\n  typedef unsigned char       BYTE;\n  typedef unsigned short      U16;\n  typedef unsigned int        U32;\n  typedef   signed int        S32;\n  typedef unsigned long long  U64;\n  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */\n#endif\n\n#if defined(__x86_64__)\n  typedef U64    reg_t;   /* 64-bits in x32 mode */\n#else\n  typedef size_t reg_t;   /* 32-bits in x32 mode */\n#endif\n\ntypedef enum {\n    notLimited = 0,\n    limitedOutput = 1,\n    fillOutput = 2\n} limitedOutput_directive;\n\n\n/*-************************************\n*  Reading and writing into memory\n**************************************/\n\n/**\n * XLZ4 relies on memcpy with a constant size being inlined. In freestanding\n * environments, the compiler can't assume the implementation of memcpy() is\n * standard compliant, so it can't apply its specialized memcpy() inlining\n * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze\n * memcpy() as if it were standard compliant, so it can inline it in freestanding\n * environments. This is needed when decompressing the Linux Kernel, for example.\n */\n#if !defined(XLZ4_memcpy)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define XLZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)\n#  else\n#    define XLZ4_memcpy(dst, src, size) memcpy(dst, src, size)\n#  endif\n#endif\n\n#if !defined(XLZ4_memmove)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define XLZ4_memmove __builtin_memmove\n#  else\n#    define XLZ4_memmove memmove\n#  endif\n#endif\n\nstatic unsigned XLZ4_isLittleEndian(void)\n{\n    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */\n    return one.c[0];\n}\n\n\n#if defined(XLZ4_FORCE_MEMORY_ACCESS) && (XLZ4_FORCE_MEMORY_ACCESS==2)\n/* lie to the compiler about data alignment; use with caution */\n\nstatic U16 XLZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }\nstatic U32 XLZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }\nstatic reg_t XLZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }\n\nstatic void XLZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }\nstatic void XLZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }\n\n#elif defined(XLZ4_FORCE_MEMORY_ACCESS) && (XLZ4_FORCE_MEMORY_ACCESS==1)\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) XLZ4_unalign;\n\nstatic U16 XLZ4_read16(const void* ptr) { return ((const XLZ4_unalign*)ptr)->u16; }\nstatic U32 XLZ4_read32(const void* ptr) { return ((const XLZ4_unalign*)ptr)->u32; }\nstatic reg_t XLZ4_read_ARCH(const void* ptr) { return ((const XLZ4_unalign*)ptr)->uArch; }\n\nstatic void XLZ4_write16(void* memPtr, U16 value) { ((XLZ4_unalign*)memPtr)->u16 = value; }\nstatic void XLZ4_write32(void* memPtr, U32 value) { ((XLZ4_unalign*)memPtr)->u32 = value; }\n\n#else  /* safe and portable access using memcpy() */\n\nstatic U16 XLZ4_read16(const void* memPtr)\n{\n    U16 val; XLZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic U32 XLZ4_read32(const void* memPtr)\n{\n    U32 val; XLZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic reg_t XLZ4_read_ARCH(const void* memPtr)\n{\n    reg_t val; XLZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic void XLZ4_write16(void* memPtr, U16 value)\n{\n    XLZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\nstatic void XLZ4_write32(void* memPtr, U32 value)\n{\n    XLZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\n#endif /* XLZ4_FORCE_MEMORY_ACCESS */\n\n\nstatic U16 XLZ4_readLE16(const void* memPtr)\n{\n    if (XLZ4_isLittleEndian()) {\n        return XLZ4_read16(memPtr);\n    } else {\n        const BYTE* p = (const BYTE*)memPtr;\n        return (U16)((U16)p[0] + (p[1]<<8));\n    }\n}\n\nstatic void XLZ4_writeLE16(void* memPtr, U16 value)\n{\n    if (XLZ4_isLittleEndian()) {\n        XLZ4_write16(memPtr, value);\n    } else {\n        BYTE* p = (BYTE*)memPtr;\n        p[0] = (BYTE) value;\n        p[1] = (BYTE)(value>>8);\n    }\n}\n\n/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */\nXLZ4_FORCE_INLINE\nvoid XLZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { XLZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);\n}\n\nstatic const unsigned inc32table[8] = {0, 1, 2,  1,  0,  4, 4, 4};\nstatic const int      dec64table[8] = {0, 0, 0, -1, -4,  1, 2, 3};\n\n\n#ifndef XLZ4_FAST_DEC_LOOP\n#  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64\n#    define XLZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && defined(__APPLE__)\n#    define XLZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && !defined(__clang__)\n     /* On non-Apple aarch64, we disable this optimization for clang because\n      * on certain mobile chipsets, performance is reduced with clang. For\n      * more information refer to https://github.com/lz4/lz4/pull/707 */\n#    define XLZ4_FAST_DEC_LOOP 1\n#  else\n#    define XLZ4_FAST_DEC_LOOP 0\n#  endif\n#endif\n\n#if XLZ4_FAST_DEC_LOOP\n\nXLZ4_FORCE_INLINE void\nXLZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    assert(srcPtr + offset == dstPtr);\n    if (offset < 8) {\n        XLZ4_write32(dstPtr, 0);   /* silence an msan warning when offset==0 */\n        dstPtr[0] = srcPtr[0];\n        dstPtr[1] = srcPtr[1];\n        dstPtr[2] = srcPtr[2];\n        dstPtr[3] = srcPtr[3];\n        srcPtr += inc32table[offset];\n        XLZ4_memcpy(dstPtr+4, srcPtr, 4);\n        srcPtr -= dec64table[offset];\n        dstPtr += 8;\n    } else {\n        XLZ4_memcpy(dstPtr, srcPtr, 8);\n        dstPtr += 8;\n        srcPtr += 8;\n    }\n\n    XLZ4_wildCopy8(dstPtr, srcPtr, dstEnd);\n}\n\n/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd\n * this version copies two times 16 bytes (instead of one time 32 bytes)\n * because it must be compatible with offsets >= 16. */\nXLZ4_FORCE_INLINE void\nXLZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { XLZ4_memcpy(d,s,16); XLZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);\n}\n\n/* XLZ4_memcpy_using_offset()  presumes :\n * - dstEnd >= dstPtr + MINMATCH\n * - there is at least 8 bytes available to write after dstEnd */\nXLZ4_FORCE_INLINE void\nXLZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    BYTE v[8];\n\n    assert(dstEnd >= dstPtr + MINMATCH);\n\n    switch(offset) {\n    case 1:\n        MEM_INIT(v, *srcPtr, 8);\n        break;\n    case 2:\n        XLZ4_memcpy(v, srcPtr, 2);\n        XLZ4_memcpy(&v[2], srcPtr, 2);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(push)\n#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */\n#endif\n        XLZ4_memcpy(&v[4], v, 4);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(pop)\n#endif\n        break;\n    case 4:\n        XLZ4_memcpy(v, srcPtr, 4);\n        XLZ4_memcpy(&v[4], srcPtr, 4);\n        break;\n    default:\n        XLZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);\n        return;\n    }\n\n    XLZ4_memcpy(dstPtr, v, 8);\n    dstPtr += 8;\n    while (dstPtr < dstEnd) {\n        XLZ4_memcpy(dstPtr, v, 8);\n        dstPtr += 8;\n    }\n}\n#endif\n\n\n/*-************************************\n*  Common functions\n**************************************/\nstatic unsigned XLZ4_NbCommonBytes (reg_t val)\n{\n    assert(val != 0);\n    if (XLZ4_isLittleEndian()) {\n        if (sizeof(val) == 8) {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(XLZ4_FORCE_SW_BITCOUNT)\n/*-*************************************************************************************************\n* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.\n* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics\n* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.\n****************************************************************************************************/\n#         if defined(__clang__) && (__clang_major__ < 10)\n            /* Avoid undefined clang-cl intrinsics issue.\n             * See https://github.com/lz4/lz4/pull/1017 for details. */\n            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;\n#         else\n            /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */\n            return (unsigned)_tzcnt_u64(val) >> 3;\n#         endif\n#       elif defined(_MSC_VER) && defined(_WIN64) && !defined(XLZ4_FORCE_SW_BITCOUNT)\n            unsigned long r = 0;\n            _BitScanForward64(&r, (U64)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(XLZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctzll((U64)val) >> 3;\n#       else\n            const U64 m = 0x0101010101010101ULL;\n            val ^= val - 1;\n            return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);\n#       endif\n        } else /* 32 bits */ {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(XLZ4_FORCE_SW_BITCOUNT)\n            unsigned long r;\n            _BitScanForward(&r, (U32)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(XLZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctz((U32)val) >> 3;\n#       else\n            const U32 m = 0x01010101;\n            return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;\n#       endif\n        }\n    } else   /* Big Endian CPU */ {\n        if (sizeof(val)==8) {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(XLZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clzll((U64)val) >> 3;\n#       else\n#if 1\n            /* this method is probably faster,\n             * but adds a 128 bytes lookup table */\n            static const unsigned char ctz7_tab[128] = {\n                7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n            };\n            U64 const mask = 0x0101010101010101ULL;\n            U64 const t = (((val >> 8) - mask) | val) & mask;\n            return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];\n#else\n            /* this method doesn't consume memory space like the previous one,\n             * but it contains several branches,\n             * that may end up slowing execution */\n            static const U32 by32 = sizeof(val)*4;  /* 32 on 64 bits (goal), 16 on 32 bits.\n            Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.\n            Note that this code path is never triggered in 32-bits mode. */\n            unsigned r;\n            if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }\n            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\n            r += (!val);\n            return r;\n#endif\n#       endif\n        } else /* 32 bits */ {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(XLZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clz((U32)val) >> 3;\n#       else\n            val >>= 8;\n            val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |\n              (val + 0x00FF0000)) >> 24;\n            return (unsigned)val ^ 3;\n#       endif\n        }\n    }\n}\n\n\n#define STEPSIZE sizeof(reg_t)\nXLZ4_FORCE_INLINE\nunsigned XLZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)\n{\n    const BYTE* const pStart = pIn;\n\n    if (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = XLZ4_read_ARCH(pMatch) ^ XLZ4_read_ARCH(pIn);\n        if (!diff) {\n            pIn+=STEPSIZE; pMatch+=STEPSIZE;\n        } else {\n            return XLZ4_NbCommonBytes(diff);\n    }   }\n\n    while (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = XLZ4_read_ARCH(pMatch) ^ XLZ4_read_ARCH(pIn);\n        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }\n        pIn += XLZ4_NbCommonBytes(diff);\n        return (unsigned)(pIn - pStart);\n    }\n\n    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (XLZ4_read32(pMatch) == XLZ4_read32(pIn))) { pIn+=4; pMatch+=4; }\n    if ((pIn<(pInLimit-1)) && (XLZ4_read16(pMatch) == XLZ4_read16(pIn))) { pIn+=2; pMatch+=2; }\n    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;\n    return (unsigned)(pIn - pStart);\n}\n\n\n#ifndef XLZ4_COMMONDEFS_ONLY\n/*-************************************\n*  Local Constants\n**************************************/\nstatic const int XLZ4_64Klimit = ((64 KB) + (MFLIMIT-1));\nstatic const U32 XLZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */\n\n\n/*-************************************\n*  Local Structures and types\n**************************************/\ntypedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;\n\n/**\n * This enum distinguishes several different modes of accessing previous\n * content in the stream.\n *\n * - noDict        : There is no preceding content.\n * - withPrefix64k : Table entries up to ctx->dictSize before the current blob\n *                   blob being compressed are valid and refer to the preceding\n *                   content (of length ctx->dictSize), which is available\n *                   contiguously preceding in memory the content currently\n *                   being compressed.\n * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere\n *                   else in memory, starting at ctx->dictionary with length\n *                   ctx->dictSize.\n * - usingDictCtx  : Everything concerning the preceding content is\n *                   in a separate context, pointed to by ctx->dictCtx.\n *                   ctx->dictionary, ctx->dictSize, and table entries\n *                   in the current context that refer to positions\n *                   preceding the beginning of the current compression are\n *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx\n *                   ->dictSize describe the location and size of the preceding\n *                   content, and matches are found by looking in the ctx\n *                   ->dictCtx->hashTable.\n */\ntypedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;\ntypedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;\n\n\n/*-************************************\n*  Local Utils\n**************************************/\nint XLZ4_versionNumber (void) { return XLZ4_VERSION_NUMBER; }\nconst char* XLZ4_versionString(void) { return XLZ4_VERSION_STRING; }\nint XLZ4_compressBound(int isize)  { return XLZ4_COMPRESSBOUND(isize); }\nint XLZ4_sizeofState(void) { return sizeof(XLZ4_stream_t); }\n\n\n/*-****************************************\n*  Internal Definitions, used only in Tests\n*******************************************/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\nint XLZ4_compress_forceExtDict (XLZ4_stream_t* XLZ4_dict, const char* source, char* dest, int srcSize);\n\nint XLZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize);\nint XLZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize);\n#if defined (__cplusplus)\n}\n#endif\n\n/*-******************************\n*  Compression functions\n********************************/\nXLZ4_FORCE_INLINE U32 XLZ4_hash4(U32 sequence, tableType_t const tableType)\n{\n    if (tableType == byU16)\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(XLZ4_HASHLOG+1)));\n    else\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-XLZ4_HASHLOG));\n}\n\nXLZ4_FORCE_INLINE U32 XLZ4_hash5(U64 sequence, tableType_t const tableType)\n{\n    const U32 hashLog = (tableType == byU16) ? XLZ4_HASHLOG+1 : XLZ4_HASHLOG;\n    if (XLZ4_isLittleEndian()) {\n        const U64 prime5bytes = 889523592379ULL;\n        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));\n    } else {\n        const U64 prime8bytes = 11400714785074694791ULL;\n        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));\n    }\n}\n\nXLZ4_FORCE_INLINE U32 XLZ4_hashPosition(const void* const p, tableType_t const tableType)\n{\n    if ((sizeof(reg_t)==8) && (tableType != byU16)) return XLZ4_hash5(XLZ4_read_ARCH(p), tableType);\n    return XLZ4_hash4(XLZ4_read32(p), tableType);\n}\n\nXLZ4_FORCE_INLINE void XLZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }\n    }\n}\n\nXLZ4_FORCE_INLINE void XLZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: /* fallthrough */\n    case byPtr: { /* illegal! */ assert(0); return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }\n    }\n}\n\nXLZ4_FORCE_INLINE void XLZ4_putPositionOnHash(const BYTE* p, U32 h,\n                                  void* tableBase, tableType_t const tableType,\n                            const BYTE* srcBase)\n{\n    switch (tableType)\n    {\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }\n    }\n}\n\nXLZ4_FORCE_INLINE void XLZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    U32 const h = XLZ4_hashPosition(p, tableType);\n    XLZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);\n}\n\n/* XLZ4_getIndexOnHash() :\n * Index of match position registered in hash table.\n * hash position must be calculated by using base+index, or dictBase+index.\n * Assumption 1 : only valid if tableType == byU32 or byU16.\n * Assumption 2 : h is presumed valid (within limits of hash table)\n */\nXLZ4_FORCE_INLINE U32 XLZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)\n{\n    XLZ4_STATIC_ASSERT(XLZ4_MEMORY_USAGE > 2);\n    if (tableType == byU32) {\n        const U32* const hashTable = (const U32*) tableBase;\n        assert(h < (1U << (XLZ4_MEMORY_USAGE-2)));\n        return hashTable[h];\n    }\n    if (tableType == byU16) {\n        const U16* const hashTable = (const U16*) tableBase;\n        assert(h < (1U << (XLZ4_MEMORY_USAGE-1)));\n        return hashTable[h];\n    }\n    assert(0); return 0;  /* forbidden case */\n}\n\nstatic const BYTE* XLZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }\n    if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }\n    { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; }   /* default, to ensure a return */\n}\n\nXLZ4_FORCE_INLINE const BYTE*\nXLZ4_getPosition(const BYTE* p,\n                const void* tableBase, tableType_t tableType,\n                const BYTE* srcBase)\n{\n    U32 const h = XLZ4_hashPosition(p, tableType);\n    return XLZ4_getPositionOnHash(h, tableBase, tableType, srcBase);\n}\n\nXLZ4_FORCE_INLINE void\nXLZ4_prepareTable(XLZ4_stream_t_internal* const cctx,\n           const int inputSize,\n           const tableType_t tableType) {\n    /* If the table hasn't been used, it's guaranteed to be zeroed out, and is\n     * therefore safe to use no matter what mode we're in. Otherwise, we figure\n     * out if it's safe to leave as is or whether it needs to be reset.\n     */\n    if ((tableType_t)cctx->tableType != clearedTable) {\n        assert(inputSize >= 0);\n        if ((tableType_t)cctx->tableType != tableType\n          || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)\n          || ((tableType == byU32) && cctx->currentOffset > 1 GB)\n          || tableType == byPtr\n          || inputSize >= 4 KB)\n        {\n            DEBUGLOG(4, \"XLZ4_prepareTable: Resetting table in %p\", cctx);\n            MEM_INIT(cctx->hashTable, 0, XLZ4_HASHTABLESIZE);\n            cctx->currentOffset = 0;\n            cctx->tableType = (U32)clearedTable;\n        } else {\n            DEBUGLOG(4, \"XLZ4_prepareTable: Re-use hash table (no reset)\");\n        }\n    }\n\n    /* Adding a gap, so all previous entries are > XLZ4_DISTANCE_MAX back,\n     * is faster than compressing without a gap.\n     * However, compressing with currentOffset == 0 is faster still,\n     * so we preserve that case.\n     */\n    if (cctx->currentOffset != 0 && tableType == byU32) {\n        DEBUGLOG(5, \"XLZ4_prepareTable: adding 64KB to currentOffset\");\n        cctx->currentOffset += 64 KB;\n    }\n\n    /* Finally, clear history */\n    cctx->dictCtx = NULL;\n    cctx->dictionary = NULL;\n    cctx->dictSize = 0;\n}\n\n/** XLZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time.\n *  Presumed already validated at this stage:\n *  - source != NULL\n *  - inputSize > 0\n */\nXLZ4_FORCE_INLINE int XLZ4_compress_generic_validated(\n                 XLZ4_stream_t_internal* const cctx,\n                 const char* const source,\n                 char* const dest,\n                 const int inputSize,\n                 int*  inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int maxOutputSize,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    int result;\n    const BYTE* ip = (const BYTE*) source;\n\n    U32 const startIndex = cctx->currentOffset;\n    const BYTE* base = (const BYTE*) source - startIndex;\n    const BYTE* lowLimit;\n\n    const XLZ4_stream_t_internal* dictCtx = (const XLZ4_stream_t_internal*) cctx->dictCtx;\n    const BYTE* const dictionary =\n        dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;\n    const U32 dictSize =\n        dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;\n    const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0;   /* make indexes in dictCtx comparable with index in current context */\n\n    int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);\n    U32 const prefixIdxLimit = startIndex - dictSize;   /* used when dictDirective == dictSmall */\n    const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;\n    const BYTE* anchor = (const BYTE*) source;\n    const BYTE* const iend = ip + inputSize;\n    const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;\n    const BYTE* const matchlimit = iend - LASTLITERALS;\n\n    /* the dictCtx currentOffset is indexed on the start of the dictionary,\n     * while a dictionary in the current context precedes the currentOffset */\n    const BYTE* dictBase = (dictionary == NULL) ? NULL :\n                           (dictDirective == usingDictCtx) ?\n                            dictionary + dictSize - dictCtx->currentOffset :\n                            dictionary + dictSize - startIndex;\n\n    BYTE* op = (BYTE*) dest;\n    BYTE* const olimit = op + maxOutputSize;\n\n    U32 offset = 0;\n    U32 forwardH;\n\n    DEBUGLOG(5, \"XLZ4_compress_generic_validated: srcSize=%i, tableType=%u\", inputSize, tableType);\n    assert(ip != NULL);\n    /* If init conditions are not met, we don't have to mark stream\n     * as having dirty context, since no action was taken yet */\n    if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */\n    if ((tableType == byU16) && (inputSize>=XLZ4_64Klimit)) { return 0; }  /* Size too large (not within 64K limit) */\n    if (tableType==byPtr) assert(dictDirective==noDict);      /* only supported use case with byPtr */\n    assert(acceleration >= 1);\n\n    lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);\n\n    /* Update context state */\n    if (dictDirective == usingDictCtx) {\n        /* Subsequent linked blocks can't use the dictionary. */\n        /* Instead, they use the block we just compressed. */\n        cctx->dictCtx = NULL;\n        cctx->dictSize = (U32)inputSize;\n    } else {\n        cctx->dictSize += (U32)inputSize;\n    }\n    cctx->currentOffset += (U32)inputSize;\n    cctx->tableType = (U32)tableType;\n\n    if (inputSize<XLZ4_minLength) goto _last_literals;        /* Input too small, no compression (all literals) */\n\n    /* First Byte */\n    XLZ4_putPosition(ip, cctx->hashTable, tableType, base);\n    ip++; forwardH = XLZ4_hashPosition(ip, tableType);\n\n    /* Main Loop */\n    for ( ; ; ) {\n        const BYTE* match;\n        BYTE* token;\n        const BYTE* filledIp;\n\n        /* Find a match */\n        if (tableType == byPtr) {\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << XLZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> XLZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                match = XLZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);\n                forwardH = XLZ4_hashPosition(forwardIp, tableType);\n                XLZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);\n\n            } while ( (match+XLZ4_DISTANCE_MAX < ip)\n                   || (XLZ4_read32(match) != XLZ4_read32(ip)) );\n\n        } else {   /* byU32, byU16 */\n\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << XLZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                U32 const current = (U32)(forwardIp - base);\n                U32 matchIndex = XLZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n                assert(matchIndex <= current);\n                assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> XLZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                if (dictDirective == usingDictCtx) {\n                    if (matchIndex < startIndex) {\n                        /* there was no match, try the dictionary */\n                        assert(tableType == byU32);\n                        matchIndex = XLZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                        match = dictBase + matchIndex;\n                        matchIndex += dictDelta;   /* make dictCtx index comparable with current context */\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else if (dictDirective == usingExtDict) {\n                    if (matchIndex < startIndex) {\n                        DEBUGLOG(7, \"extDict candidate: matchIndex=%5u  <  startIndex=%5u\", matchIndex, startIndex);\n                        assert(startIndex - matchIndex >= MINMATCH);\n                        assert(dictBase);\n                        match = dictBase + matchIndex;\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else {   /* single continuous memory segment */\n                    match = base + matchIndex;\n                }\n                forwardH = XLZ4_hashPosition(forwardIp, tableType);\n                XLZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n\n                DEBUGLOG(7, \"candidate at pos=%u  (offset=%u \\n\", matchIndex, current - matchIndex);\n                if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; }    /* match outside of valid area */\n                assert(matchIndex < current);\n                if ( ((tableType != byU16) || (XLZ4_DISTANCE_MAX < XLZ4_DISTANCE_ABSOLUTE_MAX))\n                  && (matchIndex+XLZ4_DISTANCE_MAX < current)) {\n                    continue;\n                } /* too far */\n                assert((current - matchIndex) <= XLZ4_DISTANCE_MAX);  /* match now expected within distance */\n\n                if (XLZ4_read32(match) == XLZ4_read32(ip)) {\n                    if (maybe_extMem) offset = current - matchIndex;\n                    break;   /* match found */\n                }\n\n            } while(1);\n        }\n\n        /* Catch up */\n        filledIp = ip;\n        while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }\n\n        /* Encode Literals */\n        {   unsigned const litLength = (unsigned)(ip - anchor);\n            token = op++;\n            if ((outputDirective == limitedOutput) &&  /* Check output buffer overflow */\n                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n            if ((outputDirective == fillOutput) &&\n                (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {\n                op--;\n                goto _last_literals;\n            }\n            if (litLength >= RUN_MASK) {\n                int len = (int)(litLength - RUN_MASK);\n                *token = (RUN_MASK<<ML_BITS);\n                for(; len >= 255 ; len-=255) *op++ = 255;\n                *op++ = (BYTE)len;\n            }\n            else *token = (BYTE)(litLength<<ML_BITS);\n\n            /* Copy Literals */\n            XLZ4_wildCopy8(op, anchor, op+litLength);\n            op+=litLength;\n            DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                        (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));\n        }\n\n_next_match:\n        /* at this stage, the following variables must be correctly set :\n         * - ip : at start of LZ operation\n         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict\n         * - offset : if maybe_ext_memSegment==1 (constant)\n         * - lowLimit : must be == dictionary to mean \"match is within extDict\"; must be == source otherwise\n         * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written\n         */\n\n        if ((outputDirective == fillOutput) &&\n            (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {\n            /* the match was too close to the end, rewind and go to last literals */\n            op = token;\n            goto _last_literals;\n        }\n\n        /* Encode Offset */\n        if (maybe_extMem) {   /* static test */\n            DEBUGLOG(6, \"             with offset=%u  (ext if > %i)\", offset, (int)(ip - (const BYTE*)source));\n            assert(offset <= XLZ4_DISTANCE_MAX && offset > 0);\n            XLZ4_writeLE16(op, (U16)offset); op+=2;\n        } else  {\n            DEBUGLOG(6, \"             with offset=%u  (same segment)\", (U32)(ip - match));\n            assert(ip-match <= XLZ4_DISTANCE_MAX);\n            XLZ4_writeLE16(op, (U16)(ip - match)); op+=2;\n        }\n\n        /* Encode MatchLength */\n        {   unsigned matchCode;\n\n            if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)\n              && (lowLimit==dictionary) /* match within extDict */ ) {\n                const BYTE* limit = ip + (dictEnd-match);\n                assert(dictEnd > match);\n                if (limit > matchlimit) limit = matchlimit;\n                matchCode = XLZ4_count(ip+MINMATCH, match+MINMATCH, limit);\n                ip += (size_t)matchCode + MINMATCH;\n                if (ip==limit) {\n                    unsigned const more = XLZ4_count(limit, (const BYTE*)source, matchlimit);\n                    matchCode += more;\n                    ip += more;\n                }\n                DEBUGLOG(6, \"             with matchLength=%u starting in extDict\", matchCode+MINMATCH);\n            } else {\n                matchCode = XLZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);\n                ip += (size_t)matchCode + MINMATCH;\n                DEBUGLOG(6, \"             with matchLength=%u\", matchCode+MINMATCH);\n            }\n\n            if ((outputDirective) &&    /* Check output buffer overflow */\n                (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {\n                if (outputDirective == fillOutput) {\n                    /* Match description too long : reduce it */\n                    U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;\n                    ip -= matchCode - newMatchCode;\n                    assert(newMatchCode < matchCode);\n                    matchCode = newMatchCode;\n                    if (unlikely(ip <= filledIp)) {\n                        /* We have already filled up to filledIp so if ip ends up less than filledIp\n                         * we have positions in the hash table beyond the current position. This is\n                         * a problem if we reuse the hash table. So we have to remove these positions\n                         * from the hash table.\n                         */\n                        const BYTE* ptr;\n                        DEBUGLOG(5, \"Clearing %u positions\", (U32)(filledIp - ip));\n                        for (ptr = ip; ptr <= filledIp; ++ptr) {\n                            U32 const h = XLZ4_hashPosition(ptr, tableType);\n                            XLZ4_clearHash(h, cctx->hashTable, tableType);\n                        }\n                    }\n                } else {\n                    assert(outputDirective == limitedOutput);\n                    return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n                }\n            }\n            if (matchCode >= ML_MASK) {\n                *token += ML_MASK;\n                matchCode -= ML_MASK;\n                XLZ4_write32(op, 0xFFFFFFFF);\n                while (matchCode >= 4*255) {\n                    op+=4;\n                    XLZ4_write32(op, 0xFFFFFFFF);\n                    matchCode -= 4*255;\n                }\n                op += matchCode / 255;\n                *op++ = (BYTE)(matchCode % 255);\n            } else\n                *token += (BYTE)(matchCode);\n        }\n        /* Ensure we have enough space for the last literals. */\n        assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));\n\n        anchor = ip;\n\n        /* Test end of chunk */\n        if (ip >= mflimitPlusOne) break;\n\n        /* Fill table */\n        XLZ4_putPosition(ip-2, cctx->hashTable, tableType, base);\n\n        /* Test next position */\n        if (tableType == byPtr) {\n\n            match = XLZ4_getPosition(ip, cctx->hashTable, tableType, base);\n            XLZ4_putPosition(ip, cctx->hashTable, tableType, base);\n            if ( (match+XLZ4_DISTANCE_MAX >= ip)\n              && (XLZ4_read32(match) == XLZ4_read32(ip)) )\n            { token=op++; *token=0; goto _next_match; }\n\n        } else {   /* byU32, byU16 */\n\n            U32 const h = XLZ4_hashPosition(ip, tableType);\n            U32 const current = (U32)(ip-base);\n            U32 matchIndex = XLZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if (dictDirective == usingDictCtx) {\n                if (matchIndex < startIndex) {\n                    /* there was no match, try the dictionary */\n                    matchIndex = XLZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                    matchIndex += dictDelta;\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;  /* required for match length counter */\n                }\n            } else if (dictDirective==usingExtDict) {\n                if (matchIndex < startIndex) {\n                    assert(dictBase);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;   /* required for match length counter */\n                }\n            } else {   /* single memory segment */\n                match = base + matchIndex;\n            }\n            XLZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)\n              && (((tableType==byU16) && (XLZ4_DISTANCE_MAX == XLZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+XLZ4_DISTANCE_MAX >= current))\n              && (XLZ4_read32(match) == XLZ4_read32(ip)) ) {\n                token=op++;\n                *token=0;\n                if (maybe_extMem) offset = current - matchIndex;\n                DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                            (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));\n                goto _next_match;\n            }\n        }\n\n        /* Prepare next loop */\n        forwardH = XLZ4_hashPosition(++ip, tableType);\n\n    }\n\n_last_literals:\n    /* Encode Last Literals */\n    {   size_t lastRun = (size_t)(iend - anchor);\n        if ( (outputDirective) &&  /* Check output buffer overflow */\n            (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {\n            if (outputDirective == fillOutput) {\n                /* adapt lastRun to fill 'dst' */\n                assert(olimit >= op);\n                lastRun  = (size_t)(olimit-op) - 1/*token*/;\n                lastRun -= (lastRun + 256 - RUN_MASK) / 256;  /*additional length tokens*/\n            } else {\n                assert(outputDirective == limitedOutput);\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n        }\n        DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRun);\n        if (lastRun >= RUN_MASK) {\n            size_t accumulator = lastRun - RUN_MASK;\n            *op++ = RUN_MASK << ML_BITS;\n            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;\n            *op++ = (BYTE) accumulator;\n        } else {\n            *op++ = (BYTE)(lastRun<<ML_BITS);\n        }\n        XLZ4_memcpy(op, anchor, lastRun);\n        ip = anchor + lastRun;\n        op += lastRun;\n    }\n\n    if (outputDirective == fillOutput) {\n        *inputConsumed = (int) (((const char*)ip)-source);\n    }\n    result = (int)(((char*)op) - dest);\n    assert(result > 0);\n    DEBUGLOG(5, \"XLZ4_compress_generic: compressed %i bytes into %i bytes\", inputSize, result);\n    return result;\n}\n\n/** XLZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time;\n *  takes care of src == (NULL, 0)\n *  and forward the rest to XLZ4_compress_generic_validated */\nXLZ4_FORCE_INLINE int XLZ4_compress_generic(\n                 XLZ4_stream_t_internal* const cctx,\n                 const char* const src,\n                 char* const dst,\n                 const int srcSize,\n                 int *inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int dstCapacity,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    DEBUGLOG(5, \"XLZ4_compress_generic: srcSize=%i, dstCapacity=%i\",\n                srcSize, dstCapacity);\n\n    if ((U32)srcSize > (U32)XLZ4_MAX_INPUT_SIZE) { return 0; }  /* Unsupported srcSize, too large (or negative) */\n    if (srcSize == 0) {   /* src == NULL supported if srcSize == 0 */\n        if (outputDirective != notLimited && dstCapacity <= 0) return 0;  /* no output, can't write anything */\n        DEBUGLOG(5, \"Generating an empty block\");\n        assert(outputDirective == notLimited || dstCapacity >= 1);\n        assert(dst != NULL);\n        dst[0] = 0;\n        if (outputDirective == fillOutput) {\n            assert (inputConsumed != NULL);\n            *inputConsumed = 0;\n        }\n        return 1;\n    }\n    assert(src != NULL);\n\n    return XLZ4_compress_generic_validated(cctx, src, dst, srcSize,\n                inputConsumed, /* only written into if outputDirective == fillOutput */\n                dstCapacity, outputDirective,\n                tableType, dictDirective, dictIssue, acceleration);\n}\n\n\nint XLZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    XLZ4_stream_t_internal* const ctx = & XLZ4_initStream(state, sizeof(XLZ4_stream_t)) -> internal_donotuse;\n    assert(ctx != NULL);\n    if (acceleration < 1) acceleration = XLZ4_ACCELERATION_DEFAULT;\n    if (acceleration > XLZ4_ACCELERATION_MAX) acceleration = XLZ4_ACCELERATION_MAX;\n    if (maxOutputSize >= XLZ4_compressBound(inputSize)) {\n        if (inputSize < XLZ4_64Klimit) {\n            return XLZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > XLZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return XLZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (inputSize < XLZ4_64Klimit) {\n            return XLZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > XLZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return XLZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n/**\n * XLZ4_compress_fast_extState_fastReset() :\n * A variant of XLZ4_compress_fast_extState().\n *\n * Using this variant avoids an expensive initialization step. It is only safe\n * to call if the state buffer is known to be correctly initialized already\n * (see comment in lz4.h on XLZ4_resetStream_fast() for a definition of\n * \"correctly initialized\").\n */\nint XLZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)\n{\n    XLZ4_stream_t_internal* ctx = &((XLZ4_stream_t*)state)->internal_donotuse;\n    if (acceleration < 1) acceleration = XLZ4_ACCELERATION_DEFAULT;\n    if (acceleration > XLZ4_ACCELERATION_MAX) acceleration = XLZ4_ACCELERATION_MAX;\n\n    if (dstCapacity >= XLZ4_compressBound(srcSize)) {\n        if (srcSize < XLZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            XLZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > XLZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            XLZ4_prepareTable(ctx, srcSize, tableType);\n            return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (srcSize < XLZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            XLZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > XLZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            XLZ4_prepareTable(ctx, srcSize, tableType);\n            return XLZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n\nint XLZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    int result;\n#if (XLZ4_HEAPMODE)\n    XLZ4_stream_t* ctxPtr = (XLZ4_stream_t*)ALLOC(sizeof(XLZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctxPtr == NULL) return 0;\n#else\n    XLZ4_stream_t ctx;\n    XLZ4_stream_t* const ctxPtr = &ctx;\n#endif\n    result = XLZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);\n\n#if (XLZ4_HEAPMODE)\n    FREEMEM(ctxPtr);\n#endif\n    return result;\n}\n\n\nint XLZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)\n{\n    return XLZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);\n}\n\n\n/* Note!: This function leaves the stream in an unclean/broken state!\n * It is not safe to subsequently use the same state with a _fastReset() or\n * _continue() call without resetting it. */\nstatic int XLZ4_compress_destSize_extState (XLZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n    void* const s = XLZ4_initStream(state, sizeof (*state));\n    assert(s != NULL); (void)s;\n\n    if (targetDstSize >= XLZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */\n        return XLZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);\n    } else {\n        if (*srcSizePtr < XLZ4_64Klimit) {\n            return XLZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);\n        } else {\n            tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > XLZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return XLZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);\n    }   }\n}\n\n\nint XLZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n#if (XLZ4_HEAPMODE)\n    XLZ4_stream_t* ctx = (XLZ4_stream_t*)ALLOC(sizeof(XLZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctx == NULL) return 0;\n#else\n    XLZ4_stream_t ctxBody;\n    XLZ4_stream_t* ctx = &ctxBody;\n#endif\n\n    int result = XLZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);\n\n#if (XLZ4_HEAPMODE)\n    FREEMEM(ctx);\n#endif\n    return result;\n}\n\n\n\n/*-******************************\n*  Streaming functions\n********************************/\n\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nXLZ4_stream_t* XLZ4_createStream(void)\n{\n    XLZ4_stream_t* const lz4s = (XLZ4_stream_t*)ALLOC(sizeof(XLZ4_stream_t));\n    XLZ4_STATIC_ASSERT(sizeof(XLZ4_stream_t) >= sizeof(XLZ4_stream_t_internal));\n    DEBUGLOG(4, \"XLZ4_createStream %p\", lz4s);\n    if (lz4s == NULL) return NULL;\n    XLZ4_initStream(lz4s, sizeof(*lz4s));\n    return lz4s;\n}\n#endif\n\nstatic size_t XLZ4_stream_t_alignment(void)\n{\n#if XLZ4_ALIGN_TEST\n    typedef struct { char c; XLZ4_stream_t t; } t_a;\n    return sizeof(t_a) - sizeof(XLZ4_stream_t);\n#else\n    return 1;  /* effectively disabled */\n#endif\n}\n\nXLZ4_stream_t* XLZ4_initStream (void* buffer, size_t size)\n{\n    DEBUGLOG(5, \"XLZ4_initStream\");\n    if (buffer == NULL) { return NULL; }\n    if (size < sizeof(XLZ4_stream_t)) { return NULL; }\n    if (!XLZ4_isAligned(buffer, XLZ4_stream_t_alignment())) return NULL;\n    MEM_INIT(buffer, 0, sizeof(XLZ4_stream_t_internal));\n    return (XLZ4_stream_t*)buffer;\n}\n\n/* resetStream is now deprecated,\n * prefer initStream() which is more general */\nvoid XLZ4_resetStream (XLZ4_stream_t* XLZ4_stream)\n{\n    DEBUGLOG(5, \"XLZ4_resetStream (ctx:%p)\", XLZ4_stream);\n    MEM_INIT(XLZ4_stream, 0, sizeof(XLZ4_stream_t_internal));\n}\n\nvoid XLZ4_resetStream_fast(XLZ4_stream_t* ctx) {\n    XLZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);\n}\n\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nint XLZ4_freeStream (XLZ4_stream_t* XLZ4_stream)\n{\n    if (!XLZ4_stream) return 0;   /* support free on NULL */\n    DEBUGLOG(5, \"XLZ4_freeStream %p\", XLZ4_stream);\n    FREEMEM(XLZ4_stream);\n    return (0);\n}\n#endif\n\n\n#define HASH_UNIT sizeof(reg_t)\nint XLZ4_loadDict (XLZ4_stream_t* XLZ4_dict, const char* dictionary, int dictSize)\n{\n    XLZ4_stream_t_internal* dict = &XLZ4_dict->internal_donotuse;\n    const tableType_t tableType = byU32;\n    const BYTE* p = (const BYTE*)dictionary;\n    const BYTE* const dictEnd = p + dictSize;\n    const BYTE* base;\n\n    DEBUGLOG(4, \"XLZ4_loadDict (%i bytes from %p into %p)\", dictSize, dictionary, XLZ4_dict);\n\n    /* It's necessary to reset the context,\n     * and not just continue it with prepareTable()\n     * to avoid any risk of generating overflowing matchIndex\n     * when compressing using this dictionary */\n    XLZ4_resetStream(XLZ4_dict);\n\n    /* We always increment the offset by 64 KB, since, if the dict is longer,\n     * we truncate it to the last 64k, and if it's shorter, we still want to\n     * advance by a whole window length so we can provide the guarantee that\n     * there are only valid offsets in the window, which allows an optimization\n     * in XLZ4_compress_fast_continue() where it uses noDictIssue even when the\n     * dictionary isn't a full 64k. */\n    dict->currentOffset += 64 KB;\n\n    if (dictSize < (int)HASH_UNIT) {\n        return 0;\n    }\n\n    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;\n    base = dictEnd - dict->currentOffset;\n    dict->dictionary = p;\n    dict->dictSize = (U32)(dictEnd - p);\n    dict->tableType = (U32)tableType;\n\n    while (p <= dictEnd-HASH_UNIT) {\n        XLZ4_putPosition(p, dict->hashTable, tableType, base);\n        p+=3;\n    }\n\n    return (int)dict->dictSize;\n}\n\nvoid XLZ4_attach_dictionary(XLZ4_stream_t* workingStream, const XLZ4_stream_t* dictionaryStream)\n{\n    const XLZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :\n        &(dictionaryStream->internal_donotuse);\n\n    DEBUGLOG(4, \"XLZ4_attach_dictionary (%p, %p, size %u)\",\n             workingStream, dictionaryStream,\n             dictCtx != NULL ? dictCtx->dictSize : 0);\n\n    if (dictCtx != NULL) {\n        /* If the current offset is zero, we will never look in the\n         * external dictionary context, since there is no value a table\n         * entry can take that indicate a miss. In that case, we need\n         * to bump the offset to something non-zero.\n         */\n        if (workingStream->internal_donotuse.currentOffset == 0) {\n            workingStream->internal_donotuse.currentOffset = 64 KB;\n        }\n\n        /* Don't actually attach an empty dictionary.\n         */\n        if (dictCtx->dictSize == 0) {\n            dictCtx = NULL;\n        }\n    }\n    workingStream->internal_donotuse.dictCtx = dictCtx;\n}\n\n\nstatic void XLZ4_renormDictT(XLZ4_stream_t_internal* XLZ4_dict, int nextSize)\n{\n    assert(nextSize >= 0);\n    if (XLZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) {   /* potential ptrdiff_t overflow (32-bits mode) */\n        /* rescale hash table */\n        U32 const delta = XLZ4_dict->currentOffset - 64 KB;\n        const BYTE* dictEnd = XLZ4_dict->dictionary + XLZ4_dict->dictSize;\n        int i;\n        DEBUGLOG(4, \"XLZ4_renormDictT\");\n        for (i=0; i<XLZ4_HASH_SIZE_U32; i++) {\n            if (XLZ4_dict->hashTable[i] < delta) XLZ4_dict->hashTable[i]=0;\n            else XLZ4_dict->hashTable[i] -= delta;\n        }\n        XLZ4_dict->currentOffset = 64 KB;\n        if (XLZ4_dict->dictSize > 64 KB) XLZ4_dict->dictSize = 64 KB;\n        XLZ4_dict->dictionary = dictEnd - XLZ4_dict->dictSize;\n    }\n}\n\n\nint XLZ4_compress_fast_continue (XLZ4_stream_t* XLZ4_stream,\n                                const char* source, char* dest,\n                                int inputSize, int maxOutputSize,\n                                int acceleration)\n{\n    const tableType_t tableType = byU32;\n    XLZ4_stream_t_internal* const streamPtr = &XLZ4_stream->internal_donotuse;\n    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;\n\n    DEBUGLOG(5, \"XLZ4_compress_fast_continue (inputSize=%i, dictSize=%u)\", inputSize, streamPtr->dictSize);\n\n    XLZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */\n    if (acceleration < 1) acceleration = XLZ4_ACCELERATION_DEFAULT;\n    if (acceleration > XLZ4_ACCELERATION_MAX) acceleration = XLZ4_ACCELERATION_MAX;\n\n    /* invalidate tiny dictionaries */\n    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */\n      && (dictEnd != source)           /* prefix mode */\n      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */\n      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */\n      ) {\n        DEBUGLOG(5, \"XLZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small\", streamPtr->dictSize, streamPtr->dictionary);\n        /* remove dictionary existence from history, to employ faster prefix mode */\n        streamPtr->dictSize = 0;\n        streamPtr->dictionary = (const BYTE*)source;\n        dictEnd = source;\n    }\n\n    /* Check overlapping input/dictionary space */\n    {   const char* const sourceEnd = source + inputSize;\n        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {\n            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);\n            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;\n            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;\n            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;\n        }\n    }\n\n    /* prefix mode : source data follows dictionary */\n    if (dictEnd == source) {\n        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))\n            return XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);\n        else\n            return XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);\n    }\n\n    /* external dictionary mode */\n    {   int result;\n        if (streamPtr->dictCtx) {\n            /* We depend here on the fact that dictCtx'es (produced by\n             * XLZ4_loadDict) guarantee that their tables contain no references\n             * to offsets between dictCtx->currentOffset - 64 KB and\n             * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe\n             * to use noDictIssue even when the dict isn't a full 64 KB.\n             */\n            if (inputSize > 4 KB) {\n                /* For compressing large blobs, it is faster to pay the setup\n                 * cost to copy the dictionary's tables into the active context,\n                 * so that the compression loop is only looking into one table.\n                 */\n                XLZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));\n                result = XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            } else {\n                result = XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);\n            }\n        } else {  /* small data <= 4 KB */\n            if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n                result = XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);\n            } else {\n                result = XLZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            }\n        }\n        streamPtr->dictionary = (const BYTE*)source;\n        streamPtr->dictSize = (U32)inputSize;\n        return result;\n    }\n}\n\n\n/* Hidden debug function, to force-test external dictionary mode */\nint XLZ4_compress_forceExtDict (XLZ4_stream_t* XLZ4_dict, const char* source, char* dest, int srcSize)\n{\n    XLZ4_stream_t_internal* streamPtr = &XLZ4_dict->internal_donotuse;\n    int result;\n\n    XLZ4_renormDictT(streamPtr, srcSize);\n\n    if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n        result = XLZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);\n    } else {\n        result = XLZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);\n    }\n\n    streamPtr->dictionary = (const BYTE*)source;\n    streamPtr->dictSize = (U32)srcSize;\n\n    return result;\n}\n\n\n/*! XLZ4_saveDict() :\n *  If previously compressed data block is not guaranteed to remain available at its memory location,\n *  save it into a safer place (char* safeBuffer).\n *  Note : no need to call XLZ4_loadDict() afterwards, dictionary is immediately usable,\n *         one can therefore call XLZ4_compress_fast_continue() right after.\n * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.\n */\nint XLZ4_saveDict (XLZ4_stream_t* XLZ4_dict, char* safeBuffer, int dictSize)\n{\n    XLZ4_stream_t_internal* const dict = &XLZ4_dict->internal_donotuse;\n\n    DEBUGLOG(5, \"XLZ4_saveDict : dictSize=%i, safeBuffer=%p\", dictSize, safeBuffer);\n\n    if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */\n    if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }\n\n    if (safeBuffer == NULL) assert(dictSize == 0);\n    if (dictSize > 0) {\n        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;\n        assert(dict->dictionary);\n        XLZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);\n    }\n\n    dict->dictionary = (const BYTE*)safeBuffer;\n    dict->dictSize = (U32)dictSize;\n\n    return dictSize;\n}\n\n\n\n/*-*******************************\n *  Decompression functions\n ********************************/\n\ntypedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;\n\n#undef MIN\n#define MIN(a,b)    ( (a) < (b) ? (a) : (b) )\n\n\n/* variant for decompress_unsafe()\n * does not know end of input\n * presumes input is well formed\n * note : will consume at least one byte */\nsize_t read_long_length_no_check(const BYTE** pp)\n{\n    size_t b, l = 0;\n    do { b = **pp; (*pp)++; l += b; } while (b==255);\n    DEBUGLOG(6, \"read_long_length_no_check: +length=%zu using %zu input bytes\", l, l/255 + 1)\n    return l;\n}\n\n/* core decoder variant for XLZ4_decompress_fast*()\n * for legacy support only : these entry points are deprecated.\n * - Presumes input is correctly formed (no defense vs malformed inputs)\n * - Does not know input size (presume input buffer is \"large enough\")\n * - Decompress a full block (only)\n * @return : nb of bytes read from input.\n * Note : this variant is not optimized for speed, just for maintenance.\n *        the goal is to remove support of decompress_fast*() variants by v2.0\n**/\nXLZ4_FORCE_INLINE int\nXLZ4_decompress_unsafe_generic(\n                 const BYTE* const istart,\n                 BYTE* const ostart,\n                 int decompressedSize,\n\n                 size_t prefixSize,\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note: =0 if dictStart==NULL */\n                 )\n{\n    const BYTE* ip = istart;\n    BYTE* op = (BYTE*)ostart;\n    BYTE* const oend = ostart + decompressedSize;\n    const BYTE* const prefixStart = ostart - prefixSize;\n\n    DEBUGLOG(5, \"XLZ4_decompress_unsafe_generic\");\n    if (dictStart == NULL) assert(dictSize == 0);\n\n    while (1) {\n        /* start new sequence */\n        unsigned token = *ip++;\n\n        /* literals */\n        {   size_t ll = token >> ML_BITS;\n            if (ll==15) {\n                /* long literal length */\n                ll += read_long_length_no_check(&ip);\n            }\n            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */\n            XLZ4_memmove(op, ip, ll); /* support in-place decompression */\n            op += ll;\n            ip += ll;\n            if ((size_t)(oend-op) < MFLIMIT) {\n                if (op==oend) break;  /* end of block */\n                DEBUGLOG(5, \"invalid: literals end at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must start at least MFLIMIT==12 bytes before end of output block */\n                return -1;\n        }   }\n\n        /* match */\n        {   size_t ml = token & 15;\n            size_t const offset = XLZ4_readLE16(ip);\n            ip+=2;\n\n            if (ml==15) {\n                /* long literal length */\n                ml += read_long_length_no_check(&ip);\n            }\n            ml += MINMATCH;\n\n            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */\n\n            {   const BYTE* match = op - offset;\n\n                /* out of range */\n                if (offset > (size_t)(op - prefixStart) + dictSize) {\n                    DEBUGLOG(6, \"offset out of range\");\n                    return -1;\n                }\n\n                /* check special case : extDict */\n                if (offset > (size_t)(op - prefixStart)) {\n                    /* extDict scenario */\n                    const BYTE* const dictEnd = dictStart + dictSize;\n                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));\n                    size_t const extml = (size_t)(dictEnd - extMatch);\n                    if (extml > ml) {\n                        /* match entirely within extDict */\n                        XLZ4_memmove(op, extMatch, ml);\n                        op += ml;\n                        ml = 0;\n                    } else {\n                        /* match split between extDict & prefix */\n                        XLZ4_memmove(op, extMatch, extml);\n                        op += extml;\n                        ml -= extml;\n                    }\n                    match = prefixStart;\n                }\n\n                /* match copy - slow variant, supporting overlap copy */\n                {   size_t u;\n                    for (u=0; u<ml; u++) {\n                        op[u] = match[u];\n            }   }   }\n            op += ml;\n            if ((size_t)(oend-op) < LASTLITERALS) {\n                DEBUGLOG(5, \"invalid: match ends at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */\n                return -1;\n            }\n        } /* match */\n    } /* main loop */\n    return (int)(ip - istart);\n}\n\n\n/* Read the variable-length literal or match length.\n *\n * @ip : input pointer\n * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.\n * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.\n * @error (output) - error code.  Must be set to 0 before call.\n**/\ntypedef size_t Rvl_t;\nstatic const Rvl_t rvl_error = (Rvl_t)(-1);\nXLZ4_FORCE_INLINE Rvl_t\nread_variable_length(const BYTE** ip, const BYTE* ilimit,\n                     int initial_check)\n{\n    Rvl_t s, length = 0;\n    assert(ip != NULL);\n    assert(*ip !=  NULL);\n    assert(ilimit != NULL);\n    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */\n        return rvl_error;\n    }\n    do {\n        s = **ip;\n        (*ip)++;\n        length += s;\n        if (unlikely((*ip) > ilimit)) {    /* read limit reached */\n            return rvl_error;\n        }\n        /* accumulator overflow detection (32-bit mode only) */\n        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {\n            return rvl_error;\n        }\n    } while (s==255);\n\n    return length;\n}\n\n/*! XLZ4_decompress_generic() :\n *  This generic decompression function covers all use cases.\n *  It shall be instantiated several times, using different sets of directives.\n *  Note that it is important for performance that this function really get inlined,\n *  in order to remove useless branches during compilation optimization.\n */\nXLZ4_FORCE_INLINE int\nXLZ4_decompress_generic(\n                 const char* const src,\n                 char* const dst,\n                 int srcSize,\n                 int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */\n\n                 earlyEnd_directive partialDecoding,  /* full, partial */\n                 dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */\n                 const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note : = 0 if noDict */\n                 )\n{\n    if ((src == NULL) || (outputSize < 0)) { return -1; }\n\n    {   const BYTE* ip = (const BYTE*) src;\n        const BYTE* const iend = ip + srcSize;\n\n        BYTE* op = (BYTE*) dst;\n        BYTE* const oend = op + outputSize;\n        BYTE* cpy;\n\n        const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;\n\n        const int checkOffset = (dictSize < (int)(64 KB));\n\n\n        /* Set up the \"end\" pointers for the shortcut. */\n        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;\n        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;\n\n        const BYTE* match;\n        size_t offset;\n        unsigned token;\n        size_t length;\n\n\n        DEBUGLOG(5, \"XLZ4_decompress_generic (srcSize:%i, dstSize:%i)\", srcSize, outputSize);\n\n        /* Special cases */\n        assert(lowPrefix <= op);\n        if (unlikely(outputSize==0)) {\n            /* Empty output buffer */\n            if (partialDecoding) return 0;\n            return ((srcSize==1) && (*ip==0)) ? 0 : -1;\n        }\n        if (unlikely(srcSize==0)) { return -1; }\n\n    /* XLZ4_FAST_DEC_LOOP:\n     * designed for modern OoO performance cpus,\n     * where copying reliably 32-bytes is preferable to an unpredictable branch.\n     * note : fast loop may show a regression for some client arm chips. */\n#if XLZ4_FAST_DEC_LOOP\n        if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {\n            DEBUGLOG(6, \"skip fast decode loop\");\n            goto safe_decode;\n        }\n\n        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */\n            assert(oend - op >= FASTLOOP_SAFE_DISTANCE);\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n\n                /* copy literals */\n                cpy = op+length;\n                XLZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n                if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }\n                XLZ4_wildCopy32(op, ip, cpy);\n                ip += length; op = cpy;\n            } else {\n                cpy = op+length;\n                DEBUGLOG(7, \"copy %u bytes in a 16-bytes stripe\", (unsigned)length);\n                /* We don't need to check oend, since we check it once for each loop below */\n                if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }\n                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */\n                XLZ4_memcpy(op, ip, 16);\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = XLZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n            assert(match <= op);  /* overflow check */\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                length += MINMATCH;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */\n                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n            } else {\n                length += MINMATCH;\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n\n                /* Fastpath check: skip XLZ4_wildCopy32 when true */\n                if ((dict == withPrefix64k) || (match >= lowPrefix)) {\n                    if (offset >= 8) {\n                        assert(match >= lowPrefix);\n                        assert(match <= op);\n                        assert(op + 18 <= oend);\n\n                        XLZ4_memcpy(op, match, 8);\n                        XLZ4_memcpy(op+8, match+8, 8);\n                        XLZ4_memcpy(op+16, match+16, 2);\n                        op += length;\n                        continue;\n            }   }   }\n\n            if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) {\n                        DEBUGLOG(7, \"partialDecoding: dictionary match, close to dstEnd\");\n                        length = MIN(length, (size_t)(oend-op));\n                    } else {\n                        goto _output_error;  /* end-of-block condition violated */\n                }   }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    XLZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    XLZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) { *op++ = *copyFrom++; }\n                    } else {\n                        XLZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n\n            /* copy match within block */\n            cpy = op + length;\n\n            assert((op <= oend) && (oend-op >= 32));\n            if (unlikely(offset<16)) {\n                XLZ4_memcpy_using_offset(op, match, cpy, offset);\n            } else {\n                XLZ4_wildCopy32(op, match, cpy);\n            }\n\n            op = cpy;   /* wildcopy correction */\n        }\n    safe_decode:\n#endif\n\n        /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* A two-stage shortcut for the most common case:\n             * 1) If the literal length is 0..14, and there is enough space,\n             * enter the shortcut and copy 16 bytes on behalf of the literals\n             * (in the fast mode, only 8 bytes can be safely copied this way).\n             * 2) Further if the match length is 4..18, copy 18 bytes in a similar\n             * manner; but we ensure that there's enough space in the output for\n             * those 18 bytes earlier, upon entering the shortcut (in other words,\n             * there is a combined check for both stages).\n             */\n            if ( (length != RUN_MASK)\n                /* strictly \"less than\" on input, to re-enter the loop with at least one byte */\n              && likely((ip < shortiend) & (op <= shortoend)) ) {\n                /* Copy the literals */\n                XLZ4_memcpy(op, ip, 16);\n                op += length; ip += length;\n\n                /* The second stage: prepare for match copying, decode full info.\n                 * If it doesn't work out, the info won't be wasted. */\n                length = token & ML_MASK; /* match length */\n                offset = XLZ4_readLE16(ip); ip += 2;\n                match = op - offset;\n                assert(match <= op); /* check overflow */\n\n                /* Do not deal with overlapping matches. */\n                if ( (length != ML_MASK)\n                  && (offset >= 8)\n                  && (dict==withPrefix64k || match >= lowPrefix) ) {\n                    /* Copy the match. */\n                    XLZ4_memcpy(op + 0, match + 0, 8);\n                    XLZ4_memcpy(op + 8, match + 8, 8);\n                    XLZ4_memcpy(op +16, match +16, 2);\n                    op += length + MINMATCH;\n                    /* Both stages worked, load the next token. */\n                    continue;\n                }\n\n                /* The second stage didn't work out, but the info is ready.\n                 * Propel it right to the point of match copying. */\n                goto _copy_match;\n            }\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n            }\n\n            /* copy literals */\n            cpy = op+length;\n#if XLZ4_FAST_DEC_LOOP\n        safe_literal_copy:\n#endif\n            XLZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {\n                /* We've either hit the input parsing restriction or the output parsing restriction.\n                 * In the normal scenario, decoding a full block, it must be the last sequence,\n                 * otherwise it's an error (invalid input or dimensions).\n                 * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.\n                 */\n                if (partialDecoding) {\n                    /* Since we are partial decoding we may be in this block because of the output parsing\n                     * restriction, which is not valid since the output buffer is allowed to be undersized.\n                     */\n                    DEBUGLOG(7, \"partialDecoding: copying literals, close to input or output end\")\n                    DEBUGLOG(7, \"partialDecoding: literal length = %u\", (unsigned)length);\n                    DEBUGLOG(7, \"partialDecoding: remaining space in dstBuffer : %i\", (int)(oend - op));\n                    DEBUGLOG(7, \"partialDecoding: remaining space in srcBuffer : %i\", (int)(iend - ip));\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of input.\n                     */\n                    if (ip+length > iend) {\n                        length = (size_t)(iend-ip);\n                        cpy = op + length;\n                    }\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of output space.\n                     */\n                    if (cpy > oend) {\n                        cpy = oend;\n                        assert(op<=oend);\n                        length = (size_t)(oend-op);\n                    }\n                } else {\n                     /* We must be on the last sequence (or invalid) because of the parsing limitations\n                      * so check that we exactly consume the input and don't overrun the output buffer.\n                      */\n                    if ((ip+length != iend) || (cpy > oend)) {\n                        DEBUGLOG(6, \"should have been last run of literals\")\n                        DEBUGLOG(6, \"ip(%p) + length(%i) = %p != iend (%p)\", ip, (int)length, ip+length, iend);\n                        DEBUGLOG(6, \"or cpy(%p) > oend(%p)\", cpy, oend);\n                        goto _output_error;\n                    }\n                }\n                XLZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */\n                ip += length;\n                op += length;\n                /* Necessarily EOF when !partialDecoding.\n                 * When partialDecoding, it is EOF if we've either\n                 * filled the output buffer or\n                 * can't proceed with reading an offset for following match.\n                 */\n                if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {\n                    break;\n                }\n            } else {\n                XLZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = XLZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n    _copy_match:\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */\n            }\n            length += MINMATCH;\n\n#if XLZ4_FAST_DEC_LOOP\n        safe_match_copy:\n#endif\n            if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) length = MIN(length, (size_t)(oend-op));\n                    else goto _output_error;   /* doesn't respect parsing restriction */\n                }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    XLZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    XLZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) *op++ = *copyFrom++;\n                    } else {\n                        XLZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n            assert(match >= lowPrefix);\n\n            /* copy match within block */\n            cpy = op + length;\n\n            /* partialDecoding : may end anywhere within the block */\n            assert(op<=oend);\n            if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                size_t const mlen = MIN(length, (size_t)(oend-op));\n                const BYTE* const matchEnd = match + mlen;\n                BYTE* const copyEnd = op + mlen;\n                if (matchEnd > op) {   /* overlap copy */\n                    while (op < copyEnd) { *op++ = *match++; }\n                } else {\n                    XLZ4_memcpy(op, match, mlen);\n                }\n                op = copyEnd;\n                if (op == oend) { break; }\n                continue;\n            }\n\n            if (unlikely(offset<8)) {\n                XLZ4_write32(op, 0);   /* silence msan warning when offset==0 */\n                op[0] = match[0];\n                op[1] = match[1];\n                op[2] = match[2];\n                op[3] = match[3];\n                match += inc32table[offset];\n                XLZ4_memcpy(op+4, match, 4);\n                match -= dec64table[offset];\n            } else {\n                XLZ4_memcpy(op, match, 8);\n                match += 8;\n            }\n            op += 8;\n\n            if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);\n                if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */\n                if (op < oCopyLimit) {\n                    XLZ4_wildCopy8(op, match, oCopyLimit);\n                    match += oCopyLimit - op;\n                    op = oCopyLimit;\n                }\n                while (op < cpy) { *op++ = *match++; }\n            } else {\n                XLZ4_memcpy(op, match, 8);\n                if (length > 16)  { XLZ4_wildCopy8(op+8, match+8, cpy); }\n            }\n            op = cpy;   /* wildcopy correction */\n        }\n\n        /* end of decoding */\n        DEBUGLOG(5, \"decoded %i bytes\", (int) (((char*)op)-dst));\n        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */\n\n        /* Overflow error detected */\n    _output_error:\n        return (int) (-(((const char*)ip)-src))-1;\n    }\n}\n\n\n/*===== Instantiate the API decoding functions. =====*/\n\nXLZ4_FORCE_O2\nint XLZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)\n{\n    return XLZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nint XLZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return XLZ4_decompress_generic(src, dst, compressedSize, dstCapacity,\n                                  partial_decode,\n                                  noDict, (BYTE*)dst, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nint XLZ4_decompress_fast(const char* source, char* dest, int originalSize)\n{\n    DEBUGLOG(5, \"XLZ4_decompress_fast\");\n    return XLZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, NULL, 0);\n}\n\n/*===== Instantiate a few more decoding cases, used more than once. =====*/\n\nXLZ4_FORCE_O2 /* Exported, an obsolete API function. */\nint XLZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    return XLZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nstatic int XLZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return XLZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\n/* Another obsolete API function, paired with the previous one. */\nint XLZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)\n{\n    return XLZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                64 KB, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nstatic int XLZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                               size_t prefixSize)\n{\n    return XLZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nstatic int XLZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,\n                                               size_t prefixSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return XLZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nXLZ4_FORCE_O2\nint XLZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize)\n{\n    return XLZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nXLZ4_FORCE_O2\nint XLZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return XLZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nXLZ4_FORCE_O2\nstatic int XLZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,\n                                       const void* dictStart, size_t dictSize)\n{\n    return XLZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, (const BYTE*)dictStart, dictSize);\n}\n\n/* The \"double dictionary\" mode, for use with e.g. ring buffers: the first part\n * of the dictionary is passed as prefix, and the second via dictStart + dictSize.\n * These routines are used only once, in XLZ4_decompress_*_continue().\n */\nXLZ4_FORCE_INLINE\nint XLZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                   size_t prefixSize, const void* dictStart, size_t dictSize)\n{\n    return XLZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);\n}\n\n/*===== streaming decompression functions =====*/\n\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nXLZ4_streamDecode_t* XLZ4_createStreamDecode(void)\n{\n    XLZ4_STATIC_ASSERT(sizeof(XLZ4_streamDecode_t) >= sizeof(XLZ4_streamDecode_t_internal));\n    return (XLZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(XLZ4_streamDecode_t));\n}\n\nint XLZ4_freeStreamDecode (XLZ4_streamDecode_t* XLZ4_stream)\n{\n    if (XLZ4_stream == NULL) { return 0; }  /* support free on NULL */\n    FREEMEM(XLZ4_stream);\n    return 0;\n}\n#endif\n\n/*! XLZ4_setStreamDecode() :\n *  Use this function to instruct where to find the dictionary.\n *  This function is not necessary if previous data is still available where it was decoded.\n *  Loading a size of 0 is allowed (same effect as no dictionary).\n * @return : 1 if OK, 0 if error\n */\nint XLZ4_setStreamDecode (XLZ4_streamDecode_t* XLZ4_streamDecode, const char* dictionary, int dictSize)\n{\n    XLZ4_streamDecode_t_internal* lz4sd = &XLZ4_streamDecode->internal_donotuse;\n    lz4sd->prefixSize = (size_t)dictSize;\n    if (dictSize) {\n        assert(dictionary != NULL);\n        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;\n    } else {\n        lz4sd->prefixEnd = (const BYTE*) dictionary;\n    }\n    lz4sd->externalDict = NULL;\n    lz4sd->extDictSize  = 0;\n    return 1;\n}\n\n/*! XLZ4_decoderRingBufferSize() :\n *  when setting a ring buffer for streaming decompression (optional scenario),\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n *  Note : in a ring buffer scenario,\n *  blocks are presumed decompressed next to each other.\n *  When not enough space remains for next block (remainingSize < maxBlockSize),\n *  decoding resumes from beginning of ring buffer.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nint XLZ4_decoderRingBufferSize(int maxBlockSize)\n{\n    if (maxBlockSize < 0) return 0;\n    if (maxBlockSize > XLZ4_MAX_INPUT_SIZE) return 0;\n    if (maxBlockSize < 16) maxBlockSize = 16;\n    return XLZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);\n}\n\n/*\n*_continue() :\n    These decoding functions allow decompression of multiple blocks in \"streaming\" mode.\n    Previously decoded blocks must still be available at the memory position where they were decoded.\n    If it's not possible, save the relevant part of decoded data into a safe buffer,\n    and indicate where it stands using XLZ4_setStreamDecode()\n*/\nXLZ4_FORCE_O2\nint XLZ4_decompress_safe_continue (XLZ4_streamDecode_t* XLZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    XLZ4_streamDecode_t_internal* lz4sd = &XLZ4_streamDecode->internal_donotuse;\n    int result;\n\n    if (lz4sd->prefixSize == 0) {\n        /* The first call, no dictionary yet. */\n        assert(lz4sd->extDictSize == 0);\n        result = XLZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd = (BYTE*)dest + result;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        /* They're rolling the current segment. */\n        if (lz4sd->prefixSize >= 64 KB - 1)\n            result = XLZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        else if (lz4sd->extDictSize == 0)\n            result = XLZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,\n                                                         lz4sd->prefixSize);\n        else\n            result = XLZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,\n                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)result;\n        lz4sd->prefixEnd  += result;\n    } else {\n        /* The buffer wraps around, or they're switching to another buffer. */\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = XLZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,\n                                                  lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd  = (BYTE*)dest + result;\n    }\n\n    return result;\n}\n\nXLZ4_FORCE_O2 int\nXLZ4_decompress_fast_continue (XLZ4_streamDecode_t* XLZ4_streamDecode,\n                        const char* source, char* dest, int originalSize)\n{\n    XLZ4_streamDecode_t_internal* const lz4sd =\n        (assert(XLZ4_streamDecode!=NULL), &XLZ4_streamDecode->internal_donotuse);\n    int result;\n\n    DEBUGLOG(5, \"XLZ4_decompress_fast_continue (toDecodeSize=%i)\", originalSize);\n    assert(originalSize >= 0);\n\n    if (lz4sd->prefixSize == 0) {\n        DEBUGLOG(5, \"first invocation : no prefix nor extDict\");\n        assert(lz4sd->extDictSize == 0);\n        result = XLZ4_decompress_fast(source, dest, originalSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd = (BYTE*)dest + originalSize;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        DEBUGLOG(5, \"continue using existing prefix\");\n        result = XLZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        lz4sd->prefixSize,\n                        lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)originalSize;\n        lz4sd->prefixEnd  += originalSize;\n    } else {\n        DEBUGLOG(5, \"prefix becomes extDict\");\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = XLZ4_decompress_fast_extDict(source, dest, originalSize,\n                                             lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;\n    }\n\n    return result;\n}\n\n\n/*\nAdvanced decoding functions :\n*_usingDict() :\n    These decoding functions work the same as \"_continue\" ones,\n    the dictionary must be explicitly provided within parameters\n*/\n\nint XLZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return XLZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return XLZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        }\n        assert(dictSize >= 0);\n        return XLZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return XLZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);\n}\n\nint XLZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return XLZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return XLZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);\n        }\n        assert(dictSize >= 0);\n        return XLZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return XLZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);\n}\n\nint XLZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0 || dictStart+dictSize == dest)\n        return XLZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        (size_t)dictSize, NULL, 0);\n    assert(dictSize >= 0);\n    return XLZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);\n}\n\n\n/*=*************************************************\n*  Obsolete Functions\n***************************************************/\n/* obsolete compression functions */\nint XLZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)\n{\n    return XLZ4_compress_default(source, dest, inputSize, maxOutputSize);\n}\nint XLZ4_compress(const char* src, char* dest, int srcSize)\n{\n    return XLZ4_compress_default(src, dest, srcSize, XLZ4_compressBound(srcSize));\n}\nint XLZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)\n{\n    return XLZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);\n}\nint XLZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)\n{\n    return XLZ4_compress_fast_extState(state, src, dst, srcSize, XLZ4_compressBound(srcSize), 1);\n}\nint XLZ4_compress_limitedOutput_continue (XLZ4_stream_t* XLZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    return XLZ4_compress_fast_continue(XLZ4_stream, src, dst, srcSize, dstCapacity, 1);\n}\nint XLZ4_compress_continue (XLZ4_stream_t* XLZ4_stream, const char* source, char* dest, int inputSize)\n{\n    return XLZ4_compress_fast_continue(XLZ4_stream, source, dest, inputSize, XLZ4_compressBound(inputSize), 1);\n}\n\n/*\nThese decompression functions are deprecated and should no longer be used.\nThey are only provided here for compatibility with older user programs.\n- XLZ4_uncompress is totally equivalent to XLZ4_decompress_fast\n- XLZ4_uncompress_unknownOutputSize is totally equivalent to XLZ4_decompress_safe\n*/\nint XLZ4_uncompress (const char* source, char* dest, int outputSize)\n{\n    return XLZ4_decompress_fast(source, dest, outputSize);\n}\nint XLZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)\n{\n    return XLZ4_decompress_safe(source, dest, isize, maxOutputSize);\n}\n\n/* Obsolete Streaming functions */\n\nint XLZ4_sizeofStreamState(void) { return sizeof(XLZ4_stream_t); }\n\nint XLZ4_resetStreamState(void* state, char* inputBuffer)\n{\n    (void)inputBuffer;\n    XLZ4_resetStream((XLZ4_stream_t*)state);\n    return 0;\n}\n\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nvoid* XLZ4_create (char* inputBuffer)\n{\n    (void)inputBuffer;\n    return XLZ4_createStream();\n}\n#endif\n\nchar* XLZ4_slideInputBuffer (void* state)\n{\n    /* avoid const char * -> char * conversion warning */\n    return (char *)(uptrval)((XLZ4_stream_t*)state)->internal_donotuse.dictionary;\n}\n\n#endif   /* XLZ4_COMMONDEFS_ONLY */\n"
  },
  {
    "path": "Source/UHMP/IOCompress/lz4.h",
    "content": "/*\n *  XLZ4 - Fast LZ compression algorithm\n *  Header File\n *  Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are\n   met:\n\n       * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n   copyright notice, this list of conditions and the following disclaimer\n   in the documentation and/or other materials provided with the\n   distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n   You can contact the author at :\n    - XLZ4 homepage : http://www.lz4.org\n    - XLZ4 source repository : https://github.com/lz4/lz4\n*/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef XLZ4_H_2983827168210\n#define XLZ4_H_2983827168210\n\n/* --- Dependency --- */\n#include <stddef.h>   /* size_t */\n\n\n/**\n  Introduction\n\n  XLZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,\n  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in\n  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.\n\n  The XLZ4 compression library provides in-memory compression and decompression functions.\n  It gives full buffer control to user.\n  Compression can be done in:\n    - a single step (described as Simple Functions)\n    - a single step, reusing a context (described in Advanced Functions)\n    - unbounded multiple steps (described as Streaming compression)\n\n  lz4.h generates and decodes XLZ4-compressed blocks (doc/lz4_Block_format.md).\n  Decompressing such a compressed block requires additional metadata.\n  Exact metadata depends on exact decompression function.\n  For the typical case of XLZ4_decompress_safe(),\n  metadata includes block's compressed size, and maximum bound of decompressed size.\n  Each application is free to encode and pass such metadata in whichever way it wants.\n\n  lz4.h only handle blocks, it can not generate Frames.\n\n  Blocks are different from Frames (doc/lz4_Frame_format.md).\n  Frames bundle both blocks and metadata in a specified manner.\n  Embedding metadata is required for compressed data to be self-contained and portable.\n  Frame format is delivered through a companion API, declared in lz4frame.h.\n  The `lz4` CLI can only manage frames.\n*/\n\n/*^***************************************************************\n*  Export parameters\n*****************************************************************/\n/*\n*  XLZ4_DLL_EXPORT :\n*  Enable exporting of functions when building a Windows DLL\n*  XLZ4LIB_VISIBILITY :\n*  Control library symbols visibility.\n*/\n#ifndef XLZ4LIB_VISIBILITY\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define XLZ4LIB_VISIBILITY __attribute__ ((visibility (\"default\")))\n#  else\n#    define XLZ4LIB_VISIBILITY\n#  endif\n#endif\n#if defined(XLZ4_DLL_EXPORT) && (XLZ4_DLL_EXPORT==1)\n#  define XLZ4LIB_API __declspec(dllexport) XLZ4LIB_VISIBILITY\n#elif defined(XLZ4_DLL_IMPORT) && (XLZ4_DLL_IMPORT==1)\n#  define XLZ4LIB_API __declspec(dllimport) XLZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/\n#else\n#  define XLZ4LIB_API XLZ4LIB_VISIBILITY\n#endif\n\n/*! XLZ4_FREESTANDING :\n *  When this macro is set to 1, it enables \"freestanding mode\" that is\n *  suitable for typical freestanding environment which doesn't support\n *  standard C library.\n *\n *  - XLZ4_FREESTANDING is a compile-time switch.\n *  - It requires the following macros to be defined:\n *    XLZ4_memcpy, XLZ4_memmove, XLZ4_memset.\n *  - It only enables XLZ4/HC functions which don't use heap.\n *    All XLZ4F_* functions are not supported.\n *  - See tests/freestanding.c to check its basic setup.\n */\n#if defined(XLZ4_FREESTANDING) && (XLZ4_FREESTANDING == 1)\n#  define XLZ4_HEAPMODE 0\n#  define XLZ4HC_HEAPMODE 0\n#  define XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1\n#  if !defined(XLZ4_memcpy)\n#    error \"XLZ4_FREESTANDING requires macro 'XLZ4_memcpy'.\"\n#  endif\n#  if !defined(XLZ4_memset)\n#    error \"XLZ4_FREESTANDING requires macro 'XLZ4_memset'.\"\n#  endif\n#  if !defined(XLZ4_memmove)\n#    error \"XLZ4_FREESTANDING requires macro 'XLZ4_memmove'.\"\n#  endif\n#elif ! defined(XLZ4_FREESTANDING)\n#  define XLZ4_FREESTANDING 0\n#endif\n\n\n/*------   Version   ------*/\n#define XLZ4_VERSION_MAJOR    1    /* for breaking interface changes  */\n#define XLZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */\n#define XLZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */\n\n#define XLZ4_VERSION_NUMBER (XLZ4_VERSION_MAJOR *100*100 + XLZ4_VERSION_MINOR *100 + XLZ4_VERSION_RELEASE)\n\n#define XLZ4_LIB_VERSION XLZ4_VERSION_MAJOR.XLZ4_VERSION_MINOR.XLZ4_VERSION_RELEASE\n#define XLZ4_QUOTE(str) #str\n#define XLZ4_EXPAND_AND_QUOTE(str) XLZ4_QUOTE(str)\n#define XLZ4_VERSION_STRING XLZ4_EXPAND_AND_QUOTE(XLZ4_LIB_VERSION)  /* requires v1.7.3+ */\n\nXLZ4LIB_API int XLZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */\nXLZ4LIB_API const char* XLZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */\n\n\n/*-************************************\n*  Tuning parameter\n**************************************/\n#define XLZ4_MEMORY_USAGE_MIN 10\n#define XLZ4_MEMORY_USAGE_DEFAULT 14\n#define XLZ4_MEMORY_USAGE_MAX 20\n\n/*!\n * XLZ4_MEMORY_USAGE :\n * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )\n * Increasing memory usage improves compression ratio, at the cost of speed.\n * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.\n * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache\n */\n#ifndef XLZ4_MEMORY_USAGE\n# define XLZ4_MEMORY_USAGE XLZ4_MEMORY_USAGE_DEFAULT\n#endif\n\n#if (XLZ4_MEMORY_USAGE < XLZ4_MEMORY_USAGE_MIN)\n#  error \"XLZ4_MEMORY_USAGE is too small !\"\n#endif\n\n#if (XLZ4_MEMORY_USAGE > XLZ4_MEMORY_USAGE_MAX)\n#  error \"XLZ4_MEMORY_USAGE is too large !\"\n#endif\n\n/*-************************************\n*  Simple Functions\n**************************************/\n/*! XLZ4_compress_default() :\n *  Compresses 'srcSize' bytes from buffer 'src'\n *  into already allocated 'dst' buffer of size 'dstCapacity'.\n *  Compression is guaranteed to succeed if 'dstCapacity' >= XLZ4_compressBound(srcSize).\n *  It also runs faster, so it's a recommended setting.\n *  If the function cannot compress 'src' into a more limited 'dst' budget,\n *  compression stops *immediately*, and the function result is zero.\n *  In which case, 'dst' content is undefined (invalid).\n *      srcSize : max supported value is XLZ4_MAX_INPUT_SIZE.\n *      dstCapacity : size of buffer 'dst' (which must be already allocated)\n *     @return  : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)\n *                or 0 if compression fails\n * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).\n */\nXLZ4LIB_API int XLZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);\n\n/*! XLZ4_decompress_safe() :\n *  compressedSize : is the exact complete size of the compressed block.\n *  dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.\n * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)\n *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).\n *           If the source stream is detected malformed, the function will stop decoding and return a negative result.\n * Note 1 : This function is protected against malicious data packets :\n *          it will never writes outside 'dst' buffer, nor read outside 'source' buffer,\n *          even if the compressed block is maliciously modified to order the decoder to do these actions.\n *          In such case, the decoder stops immediately, and considers the compressed block malformed.\n * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.\n *          The implementation is free to send / store / derive this information in whichever way is most beneficial.\n *          If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.\n */\nXLZ4LIB_API int XLZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);\n\n\n/*-************************************\n*  Advanced Functions\n**************************************/\n#define XLZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */\n#define XLZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)XLZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)\n\n/*! XLZ4_compressBound() :\n    Provides the maximum size that XLZ4 compression may output in a \"worst case\" scenario (input data not compressible)\n    This function is primarily useful for memory allocation purposes (destination buffer size).\n    Macro XLZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).\n    Note that XLZ4_compress_default() compresses faster when dstCapacity is >= XLZ4_compressBound(srcSize)\n        inputSize  : max supported value is XLZ4_MAX_INPUT_SIZE\n        return : maximum output size in a \"worst case\" scenario\n              or 0, if input size is incorrect (too large or negative)\n*/\nXLZ4LIB_API int XLZ4_compressBound(int inputSize);\n\n/*! XLZ4_compress_fast() :\n    Same as XLZ4_compress_default(), but allows selection of \"acceleration\" factor.\n    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.\n    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.\n    An acceleration value of \"1\" is the same as regular XLZ4_compress_default()\n    Values <= 0 will be replaced by XLZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).\n    Values > XLZ4_ACCELERATION_MAX will be replaced by XLZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).\n*/\nXLZ4LIB_API int XLZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! XLZ4_compress_fast_extState() :\n *  Same as XLZ4_compress_fast(), using an externally allocated memory space for its state.\n *  Use XLZ4_sizeofState() to know how much memory must be allocated,\n *  and allocate it on 8-bytes boundaries (using `malloc()` typically).\n *  Then, provide this buffer as `void* state` to compression function.\n */\nXLZ4LIB_API int XLZ4_sizeofState(void);\nXLZ4LIB_API int XLZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! XLZ4_compress_destSize() :\n *  Reverse the logic : compresses as much data as possible from 'src' buffer\n *  into already allocated buffer 'dst', of size >= 'targetDestSize'.\n *  This function either compresses the entire 'src' content into 'dst' if it's large enough,\n *  or fill 'dst' buffer completely with as much data as possible from 'src'.\n *  note: acceleration parameter is fixed to \"default\".\n *\n * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.\n *               New value is necessarily <= input value.\n * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)\n *           or 0 if compression fails.\n *\n * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):\n *        the produced compressed content could, in specific circumstances,\n *        require to be decompressed into a destination buffer larger\n *        by at least 1 byte than the content to decompress.\n *        If an application uses `XLZ4_compress_destSize()`,\n *        it's highly recommended to update liblz4 to v1.9.2 or better.\n *        If this can't be done or ensured,\n *        the receiving decompression function should provide\n *        a dstCapacity which is > decompressedSize, by at least 1 byte.\n *        See https://github.com/lz4/lz4/issues/859 for details\n */\nXLZ4LIB_API int XLZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);\n\n\n/*! XLZ4_decompress_safe_partial() :\n *  Decompress an XLZ4 compressed block, of size 'srcSize' at position 'src',\n *  into destination buffer 'dst' of size 'dstCapacity'.\n *  Up to 'targetOutputSize' bytes will be decoded.\n *  The function stops decoding on reaching this objective.\n *  This can be useful to boost performance\n *  whenever only the beginning of a block is required.\n *\n * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)\n *           If source stream is detected malformed, function returns a negative result.\n *\n *  Note 1 : @return can be < targetOutputSize, if compressed block contains less data.\n *\n *  Note 2 : targetOutputSize must be <= dstCapacity\n *\n *  Note 3 : this function effectively stops decoding on reaching targetOutputSize,\n *           so dstCapacity is kind of redundant.\n *           This is because in older versions of this function,\n *           decoding operation would still write complete sequences.\n *           Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,\n *           it could write more bytes, though only up to dstCapacity.\n *           Some \"margin\" used to be required for this operation to work properly.\n *           Thankfully, this is no longer necessary.\n *           The function nonetheless keeps the same signature, in an effort to preserve API compatibility.\n *\n *  Note 4 : If srcSize is the exact size of the block,\n *           then targetOutputSize can be any value,\n *           including larger than the block's decompressed size.\n *           The function will, at most, generate block's decompressed size.\n *\n *  Note 5 : If srcSize is _larger_ than block's compressed size,\n *           then targetOutputSize **MUST** be <= block's decompressed size.\n *           Otherwise, *silent corruption will occur*.\n */\nXLZ4LIB_API int XLZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);\n\n\n/*-*********************************************\n*  Streaming Compression Functions\n***********************************************/\ntypedef union XLZ4_stream_u XLZ4_stream_t;  /* incomplete type (defined later) */\n\n/**\n Note about RC_INVOKED\n\n - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).\n   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros\n\n - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)\n   and reports warning \"RC4011: identifier truncated\".\n\n - To eliminate the warning, we surround long preprocessor symbol with\n   \"#if !defined(RC_INVOKED) ... #endif\" block that means\n   \"skip this block when rc.exe is trying to read it\".\n*/\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nXLZ4LIB_API XLZ4_stream_t* XLZ4_createStream(void);\nXLZ4LIB_API int           XLZ4_freeStream (XLZ4_stream_t* streamPtr);\n#endif /* !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! XLZ4_resetStream_fast() : v1.9.0+\n *  Use this to prepare an XLZ4_stream_t for a new chain of dependent blocks\n *  (e.g., XLZ4_compress_fast_continue()).\n *\n *  An XLZ4_stream_t must be initialized once before usage.\n *  This is automatically done when created by XLZ4_createStream().\n *  However, should the XLZ4_stream_t be simply declared on stack (for example),\n *  it's necessary to initialize it first, using XLZ4_initStream().\n *\n *  After init, start any new stream with XLZ4_resetStream_fast().\n *  A same XLZ4_stream_t can be re-used multiple times consecutively\n *  and compress multiple streams,\n *  provided that it starts each new stream with XLZ4_resetStream_fast().\n *\n *  XLZ4_resetStream_fast() is much faster than XLZ4_initStream(),\n *  but is not compatible with memory regions containing garbage data.\n *\n *  Note: it's only useful to call XLZ4_resetStream_fast()\n *        in the context of streaming compression.\n *        The *extState* functions perform their own resets.\n *        Invoking XLZ4_resetStream_fast() before is redundant, and even counterproductive.\n */\nXLZ4LIB_API void XLZ4_resetStream_fast (XLZ4_stream_t* streamPtr);\n\n/*! XLZ4_loadDict() :\n *  Use this function to reference a static dictionary into XLZ4_stream_t.\n *  The dictionary must remain available during compression.\n *  XLZ4_loadDict() triggers a reset, so any previous data will be forgotten.\n *  The same dictionary will have to be loaded on decompression side for successful decoding.\n *  Dictionary are useful for better compression of small data (KB range).\n *  While XLZ4 accept any input as dictionary,\n *  results are generally better when using Zstandard's Dictionary Builder.\n *  Loading a size of 0 is allowed, and is the same as reset.\n * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)\n */\nXLZ4LIB_API int XLZ4_loadDict (XLZ4_stream_t* streamPtr, const char* dictionary, int dictSize);\n\n/*! XLZ4_compress_fast_continue() :\n *  Compress 'src' content using data from previously compressed blocks, for better compression ratio.\n * 'dst' buffer must be already allocated.\n *  If dstCapacity >= XLZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.\n *\n * @return : size of compressed block\n *           or 0 if there is an error (typically, cannot fit into 'dst').\n *\n *  Note 1 : Each invocation to XLZ4_compress_fast_continue() generates a new block.\n *           Each block has precise boundaries.\n *           Each block must be decompressed separately, calling XLZ4_decompress_*() with relevant metadata.\n *           It's not possible to append blocks together and expect a single invocation of XLZ4_decompress_*() to decompress them together.\n *\n *  Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !\n *\n *  Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.\n *           Make sure that buffers are separated, by at least one byte.\n *           This construction ensures that each block only depends on previous block.\n *\n *  Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.\n *\n *  Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.\n */\nXLZ4LIB_API int XLZ4_compress_fast_continue (XLZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! XLZ4_saveDict() :\n *  If last 64KB data cannot be guaranteed to remain available at its current memory location,\n *  save it into a safer place (char* safeBuffer).\n *  This is schematically equivalent to a memcpy() followed by XLZ4_loadDict(),\n *  but is much faster, because XLZ4_saveDict() doesn't need to rebuild tables.\n * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.\n */\nXLZ4LIB_API int XLZ4_saveDict (XLZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);\n\n\n/*-**********************************************\n*  Streaming Decompression Functions\n*  Bufferless synchronous API\n************************************************/\ntypedef union XLZ4_streamDecode_u XLZ4_streamDecode_t;   /* tracking context */\n\n/*! XLZ4_createStreamDecode() and XLZ4_freeStreamDecode() :\n *  creation / destruction of streaming decompression tracking context.\n *  A tracking context can be re-used multiple times.\n */\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nXLZ4LIB_API XLZ4_streamDecode_t* XLZ4_createStreamDecode(void);\nXLZ4LIB_API int                 XLZ4_freeStreamDecode (XLZ4_streamDecode_t* XLZ4_stream);\n#endif /* !defined(XLZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! XLZ4_setStreamDecode() :\n *  An XLZ4_streamDecode_t context can be allocated once and re-used multiple times.\n *  Use this function to start decompression of a new stream of blocks.\n *  A dictionary can optionally be set. Use NULL or size 0 for a reset order.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.\n * @return : 1 if OK, 0 if error\n */\nXLZ4LIB_API int XLZ4_setStreamDecode (XLZ4_streamDecode_t* XLZ4_streamDecode, const char* dictionary, int dictSize);\n\n/*! XLZ4_decoderRingBufferSize() : v1.8.2+\n *  Note : in a ring buffer scenario (optional),\n *  blocks are presumed decompressed next to each other\n *  up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),\n *  at which stage it resumes from beginning of ring buffer.\n *  When setting such a ring buffer for streaming decompression,\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nXLZ4LIB_API int XLZ4_decoderRingBufferSize(int maxBlockSize);\n#define XLZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize))  /* for static allocation; maxBlockSize presumed valid */\n\n/*! XLZ4_decompress_*_continue() :\n *  These decoding functions allow decompression of consecutive blocks in \"streaming\" mode.\n *  A block is an unsplittable entity, it must be presented entirely to a decompression function.\n *  Decompression functions only accepts one block at a time.\n *  The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.\n *  If less than 64KB of data has been decoded, all the data must be present.\n *\n *  Special : if decompression side sets a ring buffer, it must respect one of the following conditions :\n *  - Decompression buffer size is _at least_ XLZ4_decoderRingBufferSize(maxBlockSize).\n *    maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized.\n *    Actually, data can be produced by any source compliant with XLZ4 format specification, and respecting maxBlockSize.\n *  - Synchronized mode :\n *    Decompression buffer size is _exactly_ the same as compression buffer size,\n *    and follows exactly same update rule (block boundaries at same positions),\n *    and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),\n *    _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).\n *  - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized,\n *    and encoding ring buffer can have any size, including small ones ( < 64 KB).\n *\n *  Whenever these conditions are not possible,\n *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,\n *  then indicate where this data is saved using XLZ4_setStreamDecode(), before decompressing next block.\n*/\nXLZ4LIB_API int\nXLZ4_decompress_safe_continue (XLZ4_streamDecode_t* XLZ4_streamDecode,\n                        const char* src, char* dst,\n                        int srcSize, int dstCapacity);\n\n\n/*! XLZ4_decompress_*_usingDict() :\n *  These decoding functions work the same as\n *  a combination of XLZ4_setStreamDecode() followed by XLZ4_decompress_*_continue()\n *  They are stand-alone, and don't need an XLZ4_streamDecode_t structure.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during decompression.\n *  Performance tip : Decompression speed can be substantially increased\n *                    when dst == dictStart + dictSize.\n */\nXLZ4LIB_API int\nXLZ4_decompress_safe_usingDict(const char* src, char* dst,\n                              int srcSize, int dstCapacity,\n                              const char* dictStart, int dictSize);\n\nXLZ4LIB_API int\nXLZ4_decompress_safe_partial_usingDict(const char* src, char* dst,\n                                      int compressedSize,\n                                      int targetOutputSize, int maxOutputSize,\n                                      const char* dictStart, int dictSize);\n\n#endif /* XLZ4_H_2983827168210 */\n\n\n/*^*************************************\n * !!!!!!   STATIC LINKING ONLY   !!!!!!\n ***************************************/\n\n/*-****************************************************************************\n * Experimental section\n *\n * Symbols declared in this section must be considered unstable. Their\n * signatures or semantics may change, or they may be removed altogether in the\n * future. They are therefore only safe to depend on when the caller is\n * statically linked against the library.\n *\n * To protect against unsafe usage, not only are the declarations guarded,\n * the definitions are hidden by default\n * when building XLZ4 as a shared/dynamic library.\n *\n * In order to access these declarations,\n * define XLZ4_STATIC_LINKING_ONLY in your application\n * before including XLZ4's headers.\n *\n * In order to make their implementations accessible dynamically, you must\n * define XLZ4_PUBLISH_STATIC_FUNCTIONS when building the XLZ4 library.\n ******************************************************************************/\n\n#ifdef XLZ4_STATIC_LINKING_ONLY\n\n#ifndef XLZ4_STATIC_3504398509\n#define XLZ4_STATIC_3504398509\n\n#ifdef XLZ4_PUBLISH_STATIC_FUNCTIONS\n#define XLZ4LIB_STATIC_API XLZ4LIB_API\n#else\n#define XLZ4LIB_STATIC_API\n#endif\n\n\n/*! XLZ4_compress_fast_extState_fastReset() :\n *  A variant of XLZ4_compress_fast_extState().\n *\n *  Using this variant avoids an expensive initialization step.\n *  It is only safe to call if the state buffer is known to be correctly initialized already\n *  (see above comment on XLZ4_resetStream_fast() for a definition of \"correctly initialized\").\n *  From a high level, the difference is that\n *  this function initializes the provided state with a call to something like XLZ4_resetStream_fast()\n *  while XLZ4_compress_fast_extState() starts with a call to XLZ4_resetStream().\n */\nXLZ4LIB_STATIC_API int XLZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! XLZ4_attach_dictionary() :\n *  This is an experimental API that allows\n *  efficient use of a static dictionary many times.\n *\n *  Rather than re-loading the dictionary buffer into a working context before\n *  each compression, or copying a pre-loaded dictionary's XLZ4_stream_t into a\n *  working XLZ4_stream_t, this function introduces a no-copy setup mechanism,\n *  in which the working stream references the dictionary stream in-place.\n *\n *  Several assumptions are made about the state of the dictionary stream.\n *  Currently, only streams which have been prepared by XLZ4_loadDict() should\n *  be expected to work.\n *\n *  Alternatively, the provided dictionaryStream may be NULL,\n *  in which case any existing dictionary stream is unset.\n *\n *  If a dictionary is provided, it replaces any pre-existing stream history.\n *  The dictionary contents are the only history that can be referenced and\n *  logically immediately precede the data compressed in the first subsequent\n *  compression call.\n *\n *  The dictionary will only remain attached to the working stream through the\n *  first compression call, at the end of which it is cleared. The dictionary\n *  stream (and source buffer) must remain in-place / accessible / unchanged\n *  through the completion of the first compression call on the stream.\n */\nXLZ4LIB_STATIC_API void\nXLZ4_attach_dictionary(XLZ4_stream_t* workingStream,\n                const XLZ4_stream_t* dictionaryStream);\n\n\n/*! In-place compression and decompression\n *\n * It's possible to have input and output sharing the same buffer,\n * for highly constrained memory environments.\n * In both cases, it requires input to lay at the end of the buffer,\n * and decompression to start at beginning of the buffer.\n * Buffer size must feature some margin, hence be larger than final size.\n *\n * |<------------------------buffer--------------------------------->|\n *                             |<-----------compressed data--------->|\n * |<-----------decompressed size------------------>|\n *                                                  |<----margin---->|\n *\n * This technique is more useful for decompression,\n * since decompressed size is typically larger,\n * and margin is short.\n *\n * In-place decompression will work inside any buffer\n * which size is >= XLZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).\n * This presumes that decompressedSize > compressedSize.\n * Otherwise, it means compression actually expanded data,\n * and it would be more efficient to store such data with a flag indicating it's not compressed.\n * This can happen when data is not compressible (already compressed, or encrypted).\n *\n * For in-place compression, margin is larger, as it must be able to cope with both\n * history preservation, requiring input data to remain unmodified up to XLZ4_DISTANCE_MAX,\n * and data expansion, which can happen when input is not compressible.\n * As a consequence, buffer size requirements are much higher,\n * and memory savings offered by in-place compression are more limited.\n *\n * There are ways to limit this cost for compression :\n * - Reduce history size, by modifying XLZ4_DISTANCE_MAX.\n *   Note that it is a compile-time constant, so all compressions will apply this limit.\n *   Lower values will reduce compression ratio, except when input_size < XLZ4_DISTANCE_MAX,\n *   so it's a reasonable trick when inputs are known to be small.\n * - Require the compressor to deliver a \"maximum compressed size\".\n *   This is the `dstCapacity` parameter in `XLZ4_compress*()`.\n *   When this size is < XLZ4_COMPRESSBOUND(inputSize), then compression can fail,\n *   in which case, the return code will be 0 (zero).\n *   The caller must be ready for these cases to happen,\n *   and typically design a backup scheme to send data uncompressed.\n * The combination of both techniques can significantly reduce\n * the amount of margin required for in-place compression.\n *\n * In-place compression can work in any buffer\n * which size is >= (maxCompressedSize)\n * with maxCompressedSize == XLZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.\n * XLZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and XLZ4_DISTANCE_MAX,\n * so it's possible to reduce memory requirements by playing with them.\n */\n\n#define XLZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize)          (((compressedSize) >> 8) + 32)\n#define XLZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize)   ((decompressedSize) + XLZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize))  /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */\n\n#ifndef XLZ4_DISTANCE_MAX   /* history window size; can be user-defined at compile time */\n#  define XLZ4_DISTANCE_MAX 65535   /* set to maximum value by default */\n#endif\n\n#define XLZ4_COMPRESS_INPLACE_MARGIN                           (XLZ4_DISTANCE_MAX + 32)   /* XLZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */\n#define XLZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize)   ((maxCompressedSize) + XLZ4_COMPRESS_INPLACE_MARGIN)  /**< maxCompressedSize is generally XLZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */\n\n#endif   /* XLZ4_STATIC_3504398509 */\n#endif   /* XLZ4_STATIC_LINKING_ONLY */\n\n\n\n#ifndef XLZ4_H_98237428734687\n#define XLZ4_H_98237428734687\n\n/*-************************************************************\n *  Private Definitions\n **************************************************************\n * Do not use these definitions directly.\n * They are only exposed to allow static allocation of `XLZ4_stream_t` and `XLZ4_streamDecode_t`.\n * Accessing members will expose user code to API and/or ABI break in future versions of the library.\n **************************************************************/\n#define XLZ4_HASHLOG   (XLZ4_MEMORY_USAGE-2)\n#define XLZ4_HASHTABLESIZE (1 << XLZ4_MEMORY_USAGE)\n#define XLZ4_HASH_SIZE_U32 (1 << XLZ4_HASHLOG)       /* required as macro for static allocation */\n\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  int8_t  XLZ4_i8;\n  typedef uint8_t  XLZ4_byte;\n  typedef uint16_t XLZ4_u16;\n  typedef uint32_t XLZ4_u32;\n#else\n  typedef   signed char  XLZ4_i8;\n  typedef unsigned char  XLZ4_byte;\n  typedef unsigned short XLZ4_u16;\n  typedef unsigned int   XLZ4_u32;\n#endif\n\n/*! XLZ4_stream_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an XLZ4_stream_t object.\n**/\n\ntypedef struct XLZ4_stream_t_internal XLZ4_stream_t_internal;\nstruct XLZ4_stream_t_internal {\n    XLZ4_u32 hashTable[XLZ4_HASH_SIZE_U32];\n    const XLZ4_byte* dictionary;\n    const XLZ4_stream_t_internal* dictCtx;\n    XLZ4_u32 currentOffset;\n    XLZ4_u32 tableType;\n    XLZ4_u32 dictSize;\n    /* Implicit padding to ensure structure is aligned */\n};\n\n#define XLZ4_STREAM_MINSIZE  ((1UL << XLZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */\nunion XLZ4_stream_u {\n    char minStateSize[XLZ4_STREAM_MINSIZE];\n    XLZ4_stream_t_internal internal_donotuse;\n}; /* previously typedef'd to XLZ4_stream_t */\n\n\n/*! XLZ4_initStream() : v1.9.0+\n *  An XLZ4_stream_t structure must be initialized at least once.\n *  This is automatically done when invoking XLZ4_createStream(),\n *  but it's not when the structure is simply declared on stack (for example).\n *\n *  Use XLZ4_initStream() to properly initialize a newly declared XLZ4_stream_t.\n *  It can also initialize any arbitrary buffer of sufficient size,\n *  and will @return a pointer of proper type upon initialization.\n *\n *  Note : initialization fails if size and alignment conditions are not respected.\n *         In which case, the function will @return NULL.\n *  Note2: An XLZ4_stream_t structure guarantees correct alignment and size.\n *  Note3: Before v1.9.0, use XLZ4_resetStream() instead\n**/\nXLZ4LIB_API XLZ4_stream_t* XLZ4_initStream (void* buffer, size_t size);\n\n\n/*! XLZ4_streamDecode_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an XLZ4_streamDecode_t object.\n**/\ntypedef struct {\n    const XLZ4_byte* externalDict;\n    const XLZ4_byte* prefixEnd;\n    size_t extDictSize;\n    size_t prefixSize;\n} XLZ4_streamDecode_t_internal;\n\n#define XLZ4_STREAMDECODE_MINSIZE 32\nunion XLZ4_streamDecode_u {\n    char minStateSize[XLZ4_STREAMDECODE_MINSIZE];\n    XLZ4_streamDecode_t_internal internal_donotuse;\n} ;   /* previously typedef'd to XLZ4_streamDecode_t */\n\n\n\n/*-************************************\n*  Obsolete Functions\n**************************************/\n\n/*! Deprecation warnings\n *\n *  Deprecated functions make the compiler generate a warning when invoked.\n *  This is meant to invite users to update their source code.\n *  Should deprecation warnings be a problem, it is generally possible to disable them,\n *  typically with -Wno-deprecated-declarations for gcc\n *  or _CRT_SECURE_NO_WARNINGS in Visual.\n *\n *  Another method is to define XLZ4_DISABLE_DEPRECATE_WARNINGS\n *  before including the header file.\n */\n#ifdef XLZ4_DISABLE_DEPRECATE_WARNINGS\n#  define XLZ4_DEPRECATED(message)   /* disable deprecation warnings */\n#else\n#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */\n#    define XLZ4_DEPRECATED(message) [[deprecated(message)]]\n#  elif defined(_MSC_VER)\n#    define XLZ4_DEPRECATED(message) __declspec(deprecated(message))\n#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))\n#    define XLZ4_DEPRECATED(message) __attribute__((deprecated(message)))\n#  elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)\n#    define XLZ4_DEPRECATED(message) __attribute__((deprecated))\n#  else\n#    pragma message(\"WARNING: XLZ4_DEPRECATED needs custom implementation for this compiler\")\n#    define XLZ4_DEPRECATED(message)   /* disabled */\n#  endif\n#endif /* XLZ4_DISABLE_DEPRECATE_WARNINGS */\n\n/*! Obsolete compression functions (since v1.7.3) */\nXLZ4_DEPRECATED(\"use XLZ4_compress_default() instead\")       XLZ4LIB_API int XLZ4_compress               (const char* src, char* dest, int srcSize);\nXLZ4_DEPRECATED(\"use XLZ4_compress_default() instead\")       XLZ4LIB_API int XLZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);\nXLZ4_DEPRECATED(\"use XLZ4_compress_fast_extState() instead\") XLZ4LIB_API int XLZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);\nXLZ4_DEPRECATED(\"use XLZ4_compress_fast_extState() instead\") XLZ4LIB_API int XLZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);\nXLZ4_DEPRECATED(\"use XLZ4_compress_fast_continue() instead\") XLZ4LIB_API int XLZ4_compress_continue                (XLZ4_stream_t* XLZ4_streamPtr, const char* source, char* dest, int inputSize);\nXLZ4_DEPRECATED(\"use XLZ4_compress_fast_continue() instead\") XLZ4LIB_API int XLZ4_compress_limitedOutput_continue  (XLZ4_stream_t* XLZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);\n\n/*! Obsolete decompression functions (since v1.8.0) */\nXLZ4_DEPRECATED(\"use XLZ4_decompress_fast() instead\") XLZ4LIB_API int XLZ4_uncompress (const char* source, char* dest, int outputSize);\nXLZ4_DEPRECATED(\"use XLZ4_decompress_safe() instead\") XLZ4LIB_API int XLZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);\n\n/* Obsolete streaming functions (since v1.7.0)\n * degraded functionality; do not use!\n *\n * In order to perform streaming compression, these functions depended on data\n * that is no longer tracked in the state. They have been preserved as well as\n * possible: using them will still produce a correct output. However, they don't\n * actually retain any history between compression calls. The compression ratio\n * achieved will therefore be no better than compressing each chunk\n * independently.\n */\nXLZ4_DEPRECATED(\"Use XLZ4_createStream() instead\") XLZ4LIB_API void* XLZ4_create (char* inputBuffer);\nXLZ4_DEPRECATED(\"Use XLZ4_createStream() instead\") XLZ4LIB_API int   XLZ4_sizeofStreamState(void);\nXLZ4_DEPRECATED(\"Use XLZ4_resetStream() instead\")  XLZ4LIB_API int   XLZ4_resetStreamState(void* state, char* inputBuffer);\nXLZ4_DEPRECATED(\"Use XLZ4_saveDict() instead\")     XLZ4LIB_API char* XLZ4_slideInputBuffer (void* state);\n\n/*! Obsolete streaming decoding functions (since v1.7.0) */\nXLZ4_DEPRECATED(\"use XLZ4_decompress_safe_usingDict() instead\") XLZ4LIB_API int XLZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);\nXLZ4_DEPRECATED(\"use XLZ4_decompress_fast_usingDict() instead\") XLZ4LIB_API int XLZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);\n\n/*! Obsolete XLZ4_decompress_fast variants (since v1.9.0) :\n *  These functions used to be faster than XLZ4_decompress_safe(),\n *  but this is no longer the case. They are now slower.\n *  This is because XLZ4_decompress_fast() doesn't know the input size,\n *  and therefore must progress more cautiously into the input buffer to not read beyond the end of block.\n *  On top of that `XLZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.\n *  As a consequence, XLZ4_decompress_fast() is strongly discouraged, and deprecated.\n *\n *  The last remaining XLZ4_decompress_fast() specificity is that\n *  it can decompress a block without knowing its compressed size.\n *  Such functionality can be achieved in a more secure manner\n *  by employing XLZ4_decompress_safe_partial().\n *\n *  Parameters:\n *  originalSize : is the uncompressed size to regenerate.\n *                 `dst` must be already allocated, its size must be >= 'originalSize' bytes.\n * @return : number of bytes read from source buffer (== compressed size).\n *           The function expects to finish at block's end exactly.\n *           If the source stream is detected malformed, the function stops decoding and returns a negative result.\n *  note : XLZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.\n *         However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.\n *         Also, since match offsets are not validated, match reads from 'src' may underflow too.\n *         These issues never happen if input (compressed) data is correct.\n *         But they may happen if input data is invalid (error or intentional tampering).\n *         As a consequence, use these functions in trusted environments with trusted data **only**.\n */\nXLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using XLZ4_decompress_safe() instead\")\nXLZ4LIB_API int XLZ4_decompress_fast (const char* src, char* dst, int originalSize);\nXLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using XLZ4_decompress_safe_continue() instead\")\nXLZ4LIB_API int XLZ4_decompress_fast_continue (XLZ4_streamDecode_t* XLZ4_streamDecode, const char* src, char* dst, int originalSize);\nXLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using XLZ4_decompress_safe_usingDict() instead\")\nXLZ4LIB_API int XLZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);\n\n/*! XLZ4_resetStream() :\n *  An XLZ4_stream_t structure must be initialized at least once.\n *  This is done with XLZ4_initStream(), or XLZ4_resetStream().\n *  Consider switching to XLZ4_initStream(),\n *  invoking XLZ4_resetStream() will trigger deprecation warnings in the future.\n */\nXLZ4LIB_API void XLZ4_resetStream (XLZ4_stream_t* streamPtr);\n\n\n#endif /* XLZ4_H_98237428734687 */\n\n\n#if defined (__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "Source/UHMP/UHMP.Build.cs",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\nusing UnrealBuildTool;\n\npublic class UHMP : ModuleRules\n{\n\tpublic UHMP(ReadOnlyTargetRules Target) : base(Target)\n\t{\n\t\tPCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;\n\t\n\t\tPublicDependencyModuleNames.AddRange(new string[] { \"Core\", \"CoreUObject\", \"Engine\", \"InputCore\", \"Sockets\", \"Networking\", \"Jsonx\", \"JsonxUtilities\", \"XtensorAPI\" });\n\n\t\tPrivateDependencyModuleNames.AddRange(new string[] { });\n\n\t\tbEnableExceptions = true;\n\n\t\t// Uncomment if you are using Slate UI\n\t\t// PrivateDependencyModuleNames.AddRange(new string[] { \"Slate\", \"SlateCore\" });\n\n\t\t// Uncomment if you are using online features\n\t\t// PrivateDependencyModuleNames.Add(\"OnlineSubsystem\");\n\n\t\t// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true\n\t}\n}\n"
  },
  {
    "path": "Source/UHMP/UHMP.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#include \"UHMP.h\"\n#include \"Modules/ModuleManager.h\"\n\nIMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, UHMP, \"UHMP\" );\n"
  },
  {
    "path": "Source/UHMP/UHMP.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n\n"
  },
  {
    "path": "Source/UHMP/UHMPBlueprintFunctionLibrary.cpp",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"UHMPBlueprintFunctionLibrary.h\"\n\nvoid UUHMPBlueprintFunctionLibrary::PrintStringSpecial(FString string)\n{\n\tif (GEngine)\n\t{\n\t\tGEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, string);\n\t}\n}\n\n\nvoid UUHMPBlueprintFunctionLibrary::AssertFalse(FString string)\n{\n\tif (GEngine)\n\t{\n\t\tGEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, string);\n\t}\n\tcheck(false);\n}\n\nvoid UUHMPBlueprintFunctionLibrary::RaiseError(UObject* WorldContextObject, const FString& ErrorMessage, bool bPrintToOutputLog)\n{\n\tFString MessageToLog = FString::Printf(TEXT(\"\\\"%s\\\"\"), *ErrorMessage);\n\n\n#if WITH_EDITOR\n\t/*FKismetDebugUtilities::OnScriptException(WorldContextObject,);*/\n\tTSharedRef<FTokenizedMessage> TokenizedMessage = FTokenizedMessage::Create(EMessageSeverity::Error, FText::FromString(MessageToLog));\n\tTokenizedMessage.Get().AddToken(FUObjectToken::Create(WorldContextObject));\n\tFMessageLog BlueprintLog = FMessageLog(\"BlueprintLog\").SuppressLoggingToOutputLog(!bPrintToOutputLog);\n\tBlueprintLog.AddMessage(TokenizedMessage);\n\tBlueprintLog.Notify();\n\n#else\n\tif (bPrintToOutputLog)\n\t{\n\t\tUE_LOG(LogTemp, Error, TEXT(\"%s\"), *MessageToLog);\n\t}\n#endif\n}\n\n\nvoid UUHMPBlueprintFunctionLibrary::RaiseFatalError(UObject* WorldContextObject, const FString& ErrorMessage)\n{\n\tFString MessageToLog = FString::Printf(TEXT(\"\\\"%s\\\"\"), *ErrorMessage);\n\t//char* result = TCHAR_TO_ANSI(*ErrorMessage);\n\t//assertm(false, result);\n\n\n#if WITH_EDITOR\n\t/*FKismetDebugUtilities::OnScriptException(WorldContextObject,);*/\n\tTSharedRef<FTokenizedMessage> TokenizedMessage = FTokenizedMessage::Create(EMessageSeverity::Error, FText::FromString(MessageToLog));\n\tTokenizedMessage.Get().AddToken(FUObjectToken::Create(WorldContextObject));\n\tFMessageLog BlueprintLog = FMessageLog(\"BlueprintLog\").SuppressLoggingToOutputLog(false);\n\tBlueprintLog.AddMessage(TokenizedMessage);\n\tBlueprintLog.Notify();\n#else\n\tUE_LOG(LogTemp, Error, TEXT(\"%s\"), *MessageToLog);\n\t//ensureAlwaysMsgf(false, TEXT(\"%s\"), *Messa geToLog);\n\tcheckf(false, TEXT(\"%s\"), *MessageToLog);\n\n#endif\n}\n\n\nFVector UUHMPBlueprintFunctionLibrary::VectorClearZ(FVector Vector)\n{\n\tFVector new_vec = FVector(Vector);\n\tnew_vec.Z = 0;\n\treturn new_vec;\n}\n\n\nbool UUHMPBlueprintFunctionLibrary::IsAllZeroVector(FVector Vector)\n{\n\tif (Vector.X == 0 && Vector.Z == 0 && Vector.Y == 0) return true;\n\telse return false;\n}\n\n\nbool UUHMPBlueprintFunctionLibrary::IsZero(int x)\n{\n\tif (x == 0) return true;\n\telse return false;\n}\n\nbool UUHMPBlueprintFunctionLibrary::IsZeroFloat(float x)\n{\n\tif (x == 0) return true;\n\telse return false;\n}\n\n\nfloat UUHMPBlueprintFunctionLibrary::GetFrameRatePerGameSecond()\n{\n\treturn GEngine->FixedFrameRate;\n}\n\nfloat UUHMPBlueprintFunctionLibrary::GetSimDeltaTime(const UObject* WorldContext)\n{\n\treturn UGameplayStatics::GetGlobalTimeDilation(WorldContext) / GEngine->FixedFrameRate;\n}\n\nvoid UUHMPBlueprintFunctionLibrary::SetAITeamForPerceptionFilter(AAIController* Controller, const FGenericTeamId& NewTeamID)\n{\n\tController->SetGenericTeamId(NewTeamID);\n}\n\n\nFGenericTeamId UUHMPBlueprintFunctionLibrary::GetAITeamForPerceptionFilter(AAIController* Controller)\n{\n\treturn Controller->GetGenericTeamId();\n}\n\nTArray<int> UUHMPBlueprintFunctionLibrary::GetAffilationArray(const TArray<AAgentBaseCpp*>& agents)\n{\n\tTArray<int> AffilationArray;\n\tfor (auto agent : agents)\n\t{\n\t\tif (agent) \n\t\t{\n\t\t\tint teamID = agent->GenericTeamNo;\n\t\t\tensureMsgf(teamID>=0, TEXT(\"Team ID must > 0\"));\n\t\t\tAffilationArray.Add(teamID);\n\t\t}\n\t\telse \n\t\t{\n\t\t\tAffilationArray.Add(-1);\n\t\t}\n\t}\n\treturn AffilationArray;\n}\n\nvoid UUHMPBlueprintFunctionLibrary::MannualGc()\n{\n\t// void UEngine::ForceGarbageCollection(bool bForcePurge/*=false*/)\n\tGEngine->ForceGarbageCollection(true);\n}\n\nbool UUHMPBlueprintFunctionLibrary::IsEditor()\n{\n\treturn GEngine->IsEditor();\n}\n \n\t\n\nFVector UUHMPBlueprintFunctionLibrary::FlyingTracking(FVector self_pos, FVector dst_pos, bool maintain_z, float dis_aim)\n{\n\t// get delta vector and height error\n\tFVector delta_vec = self_pos - dst_pos;\n\tfloat z_delta = FMath::Abs(delta_vec.Z);\n\n\t// get horizontal distance to satisfy dis_aim\n\tfloat horizontal_dis_aim = 0;\n\tif (dis_aim > z_delta) \n\t{\n\t\thorizontal_dis_aim = FMath::Sqrt(dis_aim * dis_aim - z_delta * z_delta);\n\t}\n\telse\n\t{\n\t\t// PrintStringSpecial(\"z_delta > dis_aim !\");\n\t}\n\t// remove z, and normalize\n\tdelta_vec.Z = 0;\n\tdelta_vec.Normalize();\n\n\t// not sure Fvector is passed as reference or value, so copy it first\n\tFVector dst_posx;\n\tdst_posx = dst_pos;\n\tif (maintain_z)\n\t{\n\t\tdst_posx.Z = self_pos.Z;\n\t}\n\tdst_posx = dst_posx + delta_vec * horizontal_dis_aim;\n\n\treturn dst_posx;\n}\n\n\n//void bubble_sort(T arr[], int len) {\n//\tint i, j;\n//\tfor (i = 0; i < len - 1; i++)\n//\t\tfor (j = 0; j < len - 1 - i; j++)\n//\t\t\tif (arr[j] > arr[j + 1])\n//\t\t\t\tswap(arr[j], arr[j + 1]);\n//}\n\nvoid UUHMPBlueprintFunctionLibrary::SortActorListBy(TArray<AActor*> InActors, TArray<float> ScoreList, TArray<AActor*>& OutActors)\n{\n\tOutActors.Reset();\n\tint i, j;\n\tint len = InActors.Num();\n\tfor (i = 0; i < len - 1; i++)\n\t{\n\t\tfor (j = 0; j < len - 1 - i; j++)\n\t\t{\n\t\t\tif (ScoreList[j] > ScoreList[j + 1]) {\n\t\t\t\tScoreList.Swap(j, j + 1);\n\t\t\t\tInActors.Swap(j, j + 1);\n\t\t\t}\n\t\t}\n\t}\n\tOutActors = InActors;\n}\n\n\n//void UUHMPBlueprintFunctionLibrary::MyGetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors)\n//{\n//\tOutActors.Reset();\n//\n//\t// We do nothing if no is class provided, rather than giving ALL actors!\n//\tif (ActorClass)\n//\t{\n//\t\tif (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))\n//\t\t{\n//\t\t\tfor (TActorIterator<AActor> It(World, ActorClass); It; ++It)\n//\t\t\t{\n//\t\t\t\tAActor* Actor = *It;\n//\t\t\t\tOutActors.Add(Actor);\n//\t\t\t}\n//\t\t}\n//\t}\n//}\n//\n\nvoid UUHMPBlueprintFunctionLibrary::TarrayChangeClass(TArray<AActor*> InActors, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors)\n{\n\tOutActors = InActors;\n}\n\n\nvoid UUHMPBlueprintFunctionLibrary::GetAllActorsOfClassWithOrder(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors)\n{\n\tQUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsOfClass);\n\tOutActors.Reset();\n\n\t// We do nothing if no is class provided, rather than giving ALL actors!\n\tif (ActorClass)\n\t{\n\t\tif (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))\n\t\t{\n\t\t\tfor (TActorIterator<AActor> It(World, ActorClass); It; ++It)\n\t\t\t{\n\t\t\t\tAActor* Actor = *It;\n\t\t\t\tOutActors.Add(Actor);\n\t\t\t}\n\t\t\t// Sort the actors according to their z-axis location\n\t\t\tOutActors.Sort([](const AActor& Actor1, const AActor& Actor2)\n\t\t\t\t{\n\t\t\t\t\tif (Actor1.GetActorLocation().X != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().X;\n\t\t\t\t\tif (Actor1.GetActorLocation().Y != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().Y;\n\t\t\t\t\t// if (Actor1.GetActorLocation().Z != Actor2.GetActorLocation().Z) return Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t\t\treturn Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t\t});\n\n\t\t}\n\t}\n}\n\n\nvoid UUHMPBlueprintFunctionLibrary::GetAllActorsWithTagWithOrder(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors)\n{\n\tQUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsWithTag);\n\tOutActors.Reset();\n\n\t// We do nothing if no tag is provided, rather than giving ALL actors!\n\tif (!Tag.IsNone())\n\t{\n\t\tif (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))\n\t\t{\n\t\t\tfor (FActorIterator It(World); It; ++It)\n\t\t\t{\n\t\t\t\tAActor* Actor = *It;\n\t\t\t\tif (Actor->ActorHasTag(Tag))\n\t\t\t\t{\n\t\t\t\t\tOutActors.Add(Actor);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Sort the actors according to their z-axis location\n\t\t\tOutActors.Sort([](const AActor& Actor1, const AActor& Actor2)\n\t\t\t\t{\n\t\t\t\t\tif (Actor1.GetActorLocation().X != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().X;\n\t\t\t\t\tif (Actor1.GetActorLocation().Y != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().Y;\n\t\t\t\t\t// if (Actor1.GetActorLocation().Z != Actor2.GetActorLocation().Z) return Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t\t\treturn Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t\t});\n\n\t\t}\n\t}\n}\n\n\nvoid UUHMPBlueprintFunctionLibrary::GetAllActorsOfClassWithTagWithOrder(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors)\n{\n\tQUICK_SCOPE_CYCLE_COUNTER(UGameplayStatics_GetAllActorsOfClass);\n\tOutActors.Reset();\n\n\tUWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);\n\n\t// We do nothing if no is class provided, rather than giving ALL actors!\n\tif (ActorClass && World)\n\t{\n\t\tfor (TActorIterator<AActor> It(World, ActorClass); It; ++It)\n\t\t{\n\t\t\tAActor* Actor = *It;\n\t\t\tif (Actor && !Actor->IsPendingKill() && Actor->ActorHasTag(Tag))\n\t\t\t{\n\t\t\t\tOutActors.Add(Actor);\n\t\t\t}\n\t\t}\n\t\t// Sort the actors according to their z-axis location\n\t\tOutActors.Sort([](const AActor& Actor1, const AActor& Actor2)\n\t\t\t{\n\t\t\t\tif (Actor1.GetActorLocation().X != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().X;\n\t\t\t\tif (Actor1.GetActorLocation().Y != Actor2.GetActorLocation().X) return Actor1.GetActorLocation().X > Actor2.GetActorLocation().Y;\n\t\t\t\t// if (Actor1.GetActorLocation().Z != Actor2.GetActorLocation().Z) return Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t\treturn Actor1.GetActorLocation().Z > Actor2.GetActorLocation().Z;\n\t\t\t});\n\t}\n}"
  },
  {
    "path": "Source/UHMP/UHMPBlueprintFunctionLibrary.h",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"UObject/Stack.h\"\n#include \"Misc/UObjectToken.h\"\n#include \"EngineUtils.h\"\n#include \"Kismet/KismetSystemLibrary.h\"\n#include \"Kismet/BlueprintFunctionLibrary.h\"\n#include \"Kismet/GameplayStatics.h\"\n#include \"Navigation/CrowdFollowingComponent.h\"\n#include \"Runtime/CoreUObject/Public/UObject/NoExportTypes.h\"\n#include \"GenericTeamAgentInterface.h\"\n#include \"AIController.h\"\n#include \"AgentBaseCpp.h\"\n//\n//#pragma push_macro(\"NDEBUG\")\n//#undef NDEBUG\n//#include <cassert>\n//#define assertm(exp, msg) assert(((void)msg, exp))\n//#pragma pop_macro(\"NDEBUG\")\n//\n\n#include \"UHMPBlueprintFunctionLibrary.generated.h\"\n\n/**\n * \n */\nUCLASS()\nclass UHMP_API UUHMPBlueprintFunctionLibrary : public UBlueprintFunctionLibrary\n{\n\tGENERATED_BODY()\npublic:\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\")\n\t\tstatic void PrintStringSpecial(FString string);\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\")\n\t\tstatic void AssertFalse(FString string);\n\n\n\tUFUNCTION(BlueprintPure , Category = \"SpecialFn\")\n\t\tstatic FVector VectorClearZ(FVector Vector);\n\n\tUFUNCTION(BlueprintPure, Category = \"SpecialFn\")\n\t\tstatic bool IsAllZeroVector(FVector Vector);\n\n\tUFUNCTION(BlueprintPure, Category = \"SpecialFn\")\n\t\tstatic bool IsZero(int x);\n\n\tUFUNCTION(BlueprintPure, Category = \"SpecialFn\")\n\t\tstatic bool IsZeroFloat(float x);\n\n\tUFUNCTION(BlueprintCallable, meta = (WorldContext = \"WorldContextObject\", CallableWithoutWorldContext, Keywords = \"raise error\", DevelopmentOnly), Category = \"Utilities|Debugging\")\n\t\tstatic void RaiseError(UObject* WorldContextObject, const FString& ErrorMessage = FString(TEXT(\"An error occurred\")), bool bPrintToOutputLog = true);\n\t\n\tUFUNCTION(BlueprintCallable, meta = (WorldContext = \"WorldContextObject\", CallableWithoutWorldContext, Keywords = \"raise fatal error\"), Category = \"Utilities|Debugging\")\n\t\tstatic void RaiseFatalError(UObject* WorldContextObject, const FString& ErrorMessage = FString(TEXT(\"Hmap Fatal error occurred ~_~\")));\n\n\tUFUNCTION(BlueprintPure, Category = \"SpecialFn\")\n\t\tstatic float GetFrameRatePerGameSecond();\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\", meta = (WorldContext = \"WorldContextObject\", CallableWithoutWorldContext))\n\t\tstatic float GetSimDeltaTime(const UObject* WorldContextObject);\n\n\tUFUNCTION(BlueprintCallable, Category = \"AI\")\n\t\tstatic void SetAITeamForPerceptionFilter(AAIController* Controller, const FGenericTeamId& NewTeamID);\n\n\tUFUNCTION(BlueprintCallable, Category = \"AI\")\n\t\tstatic FGenericTeamId GetAITeamForPerceptionFilter(AAIController* Controller);\n\n\tUFUNCTION(BlueprintCallable, Category = \"AI\")\n\t\tstatic TArray<int> GetAffilationArray(const TArray<AAgentBaseCpp*> &agents);\n\n\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\")\n\t\tstatic void MannualGc();\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\")\n\t\tstatic bool IsEditor();\n\n\tUFUNCTION(BlueprintPure, Category = \"SpecialFn\")\n\t\tstatic FVector FlyingTracking(FVector self_pos, FVector dst_pos, bool maintain_z, float dis_aim);\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\")\n\t\tstatic void SortActorListBy(TArray<AActor*> InActors, TArray<float> ScoreList, TArray<AActor*>& OutActors);\n\n\t//UFUNCTION(BlueprintCallable, Category = \"Utilities\", meta = (WorldContext = \"WorldContextObject\", DeterminesOutputType = \"ActorClass\", DynamicOutputParam = \"OutActors\"))\n\t//\tstatic void MyGetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);\n\n\tUFUNCTION(BlueprintCallable, Category = \"SpecialFn\", meta = (WorldContext = \"WorldContextObject\", DeterminesOutputType = \"ActorClass\", DynamicOutputParam = \"OutActors\"))\n\t\tstatic void TarrayChangeClass(TArray<AActor*> InActors, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);\n\n\t/**\n\t *\tFind all Actors in the world of the specified class.\n\t *\tThis is a slow operation, use with caution e.g. do not use every frame.\n\t *\t@param\tActorClass\tClass of Actor to find. Must be specified or result array will be empty.\n\t *\t@param\tOutActors\tOutput array of Actors of the specified class.\n\t */\n\tUFUNCTION(BlueprintCallable, Category = \"Utilities\", meta = (WorldContext = \"WorldContextObject\", DeterminesOutputType = \"ActorClass\", DynamicOutputParam = \"OutActors\"))\n\t\tstatic void GetAllActorsOfClassWithOrder(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);\n\t/**\n\t *\tFind all Actors in the world with the specified tag.\n\t *\tThis is a slow operation, use with caution e.g. do not use every frame.\n\t *\t@param\tTag\t\t\tTag to find. Must be specified or result array will be empty.\n\t *\t@param\tOutActors\tOutput array of Actors of the specified tag.\n\t */\n\tUFUNCTION(BlueprintCallable, Category = \"Utilities\", meta = (WorldContext = \"WorldContextObject\"))\n\t\tstatic void GetAllActorsWithTagWithOrder(const UObject* WorldContextObject, FName Tag, TArray<AActor*>& OutActors);\n\n\t/**\n\t *\tFind all Actors in the world of the specified class with the specified tag.\n\t *\tThis is a slow operation, use with caution e.g. do not use every frame.\n\t *\t@param\tTag\t\t\tTag to find. Must be specified or result array will be empty.\n\t *\t@param\tActorClass\tClass of Actor to find. Must be specified or result array will be empty.\n\t *\t@param\tOutActors\tOutput array of Actors of the specified tag.\n\t */\n\tUFUNCTION(BlueprintCallable, Category = \"Utilities\", meta = (WorldContext = \"WorldContextObject\", DeterminesOutputType = \"ActorClass\", DynamicOutputParam = \"OutActors\"))\n\t\tstatic void GetAllActorsOfClassWithTagWithOrder(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, FName Tag, TArray<AActor*>& OutActors);\n\n};\n\n\n//UFUNCTION(BlueprintCallable, Category = \"ActorFuncions\", meta = (DeterminesOutputType = \"InputActor\"))\n//\t  static AActor* CloneActor(AActor* InputActor);"
  },
  {
    "path": "Source/UHMP.Target.cs",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\nusing UnrealBuildTool;\nusing System.Collections.Generic;\n\npublic class UHMPTarget : TargetRules\n{\n\tpublic UHMPTarget(TargetInfo Target) : base(Target)\n\t{\n\t\tType = TargetType.Game;\n\t\tDefaultBuildSettings = BuildSettingsVersion.V2;\n\t\tbUseChecksInShipping = true;\n\n\t\tExtraModuleNames.AddRange( new string[] { \"UHMP\" } );\n\t}\n}\n"
  },
  {
    "path": "Source/UHMPEditor.Target.cs",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\nusing UnrealBuildTool;\nusing System.Collections.Generic;\n\npublic class UHMPEditorTarget : TargetRules\n{\n\tpublic UHMPEditorTarget(TargetInfo Target) : base(Target)\n\t{\n\t\tType = TargetType.Editor;\n\t\tDefaultBuildSettings = BuildSettingsVersion.V2;\n\n\t\tExtraModuleNames.AddRange( new string[] { \"UHMP\" } );\n\t}\n}\n"
  },
  {
    "path": "Source/UHMPServer.Target.cs",
    "content": "// Fill out your copyright notice in the Description page of Project Settings.\n\nusing UnrealBuildTool;\nusing System.Collections.Generic;\n\npublic class UHMPServerTarget : TargetRules //Change this line according to the name of your project\n{\n    public UHMPServerTarget(TargetInfo Target) : base(Target) //Change this line according to the name of your project\n    {\n        Type = TargetType.Server;\n        DefaultBuildSettings = BuildSettingsVersion.V2;\n        bUseChecksInShipping = true;\n        ExtraModuleNames.Add(\"UHMP\"); //Change this line according to the name of your project\n    }\n}"
  },
  {
    "path": "build.py",
    "content": "import subprocess, sys\n\nWindows_Only = False\nBuild = 'Test'\n\ndef print亮绿(*kw,**kargs):\n    print(\"\\033[1;32m\",*kw,\"\\033[0m\",**kargs)\n\nif not Windows_Only:\n    process = subprocess.Popen([\n        \"C:/Program Files/FreeFileSync/FreeFileSync.exe\",\n        \"Win2Linux.ffs_batch\"\n    ])\n    process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* Finish Win2Linux ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n\n    process = subprocess.Popen([\n        \"C:/Program Files/FreeFileSync/FreeFileSync.exe\",\n        \"F:/UHMP_LINUX/CopyPackageToServer.ffs_batch\"\n    ])\n    process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* CopyPackageToServer ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n\n\n\n\n\nprint亮绿('********* Begin build windows renderer ***********')\n# build Visual\nprocess = subprocess.Popen([\n    \"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    \"-ScriptsForProject=F:/UHMP/UHMP.uproject\",\n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    \"-project=F:/UHMP/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    \"-archivedirectory=F:/UHMP/Build\",\n    \"-package\",\n    \"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph\",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    \"-targetplatform=Win64\",\n    \"-build\",\n    \"-target=UHMP\",\n    \"-clientconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\",\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* End build windows renderer ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n\n\n\n\nprint亮绿('********* Begin build windows server ***********')\n# build server\nprocess = subprocess.Popen([\n    \"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n    \"-ScriptsForProject=F:/UHMP/UHMP.uproject\",  \n    \"BuildCookRun\",\n    \"-nocompileeditor\",\n    \"-nop4\",\n    \"-project=F:/UHMP/UHMP.uproject\",\n    \"-cook\",\n    \"-stage\",\n    \"-archive\",\n    \"-archivedirectory=F:/UHMP/Build\",\n    \"-package \",\n    \"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n    \"-compressed\",\n    \"-ddc=DerivedDataBackendGraph \",\n    \"-pak\",\n    \"-prereqs\",\n    \"-nodebuginfo\",\n    \"-targetplatform=Win64\",\n    \"-build\",\n    \"-target=UHMPServer\",\n    \"-serverconfig=%s\"%Build,\n    \"-utf8output\",\n    \"-compile\"\n])\nreturn_code = process.wait()\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* End build windows server ***********')\nprint亮绿('********* ********************** ***********')\nprint亮绿('********* ********************** ***********')\nif (return_code!=0):\n    print('fail')\n    sys.exit()\n\n\n\n\nif not Windows_Only:\n\n    print亮绿('********* Begin build linux server ***********')\n    # build linux server\n    process = subprocess.Popen([\n        \"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n        \"-ScriptsForProject=F:/UHMP_LINUX/UHMP.uproject\",\n        \"BuildCookRun\",\n        \"-nocompileeditor\",\n        \"-nop4\",\n        \"-project=F:/UHMP_LINUX/UHMP.uproject\",\n        \"-cook\",\n        \"-stage\",\n        \"-archive\",\n        \"-archivedirectory=F:/UHMP_LINUX/Build\",\n        \"-package\",\n        \"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n        \"-compressed\",\n        \"-ddc=DerivedDataBackendGraph\",\n        \"-pak\",\n        \"-prereqs\",\n        \"-nodebuginfo\",\n        \"-targetplatform=Linux\",\n        \"-build\",\n        \"-target=UHMPServer\",\n        \"-serverconfig=%s\"%Build,\n        \"-utf8output\",\n        \"-compile\"\n    ])\n    return_code = process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* End build linux server ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    if (return_code!=0):\n        print('fail')\n        sys.exit()\n\n\n\n\n    print亮绿('********* Begin build linux renderer ***********')\n    # build linux renderer\n    process = subprocess.Popen([\n        \"{EnginePath}/Build/BatchFiles/RunUAT.bat\",\n        \"-ScriptsForProject=F:/UHMP_LINUX/UHMP.uproject\",\n        \"BuildCookRun\",\n        \"-nocompileeditor\",\n        \"-nop4\",\n        \"-project=F:/UHMP_LINUX/UHMP.uproject\",\n        \"-cook\",\n        \"-stage\",\n        \"-archive\",\n        \"-archivedirectory=F:/UHMP_LINUX/Build\",\n        \"-package\",\n        \"-ue4exe={EnginePath}/Binaries/Win64/UE4Editor-Cmd.exe\",\n        \"-compressed\",\n        \"-ddc=DerivedDataBackendGraph\",\n        \"-pak\",\n        \"-prereqs\",\n        \"-nodebuginfo\",\n        \"-targetplatform=Linux\",\n        \"-build\",\n        \"-target=UHMP\",\n        \"-clientconfig=%s\"%Build,\n        \"-utf8output\",\n        \"-compile\",\n    ])\n    return_code = process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* End build linux renderer  ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    if (return_code!=0):\n        print('fail')\n        sys.exit()\n\n\n\n\n\n\n    process = subprocess.Popen([\n        \"C:/Program Files/FreeFileSync/FreeFileSync.exe\",\n        \"Win2Linux.ffs_batch\"\n    ])\n    process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* Win2Linux ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n\n    process = subprocess.Popen([\n        \"C:/Program Files/FreeFileSync/FreeFileSync.exe\",\n        \"F:/UHMP_LINUX/CopyPackageToServer.ffs_batch\"\n    ])\n    process.wait()\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* CopyPackageToServer ***********')\n    print亮绿('********* ********************** ***********')\n    print亮绿('********* ********************** ***********')\n\n\n\n"
  },
  {
    "path": "compress.txt",
    "content": "./7-Zip/7z.exe a -t7z -mx4 -v4g ./Build/UHMP_v1.0.7z   ./  -xr!Binaries -xr!Intermediate -xr!Build -xr!BuildBB -xr!DerivedDataCache -xr!Saved  -xr!\".vs\" -xr!\".Build\"\n\n\nssh -p 3366 hmp@172.18.116.161 \"mkdir -p /home/hmp/UnrealHmapBinary/Version3.132/\"\nscp -P 3366 -r './Build/Linux*' hmp@172.18.116.161:/home/hmp/UnrealHmapBinary/Version3.132/"
  },
  {
    "path": "current_version",
    "content": "3.14"
  },
  {
    "path": "onedrive_util.py",
    "content": "import os\nfrom datetime import datetime\nfrom office365.sharepoint.client_context import ClientContext\nfrom office365.runtime.auth.user_credential import UserCredential\nfrom office365.runtime.client_request_exception import ClientRequestException\n\n\nroot_started = [\n    {\"type\" : \"onedrive\", \"root\": \"Documents\"},\n    {\"type\" : \"sharepoint\", \"root\" : \"Documents partages\"}\n]\n\n\nclass Main:\n\n    def __init__(self, email : str, password : str, endpoint : str, type : str):\n        self.email = email\n        self.password = password\n        self.endpoint = endpoint\n        self.type = type\n\n    \n    def __root(self):\n        return [item[\"root\"] for item in root_started if item[\"type\"].lower() == self.type.lower()][0]\n\n\n    # CONNECT USER\n    def auth(self):\n\n        return ClientContext(self.endpoint).with_credentials(\n            UserCredential(\n                user_name = self.email, \n                password = self.password\n            )\n        )\n\n\n\n    # CREATE FOLDER\n    def create_folder(self, folder_name):\n        if folder_name:\n            conn = self.auth()\n            root = self.__root()\n            conn.web.folders.add(f'{root}/{folder_name}').execute_query()\n\n            return f\"folder create success: {root}/{folder_name} !!!\"\n\n        print(\"Aucun dossier !!!\")\n        return False\n\n\n\n    # GET ALL FOLDERS FROM ROOT OR ON A FOLDER_NAME\n    def get_folders(self, folder_name : str = \"\"):\n\n        conn = self.auth()\n        root = self.__root()\n\n        folders = conn.web.get_folder_by_server_relative_url(f\"{root}/{folder_name}\").folders\n        conn.load(folders).execute_query()\n\n        return folders\n\n\n\n    # GET FILES FROM FOLDER\n    def get_files(self, folder_name : str = \"\"):\n\n        conn = self.auth()\n        root = self.__root()\n\n        files = conn.web.get_folder_by_server_relative_url(f\"{root}/{folder_name}\").files\n        conn.load(files).execute_query()\n\n        return files\n\n    \n\n    # DOWNLOAD FILE BY URL\n    def download_file(self, file_url : str):\n        conn = self.auth()\n        filename = file_url.split(\"/\")[-1]\n        dir_name =  f\"./{ datetime.now().strftime('%d-%m-%Y') }-datas\"\n\n        try:\n            os.mkdir(dir_name)\n        except FileExistsError:\n            pass\n\n        path_file = os.path.abspath( os.path.join(dir_name, filename) )\n        \n        with open(path_file, \"wb\") as local_file:\n            file = conn.web.get_file_by_server_relative_url(file_url)\n            file.download(local_file)\n            conn.execute_query()\n\n        print(f\"download { filename } success !\")\n\n\n    \n    # DOWNLOAD FILES FROM FOLDER\n    def download_files_from_folder(self, folder_name : str = \"\"):\n    \n        files = self.get_files(folder_name)\n\n        if len(files) > 0:\n            [self.download_file(file.serverRelativeUrl) for file in files]\n            return True\n\n        else:\n            print(\"Aucun Fichier téléchargé !!! Vérifiez le nom du dossier.\")\n            return False\n\n\n\n    # CHECK FOLDER EXIST    \n    def check_exist_folder(self, folder_name):\n        conn = self.auth()\n        root = self.__root()\n    \n        try:\n            req = conn.web.get_folder_by_server_relative_url(f\"{root}/{folder_name}\")\n            req.get().execute_query()\n            return req\n        \n        except ClientRequestException as e:\n            print(e)\n            return False\n\n\n\n    # UPLOAD FILE \n    def upload_file_on_folder(self, path_file_abs : str = \"\", folder_name : str = \"\"):\n        req = self.check_exist_folder(folder_name)\n\n        if req:\n            file_name = path_file_abs.split('/')[-1]\n            \n\n            size_1Mb = 1024*1024\n            chunk_size = size_1Mb * 128\n            file_size = os.path.getsize(path_file_abs)\n            def print_upload_progress(offset):\n                print(\"Uploaded '{}' bytes from '{}'...[{}%]\".format(offset, file_size, round(offset / file_size * 100, 2)))\n            q = req.files.create_upload_session(path_or_file=path_file_abs, chunk_size=chunk_size, chunk_uploaded=print_upload_progress)\n            q.execute_query()\n\n            print(f\"upload {file_name} OK !\")\n\n            return True\n\n        return False\n\n\n    # UPLOAD FILES\n    def upload_files_on_folder(self, folder_name_local : str = \"\", folder_name_online : str = \"\"):\n        req = self.check_exist_folder(folder_name_online)\n\n        if req:\n            tab_files = os.listdir(f\"{ folder_name_local }\")\n            [self.upload_file_on_folder(f\"{folder_name_local + f}\", folder_name_online) for f in tab_files if os.path.isfile(folder_name_local + f)] \n            return True\n\n        else: \n            return False\n\n\n\nclass OneDrive(Main):\n    \n    def __init__(self, email, password, endpoint, type):\n        Main.__init__(self, email, password, endpoint, type)\n\n    \n    #SHARE FOLDER\n    def share_folder(self, folder_name : str = \"\", is_edit = False):\n        conn = self.auth()\n        result = conn.web.create_anonymous_link(conn, url=f\"Documents/{folder_name}\", is_edit_link = is_edit).execute_query()\n\n        return result.value\n\n\n\n\n\n\nclass SharePoint(Main):\n    \n    def __init__(self, email, password, endpoint, type):\n        Main.__init__(self, email, password, endpoint, type)\n\n\n    def create_team_website_sharepoint(self, title : str, is_public = False):\n        conn = self.auth()\n        \n        try:\n            conn.create_team_site(alias = {title}, title = {title}, is_public = is_public)\n            print(\"creation success !!!\")\n            return True\n\n        except:\n            print(\"Erreur de création !!!\")\n            return False\n\n\n    def create_communication_website_sharepoint(self, title : str):\n        conn = self.auth()\n        \n        try:\n            conn.create_communication_site(alias = {title}, title = {title})\n            print(\"creation success !!!\")\n            return True\n\n        except:\n            print(\"Erreur de création !!!\")\n            return False\n"
  },
  {
    "path": "test_memory_leak.py",
    "content": "import os, sys\nos.chdir('./Build')\nif not os.path.exists('./hmp2g'):\n    os.system('git clone https://github.com/binary-husky/hmp2g.git -b uhmap-memleak-test')\nos.chdir('./hmp2g')\nos.system('python main.py -c ZHECKPOINT/uhmap_hete10vs10/render_result.jsonc')\n"
  },
  {
    "path": "upload_big_file.py",
    "content": "import os, time\nimport commentjson as json\nfrom onedrive_util import OneDrive  #  pip install Office365-REST-Python-Client\nfrom datetime import datetime\nimport shutil\n\n# https://pypi.org/project/onedrive-sharepoint/\ndef get_onedrive_handle():\n    # 第一步获取OneDrive访问句柄\n    email = \"fuqingxu@yiteam.tech\"\n    password = input('Enter administrator password please?')\n    endpoint = \"https://ageasga-my.sharepoint.com/personal/fuqingxu_yiteam_tech\"\n    type = \"onedrive\"\n    session = OneDrive(email=email, password=password, endpoint=endpoint, type=type)\n    return session\n\nsession = get_onedrive_handle()\n\ndef add_file_to_onedrive(session, key, path_file_name_local):\n    for retry in range(5):\n        try:\n            # 上传文件\n            shareserver_remote_root = 'ShareServer/'\n            print(f'uploading local file {path_file_name_local} with remote key {key}')\n            session.upload_file_on_folder(path_file_name_local, shareserver_remote_root)\n\n            # 创建公开链接\n            share_link = session.share_folder(os.path.join(shareserver_remote_root, os.path.basename(path_file_name_local)), is_edit=False)\n\n            # 读取manifest目录\n            dir_name = f\"./{ datetime.now().strftime('%d-%m-%Y') }-datas\"\n            session.download_file('/personal/fuqingxu_yiteam_tech/Documents/ShareServer/uhmap_manifest.jsonc')\n            manifest_path_local = os.path.relpath( os.path.join(dir_name, 'uhmap_manifest.jsonc') ).replace('\\\\','/')\n            with open(manifest_path_local, 'r', encoding='utf8') as f:\n                manifest = json.load(f)\n            manifest[key] = share_link\n\n            with open(manifest_path_local, 'w', encoding='utf8') as f:\n                json.dump(manifest, f, indent=4)\n\n            # 上传manifest目录\n            session.upload_file_on_folder(manifest_path_local, shareserver_remote_root)\n\n            print('success')\n            break\n        except:\n            print(\"upload fail\")\n\ndef get_current_version():\n    with open('current_version', 'r', encoding='utf8') as f:\n        version = f.read()\n        return version\n\ndesired_version = get_current_version()\n\ndef shutil_rmtree(p):\n    if os.path.exists(p): \n        shutil.rmtree(p)\n\n\"\"\"\n检查：是否注册了地图\n检查：是否注册了智能体\n\"\"\"\n\n\n\nplat = \"Linux\"\nkey = f\"Uhmap_{plat}_Build_Version{desired_version}\"\nshutil_rmtree('./Build/LinuxNoEditor')\nshutil_rmtree('./Build/LinuxServer')\nos.system('python BuildLinuxRender.py')\nos.system('python BuildLinuxServer.py')\nos.system(f'.\\\\7-Zip\\\\7z.exe a -tzip -mx4 ./Build/{key}.zip  ./Build/LinuxNoEditor   ./Build/LinuxServer')\n\n\n\nplat = \"Windows\"\nkey = f\"Uhmap_{plat}_Build_Version{desired_version}\"\nshutil_rmtree('./Build/WindowsNoEditor')\nshutil_rmtree('./Build/WindowsServer')\nos.system('python BuildWindowsRender.py')\nos.system('python BuildWindowsServer.py')\nos.system(f'.\\\\7-Zip\\\\7z.exe a -tzip -mx4 ./Build/{key}.zip  ./Build/WindowsNoEditor   ./Build/WindowsServer')\n\nadd_file_to_onedrive(\n    session = session, \n    key = key, \n    path_file_name_local = f'Build/{key}.zip')\n\nadd_file_to_onedrive(\n    session = session, \n    key = key, \n    path_file_name_local = f'Build/{key}.zip')\n\nos.system(f'.\\\\7-Zip\\\\7z.exe a -tzip -mx4 ./Build/uhmp-big-file-v{desired_version}.zip  -ir!Content/Model3D   Plugins  7-Zip  UHMP.uproject  EnvDesignTutorial.pptx')\nadd_file_to_onedrive(\n    session = session, \n    key = f'uhmp-big-file-v{desired_version}', \n    path_file_name_local = f'Build/uhmp-big-file-v{desired_version}.zip')"
  }
]