patterncppMinor
Simulation for rolling dice in X-Wing
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
```
#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.
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:
One final thing: the github version has replaced all the includes with a
// 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 astd::vector(unknown size) orstd::array(fixed size) instead.
- Don't use abbreviations in names unless they're really common;
NAttackDicecould rather benumAttackDiceor justattackDice;nSimcould benumSimulationsor justsimulations;distcould bedieord8.
- Use
'\n'rather thanstd::endlunless you specifically need the flush from the latter (which is almost never).
AttackHitCounter = AttackHitCounter + 1;should be just++AttackHitCounter;(orAttackHitCounter += 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 justif (Crackshot)andif (CrackShotUsed == false)should be justif (!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;-mainwill 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.