Skip to content

Instantly share code, notes, and snippets.

@carbontwelve
Created August 25, 2022 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carbontwelve/26f68755dcb035efc8990d3650ede15e to your computer and use it in GitHub Desktop.
Save carbontwelve/26f68755dcb035efc8990d3650ede15e to your computer and use it in GitHub Desktop.
<?php
/*
* PHP Space Mines Simulation
* Based upon Space Mines from Usborne Computer Space Games
* @see https://photogabble.co.uk/noteworthy/php-space-mines-introduction/
*/
function lerp($a, $b, $t): float
{
return $a + ($b - $a) * $t;
}
function minMax($value, $min = 0, $max = 1)
{
if ($value < $min) return $min;
if ($value > $max) return $max;
return $value;
}
enum BuildingKind: string
{
case Mine = 'Mine';
}
enum ResourceKind: string
{
case Ore = 'Ore';
case Food = 'Food';
}
interface ColonyModifier
{
public function modify(Colony $colony);
}
interface HasBuildings
{
public function buildings(): array;
public function buildingsOfKind(BuildingKind $kind): array;
}
abstract class Building implements ColonyModifier
{
public BuildingKind $kind;
public function __construct(BuildingKind $kind)
{
$this->kind = $kind;
}
}
class Mine extends Building
{
public function __construct()
{
parent::__construct(BuildingKind::Mine);
}
public function modify(Colony $colony)
{
$oreProduction = 60 + (60 * $this->productionPopulationModifier(count($colony->buildingsOfKind($this->kind)), $colony->population));
$oreProduction += ($oreProduction * $this->productionSatisfactionModifier($colony->happiness));
$colony->ore += ceil($oreProduction);
}
private function productionPopulationModifier(int $mines, int $population): float
{
$skeletonCrew = 20 * $mines;
$workLoadPercentage = $population > 0
? minMax($population / $skeletonCrew)
: 0;
return round(lerp(-1, 1, $workLoadPercentage), 2);
}
private function productionSatisfactionModifier(float $satisfaction): float
{
if ($satisfaction < 0) return -0.5;
if ($satisfaction > 0 && $satisfaction < 10) return 0.15;
return 0.25;
}
}
class Colony implements HasBuildings
{
public int $ore;
public int $oreSold;
public int $meals;
public int $population;
public float $happiness;
public int $money;
public int $turn;
private array $buildings;
public function __construct(int $ore = 0, int $meals = 1500, int $population = 35, float $happiness = 0.0, int $money = 0, int $turn = 1, array $buildings = null)
{
$this->ore = $ore;
$this->meals = $meals;
$this->population = $population;
$this->happiness = $happiness;
$this->money = $money;
$this->turn = $turn;
$this->oreSold = 0;
if (is_null($buildings)) {
$this->buildings = [
new Mine(),
new Mine(),
new Mine(),
];
} else {
$this->buildings = $buildings;
}
}
public function buildings(): array
{
return $this->buildings;
}
public function buildingsOfKind(BuildingKind $kind): array
{
return array_filter($this->buildings, function (Building $item) use ($kind) {
return $item->kind === $kind;
});
}
public function score(): int
{
$mod = $this->happiness < 0
? -0.2
: 0.2;
$score = (($this->population * 1000) + ($this->oreSold / 2) + ($this->money * 2));
return floor($score + ($score - ($score * $mod)));
}
public function status(): void
{
echo "TURN $this->turn" . PHP_EOL;
echo "There are $this->population people in the colony" . PHP_EOL;
echo "You have " . count($this->buildingsOfKind(BuildingKind::Mine)) . " mines and $this->money money" . PHP_EOL;
echo "Ore in store: $this->ore, total ore sold $this->oreSold" . PHP_EOL;
echo "Meals in store: $this->meals" . PHP_EOL;
echo "Satisfaction: $this->happiness" . PHP_EOL;
}
}
class SatisfactionModifier implements ColonyModifier
{
public function modify(Colony $colony)
{
$colony->happiness += (
$this->foodModifier($colony->meals, $colony->population) +
$this->mineModifier(count($colony->buildingsOfKind(BuildingKind::Mine)), $colony->population)
);
$colony->happiness = minMax($colony->happiness, -20, 20);
}
private function foodModifier(int $foodReserve, int $population): float
{
$days = 28; // 4 weeks
$mealsPerDay = 3;
$mealsRequired = $population * $days * $mealsPerDay;
$foodReservePercentage = $foodReserve > 0
? minMax($foodReserve / $mealsRequired)
: 0;
return round(lerp(-1.5, 1.5, $foodReservePercentage), 2);
}
private function mineModifier(int $mines, int $population): float
{
// min of 10 people per mine until they get over-worked, 20 is the optimal
$skeletonCrew = 20 * $mines;
$workLoadPercentage = $population > 0
? minMax($population / $skeletonCrew)
: 0;
return round(lerp(-1.5, 1.5, $workLoadPercentage), 2);
}
}
class PopulationModifier implements ColonyModifier
{
public function modify(Colony $colony)
{
// Migration
// TODO this should be between 1 and the value found based upon happiness
$colony->population += (int)($colony->happiness / 2);
if ($colony->population < 0) $colony->population = 0;
// Food consumption
$colony->meals -= $colony->population * 3;
if ($colony->meals < 0) $colony->meals = 0;
}
}
class GatherResources implements ColonyModifier
{
public function modify(Colony $colony)
{
/** @var Mine $mine */
foreach ($colony->buildingsOfKind(BuildingKind::Mine) as $mine) {
$mine->modify($colony);
}
}
}
class SellOre implements ColonyModifier {
private int $amount;
private int $value;
public function __construct(int $amount, int $value) {
$this->amount = $amount;
$this->value = $value;
}
public function modify(Colony $colony)
{
$colony->oreSold += $this->amount;
$colony->ore -= $this->amount;
$colony->money += $this->amount * $this->value;
}
}
$player = new Colony();
for ($i = 0; $i < 30; $i++) {
$player->status();
(new SatisfactionModifier)->modify($player);
(new PopulationModifier())->modify($player);
(new GatherResources())->modify($player);
(new SellOre($player->ore, rand(7,12)))->modify($player);
if ($player->happiness == -20) break;
$player->turn++;
}
echo "Player Score: " . $player->score() . PHP_EOL;
@carbontwelve
Copy link
Author

