142 lines
5.8 KiB
Solidity
142 lines
5.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.13;
|
|
|
|
import {Test, console} from "forge-std/Test.sol";
|
|
import {Army, Raider} from "../src/RaidGeldStructs.sol";
|
|
import "../src/RaidGeldUtils.sol";
|
|
import "@openzeppelin/contracts/utils/math/Math.sol";
|
|
|
|
contract raid_geldTest is Test {
|
|
function test_0_unit_price() public pure {
|
|
// buying 1 unit of moloch_denier
|
|
uint256 basePriceMolochDenier = RaidGeldUtils.calculateUnitPrice(0, 0, 1);
|
|
assertEq(basePriceMolochDenier, RaidGeldUtils.MOLOCH_DENIER_BASE_COST);
|
|
|
|
// buying 3 units
|
|
// has to be a bit more than 3 * 38 = 114
|
|
uint256 price = RaidGeldUtils.calculateUnitPrice(0, 0, 3);
|
|
assertGt(price, 3 * basePriceMolochDenier);
|
|
|
|
uint256 basePriceChamp = RaidGeldUtils.calculateUnitPrice(0, 0, 3);
|
|
// buying 3 units of champions
|
|
// has to be a bit more than 4 * 38 * 3 = 114
|
|
price = RaidGeldUtils.calculateUnitPrice(3, 0, 3);
|
|
assertGt(price, 3 * basePriceChamp);
|
|
|
|
// buying 12 units of champions when u already have 10
|
|
price = RaidGeldUtils.calculateUnitPrice(3, 10, 12);
|
|
// price should scale up
|
|
assertGt(price, basePriceChamp * 12);
|
|
}
|
|
|
|
function test_1_profits_per_second(uint16 _dLvl, uint16 _apLvl, uint16 _anLvl, uint16 _cLvl) public pure {
|
|
Army memory army = Army({
|
|
moloch_denier: Raider({level: 1}),
|
|
apprentice: Raider({level: 0}),
|
|
anointed: Raider({level: 0}),
|
|
champion: Raider({level: 0}),
|
|
profit_per_second: 0 // irrelevant for this test
|
|
});
|
|
uint256 profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
|
assertEq(profits_per_second, RaidGeldUtils.MOLOCH_DENIER_PROFIT);
|
|
|
|
army = Army({
|
|
moloch_denier: Raider({level: _dLvl}),
|
|
apprentice: Raider({level: _apLvl}),
|
|
anointed: Raider({level: _anLvl}),
|
|
champion: Raider({level: _cLvl}),
|
|
profit_per_second: 0 // irrelevant for this test
|
|
});
|
|
profits_per_second = RaidGeldUtils.calculateProfitsPerSecond(army);
|
|
uint256 expected = _dLvl * RaidGeldUtils.MOLOCH_DENIER_PROFIT + _apLvl * RaidGeldUtils.APPRENTICE_PROFIT
|
|
+ _anLvl * RaidGeldUtils.ANOINTED_PROFIT + _cLvl * RaidGeldUtils.CHAMPION_PROFIT;
|
|
assertEq(profits_per_second, expected);
|
|
}
|
|
|
|
function getConfidenceInterval(uint8 bossLevel)
|
|
internal
|
|
pure
|
|
returns (uint256 lower, uint256 upper)
|
|
{
|
|
/*
|
|
man i struggled getting this math with just integers so its
|
|
easier to just do it like this (precalculated):
|
|
z = 1.96 (95%)
|
|
Probability | Expected | Variance | Margin | Lower Bound | Upper Bound
|
|
99% 990 0.00099 62 928 1052
|
|
89% 890 0.000979 61 829 951
|
|
80% 800 0.00016 49 751 849
|
|
70% 700 0.00021 45 655 745
|
|
62% 620 0.0002356 48 572 668
|
|
51% 510 0.0002499 49 461 559
|
|
40% 400 0.00024 48 352 448
|
|
*/
|
|
if (bossLevel == 0) {
|
|
return (928, 1000);
|
|
} else if (bossLevel == 1) {
|
|
return (829, 951);
|
|
} else if (bossLevel == 2) {
|
|
return (751, 849);
|
|
} else if (bossLevel == 3) {
|
|
return (655, 745);
|
|
} else if (bossLevel == 4) {
|
|
return (572, 668);
|
|
} else if (bossLevel == 5) {
|
|
return (461, 559);
|
|
} else if (bossLevel == 6) {
|
|
return (352, 448);
|
|
}
|
|
}
|
|
|
|
function test_21_random(uint256 seed, uint256 min, uint256 max) public pure {
|
|
vm.assume(min < max);
|
|
vm.assume(max < type(uint256).max);
|
|
uint256 random = RaidGeldUtils.random(seed, min, max);
|
|
vm.assertTrue(random >= min && random <= max, "random() fell outside its range");
|
|
}
|
|
|
|
function test_22_random_range(uint256 min, uint256 max) public {
|
|
vm.assume(max > min);
|
|
vm.assume(max - min < 100);
|
|
vm.assume(max < type(uint256).max);
|
|
uint256 range = max - min + 1;
|
|
bool[] memory seen = new bool[](range);
|
|
uint256 count = 0;
|
|
for (uint256 i = 0; i < 100000; i++) {
|
|
uint256 result = RaidGeldUtils.random(block.prevrandao + i, min, max);
|
|
uint256 index = result - min;
|
|
if (!seen[index]) {
|
|
seen[index] = true;
|
|
count++;
|
|
}
|
|
if (count == range) break;
|
|
}
|
|
assertEq(count, range, "Not all values in the interval were generated");
|
|
}
|
|
|
|
function test_3_calculateBossFight_probabilities() public {
|
|
uint256[7] memory probabilities =
|
|
[uint256(99), uint256(89), uint256(80), uint256(70), uint256(62), uint256(51), uint256(40)];
|
|
uint256 totalRuns = 1000;
|
|
console.log("Checking boss rolls (1000 times)");
|
|
for (uint8 bossLevel = 0; bossLevel < 7; bossLevel++) {
|
|
console.log("Boss level: ", bossLevel);
|
|
uint256 bossPower = RaidGeldUtils.getBossPower(bossLevel);
|
|
uint256 geldBurnt = bossPower;
|
|
uint256 successCount = 0;
|
|
for (uint256 testRun = 0; testRun < totalRuns; testRun++) {
|
|
uint256 prevrandao = uint256(keccak256(abi.encodePacked(block.prevrandao, testRun * 1e9))); // Unique seed each time
|
|
if (RaidGeldUtils.calculateBossFight(bossLevel, geldBurnt, prevrandao)) {
|
|
successCount++;
|
|
}
|
|
}
|
|
(uint256 lowerProb, uint256 upperProb) = getConfidenceInterval(bossLevel);
|
|
console.log("expected at least wins", lowerProb);
|
|
console.log("expected at most wins", upperProb);
|
|
console.log("actual wins", successCount);
|
|
console.log("----------");
|
|
vm.assertTrue(successCount >= lowerProb && successCount <= upperProb, "Success rate not within expected range");
|
|
}
|
|
}
|
|
}
|