TH1/Tools/RunAIDirectorBatch.ps1

158 lines
5.6 KiB
PowerShell

param(
[string]$UnityPath = $env:UNITY_EXE,
[string]$ProjectPath,
[int]$Games = 1,
[int]$Players = 17,
[int]$Width = 30,
[int]$Height = 30,
[int]$Turns = 100,
[int]$TimeoutSeconds = 1800,
[int]$MaxActions = 20000,
[int]$MaxActionsPerPlayerTurn = 260,
[string]$OutDir,
[string]$Difficulty = "LUNATIC",
[switch]$KeepGoing,
[switch]$AllowProjectAlreadyOpen,
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
$repoRoot = Split-Path -Parent $PSScriptRoot
if ([string]::IsNullOrWhiteSpace($ProjectPath)) {
$ProjectPath = Join-Path $repoRoot "Unity"
}
$ProjectPath = (Resolve-Path $ProjectPath).Path
if ([string]::IsNullOrWhiteSpace($OutDir)) {
$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$OutDir = Join-Path $ProjectPath "Logs\AI_Batch\$stamp"
}
$OutDir = [System.IO.Path]::GetFullPath($OutDir)
[System.IO.Directory]::CreateDirectory($OutDir) | Out-Null
function Get-ProjectEditorVersion([string]$PathValue) {
$versionFile = Join-Path $PathValue "ProjectSettings\ProjectVersion.txt"
if (!(Test-Path $versionFile)) { return $null }
foreach ($line in Get-Content -LiteralPath $versionFile) {
$match = [regex]::Match($line, '^m_EditorVersion:\s*(\S+)')
if ($match.Success) { return $match.Groups[1].Value }
}
return $null
}
if ([string]::IsNullOrWhiteSpace($UnityPath)) {
$hubRoot = "C:\Program Files\Unity\Hub\Editor"
if (Test-Path $hubRoot) {
$projectEditorVersion = Get-ProjectEditorVersion $ProjectPath
if (![string]::IsNullOrWhiteSpace($projectEditorVersion)) {
$projectUnityPath = Join-Path $hubRoot "$projectEditorVersion\Editor\Unity.exe"
if (Test-Path $projectUnityPath) {
$UnityPath = $projectUnityPath
}
}
if ([string]::IsNullOrWhiteSpace($UnityPath)) {
$UnityPath = Get-ChildItem -Path $hubRoot -Directory |
Where-Object { $_.Name -match '^2022\.3\.(\d+)' } |
ForEach-Object {
[pscustomobject]@{
Path = Join-Path $_.FullName "Editor\Unity.exe"
Patch = [int]$Matches[1]
Name = $_.Name
}
} |
Where-Object { Test-Path $_.Path } |
Sort-Object Patch, Name -Descending |
Select-Object -ExpandProperty Path -First 1
}
}
}
if ([string]::IsNullOrWhiteSpace($UnityPath) -or !(Test-Path $UnityPath)) {
throw "Unity.exe not found. Pass -UnityPath or set UNITY_EXE."
}
function Normalize-ProjectPath([string]$PathValue) {
if ([string]::IsNullOrWhiteSpace($PathValue)) { return "" }
return [System.IO.Path]::GetFullPath($PathValue).TrimEnd('\', '/').ToLowerInvariant()
}
function Get-ProjectPathFromUnityCommandLine([string]$CommandLine) {
if ([string]::IsNullOrWhiteSpace($CommandLine)) { return $null }
$match = [regex]::Match($CommandLine, '(?i)-projectpath\s+(?:"([^"]+)"|([^\s]+))')
if (!$match.Success) { return $null }
if ($match.Groups[1].Success) { return $match.Groups[1].Value }
return $match.Groups[2].Value
}
function Quote-Argument([string]$Value) {
if ($null -eq $Value) { return '""' }
if ($Value -notmatch '[\s"]') { return $Value }
return '"' + ($Value -replace '"', '\"') + '"'
}
$logFile = Join-Path $OutDir "unity_batch.log"
$failFast = if ($KeepGoing) { "false" } else { "true" }
$args = @(
"-batchmode",
"-projectPath", $ProjectPath,
"-executeMethod", "TH1_Logic.Editor.AIDirectorBatchRunner.Run",
"-logFile", $logFile,
"-aiBatchGames", $Games,
"-aiBatchPlayers", $Players,
"-aiBatchWidth", $Width,
"-aiBatchHeight", $Height,
"-aiBatchTurns", $Turns,
"-aiBatchTimeoutSeconds", $TimeoutSeconds,
"-aiBatchMaxActions", $MaxActions,
"-aiBatchMaxActionsPerPlayerTurn", $MaxActionsPerPlayerTurn,
"-aiBatchDifficulty", $Difficulty,
"-aiBatchFailFast", $failFast,
"-aiBatchOut", $OutDir
)
Write-Host "[AI.Batch] Unity: $UnityPath"
Write-Host "[AI.Batch] Project: $ProjectPath"
Write-Host "[AI.Batch] Output: $OutDir"
Write-Host "[AI.Batch] Log: $logFile"
Write-Host "[AI.Batch] Args: $($args -join ' ')"
if ($DryRun) {
exit 0
}
$normalizedProjectPath = Normalize-ProjectPath $ProjectPath
$openEditors = Get-CimInstance Win32_Process -Filter "Name = 'Unity.exe'" |
Where-Object {
$openProject = Get-ProjectPathFromUnityCommandLine $_.CommandLine
(Normalize-ProjectPath $openProject) -eq $normalizedProjectPath
}
if ($openEditors -and !$AllowProjectAlreadyOpen) {
$ids = ($openEditors | Select-Object -ExpandProperty ProcessId) -join ", "
Write-Host "[AI.Batch] ERROR: Unity project is already open by process id(s): $ids. Close the editor first, or pass -AllowProjectAlreadyOpen if you intentionally want Unity to fail fast." -ForegroundColor Red
exit 20
}
$argumentLine = ($args | ForEach-Object { Quote-Argument ([string]$_) }) -join " "
$unityProcess = Start-Process -FilePath $UnityPath -ArgumentList $argumentLine -Wait -PassThru -WindowStyle Hidden
$unityExitCode = if ($null -ne $unityProcess.ExitCode) { $unityProcess.ExitCode } else { 0 }
$summaryPath = Join-Path $OutDir "batch_summary.json"
if (!(Test-Path $summaryPath)) {
Write-Host "[AI.Batch] ERROR: AI batch did not produce $summaryPath. Check $logFile." -ForegroundColor Red
if ($unityExitCode -ne 0) { exit $unityExitCode }
exit 21
}
if ($unityExitCode -ne 0) {
Write-Host "[AI.Batch] ERROR: Unity exited with code $unityExitCode. Check $logFile." -ForegroundColor Red
exit $unityExitCode
}
Write-Host "[AI.Batch] Summary: $summaryPath"
exit 0