Default output is:

TURN 1
There are 35 people in the colony
You have 3 mines and 0 money
Ore in store: 0, total ore sold 0
Meals in store: 1500
Satisfaction: 0
TURN 2
There are 35 people in the colony
You have 3 mines and 2187 money
Ore in store: 0, total ore sold 243
Meals in store: 1395
Satisfaction: 0.28
TURN 3
There are 35 people in the colony
You have 3 mines and 3888 money
Ore in store: 0, total ore sold 486
Meals in store: 1290
Satisfaction: 0.45
TURN 4
There are 35 people in the colony
You have 3 mines and 6318 money
Ore in store: 0, total ore sold 729
Meals in store: 1185
Satisfaction: 0.52
TURN 5
There are 35 people in the colony
You have 3 mines and 8262 money
Ore in store: 0, total ore sold 972
Meals in store: 1080
Satisfaction: 0.48
TURN 6
There are 35 people in the colony
You have 3 mines and 10449 money
Ore in store: 0, total ore sold 1215
Meals in store: 975
Satisfaction: 0.33
TURN 7
There are 35 people in the colony
You have 3 mines and 12636 money
Ore in store: 0, total ore sold 1458
Meals in store: 870
Satisfaction: 0.07
TURN 8
There are 35 people in the colony
You have 3 mines and 13932 money
Ore in store: 0, total ore sold 1566
Meals in store: 765
Satisfaction: -0.29
TURN 9
There are 35 people in the colony
You have 3 mines and 15228 money
Ore in store: 0, total ore sold 1674
Meals in store: 660
Satisfaction: -0.76
TURN 10
There are 35 people in the colony
You have 3 mines and 16308 money
Ore in store: 0, total ore sold 1782
Meals in store: 555
Satisfaction: -1.34
TURN 11
There are 34 people in the colony
You have 3 mines and 17430 money
Ore in store: 0, total ore sold 1884
Meals in store: 453
Satisfaction: -2.02
TURN 12
There are 33 people in the colony
You have 3 mines and 18420 money
Ore in store: 0, total ore sold 1983
Meals in store: 354
Satisfaction: -2.84
TURN 13
There are 32 people in the colony
You have 3 mines and 19113 money
Ore in store: 0, total ore sold 2082
Meals in store: 258
Satisfaction: -3.81
TURN 14
There are 30 people in the colony
You have 3 mines and 20013 money
Ore in store: 0, total ore sold 2172
Meals in store: 168
Satisfaction: -4.92
TURN 15
There are 27 people in the colony
You have 3 mines and 20661 money
Ore in store: 0, total ore sold 2253
Meals in store: 87
Satisfaction: -6.22
TURN 16
There are 24 people in the colony
You have 3 mines and 21165 money
Ore in store: 0, total ore sold 2325
Meals in store: 15
Satisfaction: -7.75
TURN 17
There are 20 people in the colony
You have 3 mines and 21732 money
Ore in store: 0, total ore sold 2388
Meals in store: 0
Satisfaction: -9.53
TURN 18
There are 15 people in the colony
You have 3 mines and 22137 money
Ore in store: 0, total ore sold 2433
Meals in store: 0
Satisfaction: -11.53
TURN 19
There are 9 people in the colony
You have 3 mines and 22434 money
Ore in store: 0, total ore sold 2460
Meals in store: 0
Satisfaction: -13.78
TURN 20
There are 1 people in the colony
You have 3 mines and 22458 money
Ore in store: 0, total ore sold 2463
Meals in store: 0
Satisfaction: -16.33
TURN 21
There are 0 people in the colony
You have 3 mines and 22458 money
Ore in store: 0, total ore sold 2463
Meals in store: 0
Satisfaction: -19.28
Player Score: 101524

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment