This is a simulation tool for moves in the board game risk. Its approach is to run a simulation as many times as specified and then calculate probabilities by counting results of those simulations. It offers generic procedures to facilitate running simulations.
The rules of a risk game are usually as follows:
However, this program allows modification of the rules. Some procedures allow specifying non-standard rules, which they give to other procedures in the process of running the simulations, so that the whole calculation is based on those modified rules.
The program makes use of a random number generator (RNG), which outputs uniformly distributed random numbers. It is possible to roll dice with more than 6 sides, but currently this is not used by any simulation procedure.
You can create a random number generator with an optionally given seed as follows:
(make-random-integer-generator #:seed 12345)
Setting a seed will make results reproducible.
You can create game rules as follows:
(make-risk-rules att-dice def-dice eq-eyes-win)
Where eq-eyes-win
is a symbol, one of the following:
There is a procedure named simulate-round
, which rolls dice for a single round of risk. Here is an example for running the simulation once using default risk game rules:
(simulate-round
(make-init-fight-situation 4 3)
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*)
You can run this simulation an arbitrary number of times, collecting a result table, which counts the outcomes as follows:
(display-summary
(try-n-times (make-init-fight-situation 4 3)
10000
simulate-round
(make-random-integer-generator)
#:rules *default-risk-rules*)
#:fraction-formatter (λ (frac)
(format-fraction frac #:precision 3)))
The procedure try-n-times
will run the simulation 10000
times and the results will be counted in a resulting table. That table is summarized and by display-summary
, which counts occurences of unique round results, calculates their probability and displays that.
Simulation of annihilation works the same way as simulating single rounds:
(simulate-fight
(make-init-fight-situation 10 7)
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*)
As does simulation of multiple annihilations:
(display-summary
(try-n-times (make-init-fight-situation 10 7)
10000
simulate-fight
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*))
Simulating battles can be done as follows:
(displayln
(format-fraction
(calc-att-winning-prob
(let ([rand-int-gen (make-random-integer-generator #:seed 12345)])
(try-n-times-general
(lambda ()
(simulate-battle 8
'(3 2 1)
rand-int-gen
#:rules *default-risk-rules*))
100000)))
#:precision 5))
There is also another procedure, which results in a battle win probability instead of a large table of all occurring courses of the battle and internally repeats fights a given number of times, instead of repeating the battle a given number of times:
(displayln
"Probability of winning with 9 against 1, 1, 10:"
(format-fraction
(calc-battle-att-win-prob 9
'(1 1 10)
10000
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*)
#:precision 5))
If you want to make assumptions about outcomes of fights of a battle, you can use another procedure as follows:
(displayln
(format-fraction
(calc-consecutive-att-win-prob '(9 8 7)
'(1 1 10)
10000
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*)
#:precision 5))
Given a result table containing fight results, you can quickly calculate the winning probability for attacker or defender as follows:
(displayln
(format-fraction
(calc-att-winning-prob
(try-n-times (make-init-fight-situation 10 7)
10000
simulate-fight
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*))
#:precision 5))
There is a convenience procedure for calculating defender winning probability as well:
(displayln
(format-fraction
(calc-def-win-prob
(try-n-times (make-init-fight-situation 10 7)
10000
simulate-fight
(make-random-integer-generator #:seed 12345)
#:rules *default-risk-rules*))
#:precision 5))
calc-battle-att-win-prob
is strange in the sense, that it internally performs n
simulations, instead of being wrapped like the other procedures with try-n-times
. It should be refactored to simulate the battle only once and then be simulated n
times via the try-n-times
procedure. – Or should it not?annihilate
-> simulate-fight
try-single-round
-> simulate-round
att-lost?
take rules as an argument as welldef-lost?
take rules as an argument as welldisplay-summary
should take a formatter procedure for formatting the fractions, instead of a precision and assuming, that the user wants to see decimal numbers. Giving a formatter procedure makes no assumptions about what the user wants.try-n-times
try-n-times-general
instead of try-n-times
try-n-times
try-n-times-general
-> try-n-times