Source Code

## What is Unconventional Russian Roulette?

Unconventional Russian Roulette introduces similarities as its origin Russian Roulette. The difference is that instead of using one bullet, it uses three bullets where they are loaded adjacently.

Let’s imagine this scenario:

Three bullets are loaded into the cylinder adjacently, Player A has taken a shot and survived. We, as Player B, need to decide whether to take a shot or not. For the sake of simplicity, the game ends this round and here are the conditions:

• If we took a shot, and it was empty -> WE WON 1 MILLION DOLLARS
• If we chose to stay, and it was loaded -> WE WON 1 MILLION DOLLARS

Q: If the main goal here is to be alive and to take 1 MILLION DOLLARS back home. Should Player B take a shot or not? and Why?

Indeed, there is a way to increase our chances of going back home alive with money.

## The Winning Strategy

A cylinder consists of 6 rounds: 3 loaded, 3 empty. That’s 3/6 and 3/6 or 50% equal chance of surviving. However, the game begins the second round for us (as Player B). After the first round has been played out and Player A has survived, the chance of winning is dimming since an empty round has been shot; that leaves us 3/5 or 60% of catching a bullet. A right-minded person would choose to pass since there’s a 60% chance of bullet existing in this round. However, this is not the case. By having three adjacent bullets loaded gives us a huge advantage of earning that lovely 1 million dollars and going home safely by taking a shot.

Considering this, Player A has survived the first round which means an empty round was fired; furthermore, the bullets are loaded adjacently. Therefore, there’s 2/3 chance that the cylinder is currently empty. The figure below depicted the aforementioned scenario in which the cylinder rotates in clockwise. • Green stars represent the possible first round shots

We can see that 2/3 will be empty and 1/3 will be a bullet. So, if we had to be choose between taking a shot or passing to get 1 MILLION DOLLARS, we’d have a better chance with taking a shot.

## Experimental Procedures

Let’s create a simple script to simulate the game for us to prove the concept.

• Create a GameEngine to simulate the game
``````# Import libs
import random
from typing import List, Dict, Any

# Core Game Engine
class GameEngine:
""" Unlike its original rules, Unconventional Russian Roulett puts 2 bullets
in the cylinder (n=6) **adjacently**. The game proceeds as usual in which the cylinder is initially spun,
and each player takes turn to pull the trigger. The survival wins, obviously.
Assume, we were "Player B" starting second round. After the first round had been played by Player A, and he/she survived.
Now it is out turn to play; however, you were given a choice to take a shot or to stay. Here are rules
- If we took a shot, and it was empty -> WE WON 1 MILIION DOLLARS
- If we chose to stay, and it was loaded -> WE WON 1 MILIION DOLLARS

The question raised here is that
- Should we take a shot or not? and why?
- Assuming that our main goal was to get 1 million dollars at all costs
"""

def __init__(self) -> None:
""" Generate cylinders (6 rounds)
0   -> Empty (n=3)
"""

self.loaded_cylinder = [1, 1, 1] + [0 for _ in range(3)] # 3 loaded; 3 empty

def _spin_cylinder(self) -> None:
""" Simulate spinning cylinder """
random_spin_num = random.randrange(1, 7) # random 1 - 6

def _play(self, take_a_shot: bool = False) -> bool:
""" Play the game
1.) Player A will guarantee to survive the first round
2.) Player B will need to choose to whether take a shoot or stay
3.) The game will stop at the Player B's first round for simplicity
"""
# Keep spinning to make sure the first round is not loaded
self._spin_cylinder()

# Skip the first round since it is guaranteed to be empty for Player A
has_player_won_million = False
if take_a_shot and bullet == 0:
has_player_won_million = True
elif not take_a_shot and bullet == 1:
has_player_won_million = True
break
return has_player_won_million

def start_experiment(self, n: int = 1000) -> Dict[str, List[bool]]:
experimental_results = {'game_num': n, 'take_a_shot_won': [], 'pass_won': []}
if not isinstance(n, int):
return experimental_results

experimental_results['take_a_shot_won'] = sum([self._play(take_a_shot=True) for _ in range(n)])
experimental_results['pass_won'] = sum([self._play(take_a_shot=False) for _ in range(n)])
return experimental_results
``````
• Create a main function to play multiple games (n=1000) and visualize the result
``````# Import libraries
import matplotlib.pyplot as plt
import pandas as pd
import plotly.express as px

from game_engine import GameEngine

game_engine = GameEngine()

# Main Function
def main() -> None:
# Experimental Results
experimental_res = game_engine.start_experiment(n=1000)

# Prepare DataFrame
df = pd.DataFrame({}, columns=['cat', 'num_of_winning'])
df = df.append({'cat': 'take_a_shot_won', 'num_of_winning': experimental_res.get('take_a_shot_won')}, ignore_index=True)
df = df.append({'cat': 'pass_won', 'num_of_winning': experimental_res.get('pass_won')}, ignore_index=True)
print(df)

# Visualisation
fig = px.pie(df, values='num_of_winning', names='cat', hole=.3)
fig.show()

return None

if __name__ == '__main__':
main()
``````

## Results

By experimenting 1000 games with a taking-a-shot method and a passing method, the results showed that taking-a-shot won 658 games; on the other hand, passing only won 312 games. The results matched our assumption.

``````               cat num_of_winning
0  take_a_shot_won            658
1         pass_won            312
`````` ## Summary

Unconventional Russian Roulette offers something fresh and interesting to explore. We can always add more peculiar rules to make things complicated, yet insert hidden solutions to take advantages.

Finally, I do hope that this post sparks your interest in programming and data exploration. Cheers.