From 0f333f8c96494c5e2bf512380ce3dbe2f023e0eb Mon Sep 17 00:00:00 2001 From: hided62 Date: Sat, 25 Jul 2026 20:16:24 +0000 Subject: [PATCH] Add nation betting comparison fixtures --- .../monthly_finish_nation_betting.json | 69 +++++++++++++++ .../fixtures/monthly_open_nation_betting.json | 65 ++++++++++++++ hwe/compare/monthly_event_trace.php | 87 ++++++++++++++++++- 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 hwe/compare/fixtures/monthly_finish_nation_betting.json create mode 100644 hwe/compare/fixtures/monthly_open_nation_betting.json diff --git a/hwe/compare/fixtures/monthly_finish_nation_betting.json b/hwe/compare/fixtures/monthly_finish_nation_betting.json new file mode 100644 index 00000000..e6fa4bdb --- /dev/null +++ b/hwe/compare/fixtures/monthly_finish_nation_betting.json @@ -0,0 +1,69 @@ +{ + "action": "FinishNationBetting", + "args": [1], + "setup": { + "clearBetting": true, + "resetNations": true, + "deleteOtherNations": true, + "keepNationIds": [1, 2], + "nation": [ + { "id": 1, "values": { "name": "위", "gennum": 1, "power": 100, "level": 2 } }, + { "id": 2, "values": { "name": "촉", "gennum": 1, "power": 300, "level": 0 } } + ], + "resetGenerals": true, + "deleteOtherGenerals": true, + "keepGeneralIds": [1, 2], + "general": [ + { "id": 1, "values": { "name": "장수1", "nation": 1, "owner": 1, "npc": 0 } }, + { "id": 2, "values": { "name": "장수2", "nation": 2, "owner": 2, "npc": 0 } } + ], + "bettingInfo": [ + { + "id": 1, + "type": "bettingNation", + "name": "천통국 예상", + "finished": false, + "selectCnt": 1, + "isExclusive": null, + "reqInheritancePoint": true, + "openYearMonth": 2400, + "closeYearMonth": 2424, + "candidates": [ + { + "title": "위", + "info": "국력: 100
장수 수: 1
도시 수: 1", + "isHtml": true, + "aux": { "nation": 1 } + }, + { + "title": "촉", + "info": "국력: 300
장수 수: 1
도시 수: 1", + "isHtml": true, + "aux": { "nation": 2 } + } + ], + "winner": null + } + ], + "bet": [ + { "betting_id": 1, "general_id": 0, "user_id": null, "betting_type": "[-1]", "amount": 100 }, + { "betting_id": 1, "general_id": 1, "user_id": 1, "betting_type": "[0]", "amount": 100 }, + { "betting_id": 1, "general_id": 2, "user_id": 2, "betting_type": "[1]", "amount": 100 } + ], + "inheritancePrevious": { + "1": 900, + "2": 900 + }, + "syncEnvironment": true + }, + "environment": { + "year": 202, + "month": 1, + "startyear": 190 + }, + "observe": { + "generalIds": [1, 2], + "nationIds": [1, 2], + "ownerIds": [1, 2] + } +} diff --git a/hwe/compare/fixtures/monthly_open_nation_betting.json b/hwe/compare/fixtures/monthly_open_nation_betting.json new file mode 100644 index 00000000..d4f8eab6 --- /dev/null +++ b/hwe/compare/fixtures/monthly_open_nation_betting.json @@ -0,0 +1,65 @@ +{ + "action": "OpenNationBetting", + "args": [1, 500], + "setup": { + "clearBetting": true, + "resetNations": true, + "deleteOtherNations": true, + "keepNationIds": [1, 2], + "nation": [ + { + "id": 1, + "values": { + "name": "위", + "color": "#111111", + "capital": 1, + "gennum": 1, + "gold": 1000, + "rice": 2000, + "tech": 100, + "power": 100, + "level": 2, + "type": "che_유가" + } + }, + { + "id": 2, + "values": { + "name": "촉", + "color": "#222222", + "capital": 2, + "gennum": 1, + "gold": 3000, + "rice": 4000, + "tech": 200, + "power": 300, + "level": 2, + "type": "che_병가" + } + } + ], + "resetCities": true, + "city": [ + { "id": 1, "values": { "nation": 1, "level": 3 } }, + { "id": 2, "values": { "nation": 2, "level": 3 } } + ], + "resetGenerals": true, + "deleteOtherGenerals": true, + "keepGeneralIds": [1, 2], + "general": [ + { "id": 1, "values": { "name": "장수1", "nation": 1, "city": 1, "owner": 1, "npc": 0 } }, + { "id": 2, "values": { "name": "장수2", "nation": 2, "city": 2, "owner": 2, "npc": 0 } } + ], + "syncEnvironment": true + }, + "environment": { + "year": 200, + "month": 1, + "startyear": 190 + }, + "observe": { + "generalIds": [1, 2], + "cityIds": [1, 2], + "nationIds": [1, 2] + } +} diff --git a/hwe/compare/monthly_event_trace.php b/hwe/compare/monthly_event_trace.php index 11f2a016..fa0214aa 100644 --- a/hwe/compare/monthly_event_trace.php +++ b/hwe/compare/monthly_event_trace.php @@ -33,7 +33,7 @@ function comparisonMonthlyPatch(string $table, int $id, array $values): void 'idColumn' => 'nation', 'allowed' => [ 'name', 'color', 'capital', 'gennum', 'gold', 'rice', 'tech', - 'level', 'type', 'rate_tmp', 'aux', + 'power', 'level', 'type', 'rate_tmp', 'aux', ], ], 'general' => [ @@ -207,6 +207,8 @@ function comparisonMonthlyEventTraceMain(): void 'InvaderEnding', 'ChangeCity', 'ProvideNPCTroopLeader', + 'OpenNationBetting', + 'FinishNationBetting', ]; if (!in_array($actionName, $supportedActions, true)) { throw new \InvalidArgumentException('unsupported monthly action'); @@ -283,6 +285,45 @@ function comparisonMonthlyEventTraceMain(): void foreach (comparisonIntegerList($setup['clearInheritanceOwnerIds'] ?? [], 'clearInheritanceOwnerIds') as $ownerId) { DB::db()->delete('storage', 'namespace = %s', "inheritance_{$ownerId}"); } + if (($setup['clearBetting'] ?? false) === true) { + DB::db()->query('DELETE FROM ng_betting'); + DB::db()->query('DELETE FROM storage WHERE namespace = %s', 'betting'); + $gameStorage = KVStorage::getStorage(DB::db(), 'game_env'); + $gameStorage->setValue('last_betting_id', 0); + } + foreach ($setup['bettingInfo'] ?? [] as $rawBettingInfo) { + if (!is_array($rawBettingInfo) || !is_int($rawBettingInfo['id'] ?? null)) { + throw new \InvalidArgumentException('invalid setup.bettingInfo row'); + } + KVStorage::getStorage(DB::db(), 'betting')->setValue( + "id_{$rawBettingInfo['id']}", + $rawBettingInfo, + ); + } + foreach ($setup['bet'] ?? [] as $row) { + if ( + !is_array($row) + || !is_int($row['betting_id'] ?? null) + || !is_int($row['general_id'] ?? null) + || !is_string($row['betting_type'] ?? null) + || !is_int($row['amount'] ?? null) + ) { + throw new \InvalidArgumentException('invalid setup.bet row'); + } + DB::db()->insert('ng_betting', [ + 'betting_id' => $row['betting_id'], + 'general_id' => $row['general_id'], + 'user_id' => $row['user_id'] ?? null, + 'betting_type' => $row['betting_type'], + 'amount' => $row['amount'], + ]); + } + foreach ($setup['inheritancePrevious'] ?? [] as $ownerId => $value) { + if (!is_numeric($ownerId) || !is_int($value)) { + throw new \InvalidArgumentException('invalid setup.inheritancePrevious entry'); + } + KVStorage::getStorage(DB::db(), "inheritance_{$ownerId}")->setValue('previous', [$value, null]); + } if (($setup['resetDiplomacy'] ?? false) === true) { DB::db()->query('DELETE FROM diplomacy'); } @@ -379,7 +420,7 @@ function comparisonMonthlyEventTraceMain(): void throw new \InvalidArgumentException('ProcessSemiAnnual requires resource'); } $action = new $actionClass($resource); - } elseif (in_array($actionName, ['CreateManyNPC', 'RegNPC', 'RegNeutralNPC', 'RaiseInvader', 'AutoDeleteInvader', 'ChangeCity'], true)) { + } elseif (in_array($actionName, ['CreateManyNPC', 'RegNPC', 'RegNeutralNPC', 'RaiseInvader', 'AutoDeleteInvader', 'ChangeCity', 'OpenNationBetting', 'FinishNationBetting'], true)) { $args = $request['args'] ?? []; if (!is_array($args)) { throw new \InvalidArgumentException("{$actionName} args must be an array"); @@ -393,6 +434,12 @@ function comparisonMonthlyEventTraceMain(): void if ($actionName === 'ChangeCity' && count($args) !== 2) { throw new \InvalidArgumentException('ChangeCity args must have exactly two entries'); } + if ($actionName === 'OpenNationBetting' && count($args) > 2) { + throw new \InvalidArgumentException('OpenNationBetting args must have at most two entries'); + } + if ($actionName === 'FinishNationBetting' && count($args) !== 1) { + throw new \InvalidArgumentException('FinishNationBetting args must have exactly one entry'); + } $action = new $actionClass(...$args); } else { $action = new $actionClass(); @@ -574,6 +621,42 @@ function comparisonMonthlyEventTraceMain(): void 'lastNPCTroopLeaderID', ]); } + if (in_array($actionName, ['OpenNationBetting', 'FinishNationBetting'], true)) { + $bettingStorage = KVStorage::getStorage(DB::db(), 'betting'); + $response['betting'] = $bettingStorage->getAll(); + $response['bets'] = iterator_to_array( + $db->query( + 'SELECT betting_id AS bettingId, general_id AS generalId, user_id AS userId, betting_type AS selectionKey, amount FROM ng_betting ORDER BY id', + ), + ); + $response['createdEvents'] = array_map( + static fn(array $row): array => comparisonPickRow( + $row, + [ + 'id' => 'id', + 'targetCode' => 'target', + 'priority' => 'priority', + 'condition' => 'condition', + 'action' => 'action', + ], + ['condition', 'action'], + ), + iterator_to_array( + $db->query( + 'SELECT id, target, priority, `condition`, action FROM event WHERE id > %i ORDER BY id', + $eventIdBeforeAction, + ), + ), + ); + $response['gameEnvironment'] = KVStorage::getStorage(DB::db(), 'game_env')->getValues([ + 'last_betting_id', + ]); + $response['inheritancePrevious'] = []; + foreach (comparisonIntegerList($request['observe']['ownerIds'] ?? [], 'ownerIds') as $ownerId) { + $value = KVStorage::getStorage(DB::db(), "inheritance_{$ownerId}")->getValue('previous'); + $response['inheritancePrevious'][(string)$ownerId] = is_array($value) ? ($value[0] ?? 0) : 0; + } + } if ($actionName === 'RaiseInvader') { $response['createdEvents'] = array_map( static fn(array $row): array => comparisonPickRow(