Skip to content

Commit

Permalink
Add simulator mode
Browse files Browse the repository at this point in the history
  • Loading branch information
gaius1304 committed Jun 26, 2022
1 parent 912c58f commit 86b98e6
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 77 deletions.
56 changes: 46 additions & 10 deletions BattleEngine.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ static void cleanup_units_attributes(struct units_attributes *units_attributes)
free(units_attributes);
}

static size_t calc_combatants_alloc_size(const struct units_attributes *restrict units_attributes,
uint32_t num_combatants) {
const uint8_t num_kinds = units_attributes->num_kinds;
return num_combatants * sizeof(struct combatant) + num_combatants * num_kinds * sizeof(uint64_t) +
num_combatants * MAX_ROUNDS * num_kinds * sizeof(struct unit_group_stats);
}

static struct combatant *load_combatants(FILE *restrict file, const struct units_attributes *restrict units_attributes,
uint32_t num_combatants) {
const uint8_t num_kinds = units_attributes->num_kinds;
Expand All @@ -174,8 +181,7 @@ static struct combatant *load_combatants(FILE *restrict file, const struct units
assert(num_combatants > 0 && num_combatants <= 2 * 256);
assert(num_kinds > 0);

size_t total_size = num_combatants * sizeof(struct combatant) + num_combatants * num_kinds * sizeof(uint64_t) +
num_combatants * MAX_ROUNDS * num_kinds * sizeof(struct unit_group_stats);
size_t total_size = calc_combatants_alloc_size(units_attributes, num_combatants);
struct combatant *combatants = calloc(total_size, 1);
if (combatants == NULL) {
fputs("Loading combatants failed, allocation of combatants failed\n", stderr);
Expand Down Expand Up @@ -485,9 +491,13 @@ static void dump_stats(FILE *restrict file, const struct combatant *restrict com
}
}

static int battle(uint32_t seed) {
static int simulate(uint32_t seed, uint32_t num_simulations) {
int n, ret = 1;

if (num_simulations == 0) {
return 0;
}

struct units_attributes *units_attributes = load_units_attributes(stdin);
if (units_attributes == NULL) {
goto out;
Expand Down Expand Up @@ -522,18 +532,37 @@ static int battle(uint32_t seed) {
goto out_units_attributes;
}

struct combatant *combatants_copy = NULL;
size_t combatants_size = 0;
if (num_simulations > 1) {
combatants_size = calc_combatants_alloc_size(units_attributes, num_combatants);
combatants_copy = malloc(combatants_size);
if (combatants_copy == NULL) {
goto out_combatants;
}
memcpy(combatants_copy, combatants, combatants_size);
}

struct combatant *attackers = combatants;
struct combatant *defenders = &combatants[num_attackers];

uint32_t num_rounds = 0;
fight(units_attributes, attackers, num_attackers, defenders, num_defenders, &num_rounds, &seed);
for (uint32_t n = 0; n < num_simulations; n++) {
if (n != 0) {
memcpy(combatants, combatants_copy, combatants_size);
}

uint32_t num_rounds = 0;
fight(units_attributes, attackers, num_attackers, defenders, num_defenders, &num_rounds, &seed);

printf("%" PRIu32 "\n\n", num_rounds);
printf("%" PRIu32 "\n\n", num_rounds);

dump_stats(stdout, combatants, num_attackers + num_defenders, num_rounds, units_attributes->num_kinds);
dump_stats(stdout, combatants, num_attackers + num_defenders, num_rounds, units_attributes->num_kinds);
}

ret = 0;

free(combatants_copy);
out_combatants:
free(combatants);
out_units_attributes:
cleanup_units_attributes(units_attributes);
Expand All @@ -544,8 +573,8 @@ static int battle(uint32_t seed) {
int main(int argc, char *argv[]) {
int n;

if (argc != 2) {
fprintf(stderr, "Usage: %s [SEED]\n", argv[0]);
if (argc != 3) {
fprintf(stderr, "Usage: %s <SEED> <NUM_SIMULATIONS>\n", argv[0]);
return 1;
}

Expand All @@ -561,5 +590,12 @@ int main(int argc, char *argv[]) {
return 1;
}

return battle(seed);
uint32_t num_simulations;
n = sscanf(argv[2], "%" SCNu32, &num_simulations);
if (n != 1) {
fputs("Scanning num_simulations failed\n", stderr);
return 1;
}

return simulate(seed, num_simulations);
}
117 changes: 69 additions & 48 deletions BattleEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@

class UnitAttributes
{
private $weapons;
private $shield;
private $armor;
private $rapidFire;
private float $weapons;
private float $shield;
private float $armor;
private array $rapidFire;

public function __construct(
float $weapons,
float $shield,
float $armor,
array $rapidFire
) {
)
{
if ($weapons <= 0.0) {
throw new InvalidArgumentException('weapons must be greater than 0');
}
Expand Down Expand Up @@ -80,17 +81,18 @@ public function getRapidFire(): array

class Combatant
{
private $weaponsTechnology;
private $shieldingTechnology;
private $armorTechnology;
private $unitGroups;
private int $weaponsTechnology;
private int $shieldingTechnology;
private int $armorTechnology;
private array $unitGroups;

public function __construct(
int $weaponsTechnology,
int $shieldingTechnology,
int $armorTechnology,
int $weaponsTechnology,
int $shieldingTechnology,
int $armorTechnology,
array $unitGroups
) {
)
{
if ($weaponsTechnology < 0 || $weaponsTechnology > 255) {
throw new InvalidArgumentException('weaponsTechnology must be between 0 and 255');
}
Expand Down Expand Up @@ -134,13 +136,13 @@ public function getUnitGroups(): array

class UnitGroupStats
{
private $timesFired;
private $timesWasShot;
private $shieldDamageDealt;
private $hullDamageDealt;
private $shieldDamageTaken;
private $hullDamageTaken;
private $numRemainingUnits;
private int $timesFired;
private int $timesWasShot;
private int $shieldDamageDealt;
private int $hullDamageDealt;
private int $shieldDamageTaken;
private int $hullDamageTaken;
private int $numRemainingUnits;

public function __construct(
int $timesFired,
Expand All @@ -150,7 +152,8 @@ public function __construct(
int $shieldDamageTaken,
int $hullDamageTaken,
int $numRemainingUnits
) {
)
{
$this->timesFired = $timesFired;
$this->timesWasShot = $timesWasShot;
$this->shieldDamageDealt = $shieldDamageDealt;
Expand Down Expand Up @@ -198,7 +201,7 @@ public function getNumRemainingUnits(): int

class CombatantOutcome
{
private $roundsStats;
private array $roundsStats;

public function __construct(array $roundsStats)
{
Expand All @@ -218,15 +221,16 @@ public function getRoundsStats(): array

class BattleOutcome
{
private $numRounds;
private $attackersOutcomes;
private $defendersOutcomes;
private int $numRounds;
private array $attackersOutcomes;
private array $defendersOutcomes;

public function __construct(
int $numRounds,
int $numRounds,
array $attackersOutcomes,
array $defendersOutcomes
) {
)
{
$this->numRounds = $numRounds;
$this->attackersOutcomes = $attackersOutcomes;
$this->defendersOutcomes = $defendersOutcomes;
Expand Down Expand Up @@ -254,8 +258,8 @@ class Error extends Exception

class BattleEngine
{
private $enginePath;
private $unitsAttributes;
private string $enginePath;
private array $unitsAttributes;

public function __construct(string $enginePath, array $unitsAttributes)
{
Expand Down Expand Up @@ -354,7 +358,8 @@ private function makeStdinForCombatant(Combatant $combatant): string
private function makeStdinForCombatants(
array $attackers,
array $defenders
): string {
): string
{
$stdin = [];
$stdin[] = sprintf('%d %d', count($attackers), count($defenders));
$stdin[] = '';
Expand All @@ -370,9 +375,10 @@ private function makeStdinForCombatants(
}

private function parseCombatantOutcome(
int $numRounds,
int $numRounds,
array $data
): CombatantOutcome {
): CombatantOutcome
{
$numKinds = count($this->unitsAttributes);
$index = 0;
$roundsStats = [];
Expand Down Expand Up @@ -404,7 +410,12 @@ private function parseCombatantOutcome(
return new CombatantOutcome($roundsStats);
}

public function battle(array $attackers, array $defenders, int $seed = 0)
public function simulate(
array $attackers,
array $defenders,
int $seed = 0,
int $numSimulations = 1
): array
{
$this->assertValidCombatants('attackers', $attackers);
$this->assertValidCombatants('defenders', $defenders);
Expand All @@ -417,7 +428,7 @@ public function battle(array $attackers, array $defenders, int $seed = 0)
$seed = rand(1, 1000000000);
}

$cmd = $this->enginePath . ' ' . $seed;
$cmd = $this->enginePath . ' ' . $seed . ' ' . $numSimulations;
$descriptorspec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
Expand Down Expand Up @@ -457,22 +468,32 @@ public function battle(array $attackers, array $defenders, int $seed = 0)
$numDefenders = count($defenders);

$result = array_map('intval', preg_split('/\s+/', $stdout));
$numRounds = array_shift($result);

$outcomeSize = $numRounds * $numKinds * 7;
$outcomes = [];
for ($i = 0; $i < $numAttackers + $numDefenders; $i++) {
$d = array_slice($result, $i * $outcomeSize, $outcomeSize);
$outcomes[] = $this->parseCombatantOutcome($numRounds, $d);
}
$simulations = [];

$attackersOutcomes = array_slice($outcomes, 0, $numAttackers);
$defendersOutcomes = array_slice($outcomes, $numAttackers);
$idx = 0;
for ($i = 0; $i < $numSimulations; $i++) {
$numRounds = $result[$idx];
$idx++;

return new BattleOutcome(
$numRounds,
$attackersOutcomes,
$defendersOutcomes
);
$outcomeSize = $numRounds * $numKinds * 7;
$outcomes = [];
for ($j = 0; $j < $numAttackers + $numDefenders; $j++) {
$d = array_slice($result, $idx, $outcomeSize);
$idx += $outcomeSize;
$outcomes[] = $this->parseCombatantOutcome($numRounds, $d);
}

$attackersOutcomes = array_slice($outcomes, 0, $numAttackers);
$defendersOutcomes = array_slice($outcomes, $numAttackers);

$simulations[] = new BattleOutcome(
$numRounds,
$attackersOutcomes,
$defendersOutcomes
);
}

return $simulations;
}
}
35 changes: 22 additions & 13 deletions BattleEngine.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ def parse_combatant_outcome(self, num_rounds: int, data: List[int]) -> Combatant
rounds_stats.append(round_stats)
return CombatantOutcome(rounds_stats)

def battle(self, attackers: List[Combatant], defenders: List[Combatant], seed: int = 0,
timeout=None) -> BattleOutcome:
def simulate(self, attackers: List[Combatant], defenders: List[Combatant], seed: int = 0,
num_simulations: int = 1, timeout=None) -> List[BattleOutcome]:
self._assert_valid_combatants('attackers', attackers)
self._assert_valid_combatants('defenders', defenders)

Expand All @@ -204,7 +204,7 @@ def battle(self, attackers: List[Combatant], defenders: List[Combatant], seed: i
combatants_stdin = self._make_stdin_for_combatants(attackers, defenders)
stdin = attrs_stdin + '\n' + combatants_stdin

args = [self.engine_path, str(seed)]
args = [self.engine_path, str(seed), str(num_simulations)]
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

try:
Expand All @@ -223,16 +223,25 @@ def battle(self, attackers: List[Combatant], defenders: List[Combatant], seed: i

out = outs[0].decode('ascii')
result = list(map(int, out.split()))
num_rounds = result[0]
data = result[1:]

outcome_size = num_rounds * num_kinds * 7
outcomes = []
for i in range(num_attackers + num_defenders):
d = data[i * outcome_size:(i + 1) * outcome_size]
outcome = self.parse_combatant_outcome(num_rounds, d)
outcomes.append(outcome)
simulations = []

attackers_outcomes, defenders_outcomes = outcomes[:num_attackers], outcomes[num_attackers:]
idx = 0
for i in range(num_simulations):
num_rounds = result[idx]
idx += 1

return BattleOutcome(num_rounds, attackers_outcomes, defenders_outcomes)
outcome_size = num_rounds * num_kinds * 7
outcomes = []
for j in range(num_attackers + num_defenders):
d = result[idx:idx + outcome_size]
idx += outcome_size
outcome = self.parse_combatant_outcome(num_rounds, d)
outcomes.append(outcome)

attackers_outcomes, defenders_outcomes = outcomes[:num_attackers], outcomes[num_attackers:]

simulation = BattleOutcome(num_rounds, attackers_outcomes, defenders_outcomes)
simulations.append(simulation)

return simulations
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ $ cmake --build build --config Release

### Run examples
Make sure you specify the correct path to the obtained battle engine binary in the examples.
For instance, in _example.php_ you need to replace the path in:
For instance, in _example-battle.php_ you need to replace the path in:

```php
$battleEngine = new BattleEngine('./build/BattleEngine', OG::$unitsAttributes);
Expand All @@ -36,6 +36,6 @@ $battleEngine = new BattleEngine('./build/BattleEngine', OG::$unitsAttributes);
After that, you can run the examples:

```
$ php example.php
$ python example.py
$ php example-battle.php
$ python example-battle.py
```
Loading

0 comments on commit 86b98e6

Please sign in to comment.