HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Simulation for rolling dice in X-Wing

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
simulationrollingdiceforwing

Problem

I've recently started playing the X-Wing Miniature game, and there is quite some math behind some of the dice rolls, as they are affected by many abilities and so on. Manually calculating these is quite burdensome, so I decided to write a simple C++ program to approximate the expected damage by running many simulations.

```
#include "targetver.h"
#include
#include
#include
#include
#include
#include
#include
int main()
{
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution dist(1, 8);
const int nSim = 100000;

const int NAttackDice = 2;
const bool AttackFocus = true;
int AttackDiceResult [NAttackDice];
int HowlRunnerResult;
const bool Howlrunner = true;
const bool CrackShot = true;
bool CrackShotUsed = false;

int AttackHitCounter = 0;
int AttackFocusCounter = 0;
int CritCounter = 0;
int HowlRunnerHitCheck;
int HowlHitCounter = 0;
int HowlFocusCounter = 0;
int HowlCritCounter = 0;

const int NEvadeDice = 3;
const bool EvadeFocus = true;
int EvadeDiceResult [NEvadeDice];

int EvadeCounter = 0;
int DefenseFocusCounter = 0;

double Damage[nSim];
double DamageSum = 0;

std::cout = 1 && CrackShotHitCheck > 0)
{
EvadeCounter = EvadeCounter - 1;
CrackShotUsed = true;
}
if (CrackShot == true && CrackShotUsed == false && DefenseFocusCounter >= 1 && CrackShotHitCheck > 0)
{
DefenseFocusCounter = DefenseFocusCounter - 1;
CrackShotUsed = true;
}

Damage[n] = std::max(0, (AttackHitCounter + CritCounter + AttackFocusCounter + HowlHitCounter + HowlFocusCounter + HowlCritCounter) - EvadeCounter - DefenseFocusCounter);
DamageSum = DamageSum + Damage[n];

AttackHitCounter = 0;
AttackFocusCounter = 0;
CritCounter = 0;
HowlHitCounter = 0;
HowlFocusCounter = 0;
HowlCritCounter = 0;
EvadeCounter = 0;
DefenseFocusCounter = 0;
}
std::cout << "Number of simulations was " << nSim << std::endl;;
std::cout << "Average damage was " << DamageSum / nSim << std::endl;
return

Solution

This code would greatly benefit from being split into functions, the most important one for your logic being a function to run a single instance of the simulation.

// Runs one simulation of an X-Wing attack and returns the actual damage result.
int SimulateDamage(int attackDice, bool attackFocus,
                   int evadeDice, bool evadeFocus,
                   bool howlRunner, bool crackShot)
{
  ... // calculations
  int totalDamage = AttackHitCounter + CritCounter + AttackFocusCounter 
                  + HowlHitCounter + HowlFocusCounter + HowlCritCounter
                  - EvadeCounter - DefenseFocusCounter;
  return std::max(0, totalDamage);
}


This clearly defines what values and options can affect the result, and you can keep all the variables and calculations needed for doing one simulation local to this function. I would consider breaking this function down further, but just pulling out this one function will make things a lot neater and clearer.

Other things in the code that aren't so serious and reflect my personal style preferences:

  • Don't use C-style arrays like int AttackDiceResult[NAttackDice] - use a std::vector (unknown size) or std::array (fixed size) instead.



  • Don't use abbreviations in names unless they're really common; NAttackDice could rather be numAttackDice or just attackDice; nSim could be numSimulations or just simulations; dist could be die or d8.



  • Use '\n' rather than std::endl unless you specifically need the flush from the latter (which is almost never).



  • AttackHitCounter = AttackHitCounter + 1; should be just ++AttackHitCounter; (or AttackHitCounter += N; for values greater than 1). This is idiomatic C++ and makes it obvious you're actually modifying a variable.



  • if (CrackShot == true) should be just if (Crackshot) and if (CrackShotUsed == false) should be just if (!CrackShotUsed) - again idiomatic C++, and well-chosen variable names will actually make these read more like natural language sentences and so easier for a reader to reason about (reading ! as "not").



  • No need for a return 0; - main will automatically return a 0 result on reaching the end.



One final thing: the github version has replaced all the includes with a #include "stdafx.h" - that's a bad use of Visual C++ pre-compiled headers. Since pre-compiled headers are non-standard, I'd recommend just not using them, but if you have to, the pre-compiled header should be duplicating your (common) includes, not replacing them.

Code Snippets

// Runs one simulation of an X-Wing attack and returns the actual damage result.
int SimulateDamage(int attackDice, bool attackFocus,
                   int evadeDice, bool evadeFocus,
                   bool howlRunner, bool crackShot)
{
  ... // calculations
  int totalDamage = AttackHitCounter + CritCounter + AttackFocusCounter 
                  + HowlHitCounter + HowlFocusCounter + HowlCritCounter
                  - EvadeCounter - DefenseFocusCounter;
  return std::max(0, totalDamage);
}

Context

StackExchange Code Review Q#118527, answer score: 4

Revisions (0)

No revisions yet.