import random
import numpy as np
import matplotlib.pylab as plt
"Obstgarten" ("orchard")¶
is a board game (for kids).
Motivation¶
At some point we were genuinely wondering if the crow wins most of the times. Also, this has served as a greatly motivating example for statistically flavoured python classes I have been teaching.
Rules of the game¶
- there are four trees, each tree has four fruit. There are red apples, green apples, pears, and plums (I didn't find a unicode symbol for plum, so it is peaches from now on)
- there is a crow that needs to reach a gate. There are five steps necessary to reach the gate
- players aim to harvest everything from all trees and aim to not let the crow reach the gate
- There is a six sided dice that is rolled by the players. The six options of the dice are ๐, ๐, ๐, ๐, ๐งบ, ๐ฆ
- if any of the fruit ๐, ๐, ๐, ๐ is rolled, the player is allowed to harvest a fruit as long as there the corresponding fruit in the tree
- if the basked ๐งบ is rolled, the player is to pick a fruit from any fruit tree that has fruit left on it
- if the crow ๐ฆ is rolled, the corw advances one step towards the gate
- the crow wins, when it reaches the gate
- the players win when all fruit are harvested
These are the options that can be rolled with the dice
dice_options = ['๐', '๐', '๐', '๐', '๐งบ', '๐ฆ
']
n_options = len(dice_options)
print(f"there are {n_options} options on the dice:")
for item in dice_options:
print(item)
Rolling Dice in Python¶
generate n_rolls
of the dice
n_rolls = 10
np.int_(np.round(np.random.random((n_rolls)) * n_options))
n_rolls = 10
rolls = np.int_(np.round(np.random.random((n_rolls)) * n_options))
rolls
check if the rolls of the dice are uniformly distributed
# roll dice
n_rolls = 10000
randomnumbers = np.int_(np.random.random((n_rolls)) * n_options)
# plot histogram
bins = np.arange(7)
plt.hist(randomnumbers,
bins=bins,
density=True)
Start a Fresh Game¶
First: Setup a counter for the available fruit in each tree and for the number of steps left for the crow on its ladder
counter_available = {'๐':4,
'๐':4,
'๐':4,
'๐':4,
'๐ฆ
':5
}
based on experience, one game should be done after 50 rolls
n_rolls = 50
randomnumbers = np.int_(np.random.random((n_rolls)) * n_options)
randomnumbers
print(type(dice_options))
doa = np.array(dice_options)
print(doa.shape)
print(type(doa))
doa
randomnumbers
this would reflect the outcome of n_rolls
times rolling the dice in this game
doa[randomnumbers]
counter_available['๐']
this is playing the game once
for cur_i, cur_fruit in enumerate(doa[randomnumbers]):
print(cur_fruit, cur_i)
if cur_fruit == '๐งบ':
# check which basket is still full
counter_available_without_bird = counter_available.copy()
del counter_available_without_bird['๐ฆ
']
v = list(counter_available_without_bird.values())
k = list(counter_available_without_bird.keys())
m = max(v)
# randomly choose one if there are multiple maxima
storage = []
j=0
for i in v:
if i==m:
storage.append(j)
j+=1
print(k[random.choice(storage)])
counter_available[k[random.choice(storage)]] -= 1
elif counter_available['๐ฆ
'] == 0:
print('the bird has won!')
raise Exception
else:
print(counter_available)
print(cur_fruit)
counter_available[cur_fruit] -= 1
counter_available_without_bird = counter_available.copy()
del counter_available_without_bird['๐ฆ
']
v = list(counter_available_without_bird.values())
if min(v)==0:
print(counter_available_without_bird)
winner = min(counter_available_without_bird, key=counter_available_without_bird.get)
print(winner + ' won!')
break
def play_orchard(n_games, counter_available_in):
list_winners = []
list_n_throws = []
for cur_game in range(n_games):
#print(cur_game,)
counter_available = counter_available_in.copy()
n_rolls = 50
randomnumbers = np.int_(np.random.random((n_rolls)) * n_options)
doa = np.array(dice_options)
# the game
for cur_i, cur_fruit in enumerate(doa[randomnumbers]):
if cur_fruit == '๐งบ':
# check which basket is still full
counter_available_without_bird = counter_available.copy()
del counter_available_without_bird['๐ฆ
']
v = list(counter_available_without_bird.values())
k = list(counter_available_without_bird.keys())
m = max(v)
# randomly choose one if there are multiple maxima
storage = []
j=0
for i in v:
if i==m:
storage.append(j)
j+=1
counter_available[k[random.choice(storage)]] -= 1
else:
if counter_available[cur_fruit] > 0:
counter_available[cur_fruit] -= 1
if counter_available['๐ฆ
'] == 0:
# print('the bird has won!')
winner = '๐ฆ
'
list_winners.append(winner)
list_n_throws.append(cur_i)
break
else:
counter_available_without_bird = counter_available.copy()
del counter_available_without_bird['๐ฆ
']
v = list(counter_available_without_bird.values())
# Trees win if all trees have no more fruit
if all(x == 0 for x in v):
list_winners.append('trees')
list_n_throws.append(cur_i)
break
perc_win_trees = np.round(list_winners.count('trees') / n_games * 100, 0)
perc_win_crow = np.round(list_winners.count('๐ฆ
') / n_games * 100, 0)
print(f"trees win {perc_win_trees}% of all games" )
print(f"crow wins {perc_win_crow}% of all games")
#print(list_winners.count('trees') + list_winners.count('๐ฆ
'))
return list_n_throws
Analyzing the Game¶
n_games = 100000
counter_available = {'๐':4,
'๐':4,
'๐':4,
'๐':4,
'๐ฆ
':5
}
list_n_throws = play_orchard(n_games, counter_available)
plt.hist(np.array(list_n_throws),
bins=np.arange(40),
density=True)
plt.xlabel('number of dice-rolls until somebody wins')
plt.ylabel('relative frequency')
plt.grid(True)
Summary¶
In the default setup, the crow wins about in 37% of the games, the fruit harvester in 63% of the games played. Most games require about 20 times rolling the dice until somebody wins
What if there were six steps necessary for the crow to reach the gate¶
n_games = 100000
counter_available = {'๐':4,
'๐':4,
'๐':4,
'๐':4,
'๐ฆ
':6
}
list_n_throws = play_orchard(n_games, counter_available)
plt.hist(np.array(list_n_throws),
bins=np.arange(40),
density=True)
plt.xlabel('number of dice-rolls until somebody wins')
plt.ylabel('relative frequency')
plt.grid(True)
In this altered setup, the crow wins about in 23% of the games, the players / fruit harvesters in 77% of the games played. Most games require about 22 times rolling the dice until somebody wins
What if there were four steps necessary for the crow to reach the gate¶
n_games = 100000
counter_available = {'๐':4,
'๐':4,
'๐':4,
'๐':4,
'๐ฆ
':4
}
list_n_throws = play_orchard(n_games, counter_available)
plt.hist(np.array(list_n_throws),
bins=np.arange(40),
density=True)
plt.xlabel('number of dice-rolls until somebody wins')
plt.ylabel('relative frequency')
plt.grid(True)
This scenario would even out the chances of winning for the crow and the players (54% for the crow and 46% for the players. Also there would be slightly fewer dice-rolls necessary to achieve victory (on average slightly less than 20).