394 lines
13 KiB
PHP
394 lines
13 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use sammo\CentennialAllStarGrowth;
|
|
use sammo\CentennialAllStarGrowthService;
|
|
use sammo\General;
|
|
use sammo\GameConst;
|
|
|
|
$loader = require __DIR__ . '/../vendor/autoload.php';
|
|
$loader->addPsr4('sammo\\', __DIR__ . '/../hwe/sammo', true);
|
|
|
|
require_once __DIR__ . '/../hwe/sammo/ActionLogger.php';
|
|
require_once __DIR__ . '/../hwe/sammo/GameConstBase.php';
|
|
require_once __DIR__ . '/../hwe/d_setting/GameConst.php';
|
|
require_once __DIR__ . '/../hwe/sammo/CentennialAllStarGrowthService.php';
|
|
|
|
final class CentennialAllStarGrowthTest extends TestCase
|
|
{
|
|
public function testProgressMilestones(): void
|
|
{
|
|
self::assertSame(0.0, CentennialAllStarGrowth::progress(180, 180, 1));
|
|
self::assertEqualsWithDelta(0.2, CentennialAllStarGrowth::progress(180, 183, 1), 0.000001);
|
|
self::assertEqualsWithDelta(0.4, CentennialAllStarGrowth::progress(180, 186, 1), 0.000001);
|
|
self::assertSame(1.0, CentennialAllStarGrowth::progress(180, 195, 1));
|
|
self::assertSame(1.0, CentennialAllStarGrowth::progress(180, 210, 1));
|
|
}
|
|
|
|
public function testMAndGGeneralsAdvanceAtNinetyPercentProgress(): void
|
|
{
|
|
self::assertSame(
|
|
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
|
CentennialAllStarGrowthService::progressMultiplierForNPCType(3)
|
|
);
|
|
self::assertSame(
|
|
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
|
CentennialAllStarGrowthService::progressMultiplierForNPCType(4)
|
|
);
|
|
self::assertSame(1.0, CentennialAllStarGrowthService::progressMultiplierForNPCType(2));
|
|
self::assertEqualsWithDelta(
|
|
0.36,
|
|
CentennialAllStarGrowthService::calculateProgress(180, 186, 1, 0.9),
|
|
0.000001
|
|
);
|
|
self::assertEqualsWithDelta(
|
|
0.9,
|
|
CentennialAllStarGrowthService::calculateProgress(180, 195, 1, 0.9),
|
|
0.000001
|
|
);
|
|
self::assertEqualsWithDelta(
|
|
0.9,
|
|
CentennialAllStarGrowthService::calculateProgress(180, 210, 1, 0.9),
|
|
0.000001
|
|
);
|
|
self::assertSame(
|
|
91,
|
|
CentennialAllStarGrowth::statFloor(100, 15, 0.9)
|
|
);
|
|
self::assertSame(
|
|
0.4,
|
|
CentennialAllStarGrowthService::dexTargetRatioForNPCType(3)
|
|
);
|
|
self::assertSame(
|
|
0.4,
|
|
CentennialAllStarGrowthService::dexTargetRatioForNPCType(4)
|
|
);
|
|
self::assertSame(
|
|
1.0,
|
|
CentennialAllStarGrowthService::dexTargetRatioForNPCType(2)
|
|
);
|
|
}
|
|
|
|
public function testProgressMultiplierMustStayWithinUnitInterval(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
CentennialAllStarGrowthService::calculateProgress(180, 195, 1, 1.01);
|
|
}
|
|
|
|
public function testLowStatGrowsWhileHigherStatStays(): void
|
|
{
|
|
$floor = CentennialAllStarGrowth::statFloor(90, 15, 0.6);
|
|
self::assertSame(60, $floor);
|
|
self::assertSame(
|
|
['value' => 70, 'granted' => 0, 'delta' => 0],
|
|
CentennialAllStarGrowth::advance(70, 0, $floor)
|
|
);
|
|
self::assertSame(
|
|
['value' => 60, 'granted' => 10, 'delta' => 10],
|
|
CentennialAllStarGrowth::advance(50, 0, $floor)
|
|
);
|
|
}
|
|
|
|
public function testUserInitialStatsPreserveCandidateShapeAtOrdinaryTotal(): void
|
|
{
|
|
$initial = CentennialAllStarGrowthService::calculateUserInitialStats([
|
|
'leadership' => 80,
|
|
'strength' => 70,
|
|
'intel' => 50,
|
|
]);
|
|
|
|
self::assertSame([
|
|
'leadership' => 65,
|
|
'strength' => 58,
|
|
'intel' => 42,
|
|
], $initial);
|
|
self::assertSame(165, array_sum($initial));
|
|
self::assertLessThanOrEqual(80, $initial['leadership']);
|
|
self::assertLessThanOrEqual(70, $initial['strength']);
|
|
self::assertLessThanOrEqual(50, $initial['intel']);
|
|
}
|
|
|
|
public function testUserInitialStatsDoNotRaiseCandidateBelowOrdinaryTotal(): void
|
|
{
|
|
self::assertSame([
|
|
'leadership' => 60,
|
|
'strength' => 45,
|
|
'intel' => 30,
|
|
], CentennialAllStarGrowthService::calculateUserInitialStats([
|
|
'leadership' => 60,
|
|
'strength' => 45,
|
|
'intel' => 30,
|
|
]));
|
|
}
|
|
|
|
public function testCurrentReselectionBaselineUsesCurrentYearProgress(): void
|
|
{
|
|
$target = [
|
|
'leadership' => 100,
|
|
'strength' => 15,
|
|
'intel' => 100,
|
|
];
|
|
|
|
self::assertSame([
|
|
'leadership' => 75,
|
|
'strength' => 15,
|
|
'intel' => 75,
|
|
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
|
$target,
|
|
['startyear' => 180, 'year' => 180, 'month' => 1]
|
|
));
|
|
self::assertSame([
|
|
'leadership' => 83,
|
|
'strength' => 15,
|
|
'intel' => 83,
|
|
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
|
$target,
|
|
['startyear' => 180, 'year' => 192, 'month' => 1]
|
|
));
|
|
self::assertSame([
|
|
'leadership' => 100,
|
|
'strength' => 15,
|
|
'intel' => 100,
|
|
], CentennialAllStarGrowthService::calculateUserCurrentTargetStats(
|
|
$target,
|
|
['startyear' => 180, 'year' => 195, 'month' => 1]
|
|
));
|
|
}
|
|
|
|
public function testInitialUserGrantMakesInitialAllocationReplaceable(): void
|
|
{
|
|
$initial = CentennialAllStarGrowthService::calculateUserInitialStats([
|
|
'uniqueName' => 'A1000001',
|
|
'leadership' => 80,
|
|
'strength' => 70,
|
|
'intel' => 50,
|
|
]);
|
|
$aux = CentennialAllStarGrowthService::initialAux([
|
|
'uniqueName' => 'A1000001',
|
|
], $initial);
|
|
|
|
self::assertSame('A1000001', $aux['targetId']);
|
|
self::assertSame($initial, $aux['userInitialStats']);
|
|
self::assertSame(1.0, $aux['dexTargetRatio']);
|
|
self::assertSame(50, $aux['granted']['leadership']);
|
|
self::assertSame(43, $aux['granted']['strength']);
|
|
self::assertSame(27, $aux['granted']['intel']);
|
|
}
|
|
|
|
public function testLegacyInitialGrantKeepsOnlyGrowthBeyondCreationRange(): void
|
|
{
|
|
self::assertSame(
|
|
35,
|
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(50, 0)
|
|
);
|
|
self::assertSame(
|
|
75,
|
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(90, 40)
|
|
);
|
|
self::assertSame(
|
|
105,
|
|
CentennialAllStarGrowthService::calculateLegacyUserGrant(140, 40)
|
|
);
|
|
self::assertSame(
|
|
35,
|
|
140 - CentennialAllStarGrowthService::calculateLegacyUserGrant(140, 40)
|
|
);
|
|
}
|
|
|
|
public function testReselectDropsOldGrantAndKeepsOrganicGrowth(): void
|
|
{
|
|
self::assertSame(
|
|
['value' => 82, 'granted' => 7, 'organic' => 75],
|
|
CentennialAllStarGrowth::replaceTarget(86, 11, 82)
|
|
);
|
|
self::assertSame(
|
|
['value' => 90, 'granted' => 0, 'organic' => 90],
|
|
CentennialAllStarGrowth::replaceTarget(101, 11, 82)
|
|
);
|
|
}
|
|
|
|
public function testRepeatedAdvanceIsIdempotent(): void
|
|
{
|
|
$first = CentennialAllStarGrowth::advance(50, 0, 60);
|
|
$second = CentennialAllStarGrowth::advance($first['value'], $first['granted'], 60);
|
|
self::assertSame(60, $second['value']);
|
|
self::assertSame(10, $second['granted']);
|
|
self::assertSame(0, $second['delta']);
|
|
self::assertSame(50, CentennialAllStarGrowth::recordableValue(60, 10));
|
|
}
|
|
|
|
public function testDexUsesSlowEarlyCurve(): void
|
|
{
|
|
self::assertSame(0, CentennialAllStarGrowth::dexFloor(900000, 0));
|
|
self::assertSame(36000, CentennialAllStarGrowth::dexFloor(900000, 0.2));
|
|
self::assertSame(144000, CentennialAllStarGrowth::dexFloor(900000, 0.4));
|
|
self::assertSame(900000, CentennialAllStarGrowth::dexFloor(900000, 1));
|
|
}
|
|
|
|
public function testNpcDexStopsAtFortyPercentOfHistoricalTarget(): void
|
|
{
|
|
$target = 900000;
|
|
$ratio = GameConst::$centennialNpcDexTargetRatio;
|
|
|
|
self::assertSame(0.4, $ratio);
|
|
self::assertSame(
|
|
57600,
|
|
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
|
$target,
|
|
['startyear' => 180, 'year' => 186, 'month' => 1],
|
|
$ratio
|
|
)
|
|
);
|
|
self::assertSame(
|
|
360000,
|
|
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
|
$target,
|
|
['startyear' => 180, 'year' => 195, 'month' => 1],
|
|
$ratio
|
|
)
|
|
);
|
|
self::assertSame(
|
|
360000,
|
|
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
|
$target,
|
|
['startyear' => 180, 'year' => 210, 'month' => 1],
|
|
$ratio
|
|
)
|
|
);
|
|
}
|
|
|
|
public function testScenarioConfigKeepsNpcDexTargetRatioAtFortyPercent(): void
|
|
{
|
|
$scenario = json_decode(
|
|
file_get_contents(__DIR__ . '/../hwe/scenario/scenario_915.json'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
|
|
self::assertSame(
|
|
GameConst::$centennialNpcDexTargetRatio,
|
|
$scenario['map']['centennialNpcDexTargetRatio']
|
|
);
|
|
}
|
|
|
|
public function testNpcDexRatioChangeRemovesOnlyOldEventGrant(): void
|
|
{
|
|
self::assertSame(
|
|
['value' => 360000, 'granted' => 360000, 'organic' => 0],
|
|
CentennialAllStarGrowth::replaceTarget(810000, 810000, 360000)
|
|
);
|
|
self::assertSame(
|
|
['value' => 360000, 'granted' => 310000, 'organic' => 50000],
|
|
CentennialAllStarGrowth::replaceTarget(860000, 810000, 360000)
|
|
);
|
|
self::assertSame(
|
|
['value' => 400000, 'granted' => 0, 'organic' => 400000],
|
|
CentennialAllStarGrowth::replaceTarget(1210000, 810000, 360000)
|
|
);
|
|
}
|
|
|
|
public function testExistingNpcDexGrantIsRebasedWithoutChangingFinalStats(): void
|
|
{
|
|
$vars = [
|
|
'leadership' => 91,
|
|
'strength' => 91,
|
|
'intel' => 91,
|
|
'dex1' => 810000,
|
|
'dex2' => 810000,
|
|
'dex3' => 810000,
|
|
'dex4' => 810000,
|
|
'dex5' => 810000,
|
|
'special' => 'None',
|
|
];
|
|
$aux = [
|
|
'targetId' => 'A1000001',
|
|
'granted' => [
|
|
'leadership' => 76,
|
|
'strength' => 76,
|
|
'intel' => 76,
|
|
'dex1' => 810000,
|
|
'dex2' => 810000,
|
|
'dex3' => 810000,
|
|
'dex4' => 810000,
|
|
'dex5' => 810000,
|
|
],
|
|
'progressMonth' => 162,
|
|
'milestone' => 4,
|
|
'naturalSpecialDomestic' => null,
|
|
'eventSpecialDomestic' => null,
|
|
'userInitialStats' => null,
|
|
];
|
|
$general = $this->getMockBuilder(General::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods(['getAuxVar', 'setAuxVar', 'getVar', 'updateVar'])
|
|
->getMock();
|
|
$general->method('getAuxVar')->willReturnCallback(
|
|
static fn(string $key) => $key === CentennialAllStarGrowthService::AUX_KEY
|
|
? $aux
|
|
: null
|
|
);
|
|
$general->method('getVar')->willReturnCallback(
|
|
static fn(string $key) => $vars[$key] ?? null
|
|
);
|
|
$general->method('updateVar')->willReturnCallback(
|
|
static function (string $key, $value) use (&$vars): void {
|
|
$vars[$key] = $value;
|
|
}
|
|
);
|
|
$general->method('setAuxVar')->willReturnCallback(
|
|
static function (string $key, $value) use (&$aux): void {
|
|
if ($key === CentennialAllStarGrowthService::AUX_KEY) {
|
|
$aux = $value;
|
|
}
|
|
}
|
|
);
|
|
|
|
$result = CentennialAllStarGrowthService::applyTarget(
|
|
$general,
|
|
[
|
|
'uniqueName' => 'A1000001',
|
|
'leadership' => 100,
|
|
'strength' => 100,
|
|
'intel' => 100,
|
|
'dex' => [900000, 900000, 900000, 900000, 900000],
|
|
],
|
|
['startyear' => 180, 'year' => 195, 'month' => 1],
|
|
CentennialAllStarGrowthService::NPC_PROGRESS_MULTIPLIER,
|
|
GameConst::$centennialNpcDexTargetRatio
|
|
);
|
|
|
|
self::assertTrue($result['changed']);
|
|
self::assertSame(91, $vars['leadership']);
|
|
self::assertSame(91, $vars['strength']);
|
|
self::assertSame(91, $vars['intel']);
|
|
foreach (['dex1', 'dex2', 'dex3', 'dex4', 'dex5'] as $key) {
|
|
self::assertSame(360000, $vars[$key]);
|
|
self::assertSame(360000, $aux['granted'][$key]);
|
|
}
|
|
self::assertSame(0.4, $aux['dexTargetRatio']);
|
|
}
|
|
|
|
public function testUserDexStillReachesFullHistoricalTarget(): void
|
|
{
|
|
self::assertSame(
|
|
900000,
|
|
CentennialAllStarGrowthService::calculateDexTargetFloor(
|
|
900000,
|
|
['startyear' => 180, 'year' => 195, 'month' => 1]
|
|
)
|
|
);
|
|
}
|
|
|
|
public function testUntrackedGeneralKeepsFullHallValue(): void
|
|
{
|
|
self::assertSame(
|
|
123456,
|
|
CentennialAllStarGrowthService::recordableRawValue(
|
|
['dex1' => 123456, 'aux' => '{}'],
|
|
'dex1'
|
|
)
|
|
);
|
|
}
|
|
}
|